simple_oauth 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 86b6ff6f62fce7e9cc8119852cbf8470c5a1b7f9ab4ecaaf929d2aba35121a2b
4
- data.tar.gz: c40d43c6172cf63cca3d04f81291b8e9917062cea0c0b112943cb0a2428c488c
3
+ metadata.gz: c80dd2b6480173dc8278a8b927f6b25929bddb430a72e152abbd29c8cae4c4da
4
+ data.tar.gz: ac11a3c3e75fb0d1eb7964d25e5f53b002b334e27273e9e6428b5a6b9226481f
5
5
  SHA512:
6
- metadata.gz: fafef85869f766c1d1c19ebceb4e8152741734ce438e3379ff0fd94d15b2a4437d7b19e74fe62506f0ff4214eaf37c64cfffaa979400df0fc49b765f8bc1c325
7
- data.tar.gz: b95a19574fdd189c53f1e3d263515533991046f8e29e501f12c0a37c39158351f1376c851d58f751b7efda99b6d4a676be2ce243cc5540e52ab4c7d2ef18870e
6
+ metadata.gz: 7749f45d33c7f55a4186b9a744ea4119fe9d0ce1211ee26c89789887cad66f0a478dfddf4146e585044078adbaae82f548620794b71961a9da832cd8888ee5d3
7
+ data.tar.gz: bc81ed8ad865e68843a764e75fc04b06afa4cd441fb4b24a22406c8512ec844dfd01a975dde11ea88bf96f1d4679a96b23fbe63463db5735803193781d72c39c
data/.rubocop.yml CHANGED
@@ -7,8 +7,14 @@ plugins:
7
7
  - rubocop-performance
8
8
  - rubocop-rake
9
9
 
10
+ inherit_mode:
11
+ merge:
12
+ - Include
13
+
10
14
  AllCops:
11
15
  NewCops: enable
16
+ Include:
17
+ - .duel.rb
12
18
  TargetRubyVersion: 3.2
13
19
 
14
20
  Layout/ArgumentAlignment:
data/CHANGELOG.md CHANGED
@@ -1,96 +1,128 @@
1
- ## [1.0.0] - 2026-09-12
1
+ # Changelog
2
2
 
3
- ### Changed
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/2.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.0.2] - 2026-11-14
9
+
10
+ ### Security
4
11
 
5
- * Every file declares `frozen_string_literal: true`, and the explicit `freeze` calls on string literals it makes redundant are gone
6
- * Drop the `base64` runtime dependency: `pack("m0")` and `unpack1("m")` encode and decode Base64 in core Ruby, so the gem now has no runtime dependencies at all
7
- * Drop the `cgi` runtime dependency: query strings and form bodies are read with `URI.decode_www_form`, which Ruby ships in every supported version
8
- * **Breaking**: raise `ParseError` when an Authorization header, form body, or query string repeats an OAuth protocol parameter, which RFC 5849 Section 3.2 does not allow; `Header.parse` previously kept the last value and `Header.parse_form_body` the first, so the two disagreed about which one a request carried
9
- * **Breaking**: rename `Signature.methods` to `Signature.registered_methods`, so that `Signature.methods` is the module's own method list again
10
- * **Breaking**: define `VERSION` in `SimpleOAuth`, the module the rest of the library uses, rather than in a second `SimpleOauth` module; `SimpleOauth::VERSION` is gone and `SimpleOAuth::VERSION` now resolves
11
- * **Breaking**: `Signature.rsa?` raises `ArgumentError` for a signature method that is not registered, as `Signature.digest`, `sign`, and `verify` already do, rather than answering false
12
- * `SimpleOAuth::Error` is the base of every error the library raises, so `rescue SimpleOAuth::Error` catches `ParseError`, `InvalidOptionsError`, and `OAuth2::Error` alike
12
+ - `Header#valid?` no longer verifies with whatever signature method the received header claims. A server verifying RSA signatures with a client's public key, as the README showed, accepted forged requests signed with that public key as an HMAC-SHA1 or PLAINTEXT secret. Reported by Rathnadeepan (HotWater, [@HotWater-015](https://github.com/HotWater-015))
13
+ - Given a `signature_method`, the header must claim that method
14
+ - Without one, the header must claim an RSA method for an RSA key and an HMAC method for any other secret
15
+ - PLAINTEXT signatures verify only when the caller names `PLAINTEXT`
16
+
17
+ ### Deprecated
18
+
19
+ - Calling `Header#valid?` without a `signature_method`, which a later major version will require
20
+
21
+ ## [1.0.1] - 2026-09-30
13
22
 
14
23
  ### Fixed
15
24
 
16
- * Treat only `&` as a parameter separator when reading a query string or form body; a `;` is part of the value, as every current server reads it, so `?a=1;b=2` no longer signs two parameters where the server sees one
25
+ - Ship RBS signatures that leave core and the standard library alone: the gem no longer redeclares `MatchData#[]`, `OpenSSL.secure_compare`, `OpenSSL::PKey::PKey#sign` and `#verify`, or `StringScanner`, so code that loads its signatures through `rbs collection` no longer fails `rbs validate` with a duplicated `MatchData#[]` definition
26
+ - Add `sig/manifest.yaml`, naming the standard libraries the signatures refer to (`json`, `openssl`, `securerandom`, `strscan`, and `uri`), so `rbs collection` loads them for code that depends on the gem
27
+
28
+ ## [1.0.0] - 2026-09-12
17
29
 
18
30
  ### Added
19
31
 
20
- * OAuth 2.0 request builders and response parsers in `SimpleOAuth::OAuth2`, which make no HTTP requests:
21
- * `Client#authorization_url` for the authorization code flow, which names `pkce` so that a client either sends a challenge or says `pkce: nil`, as OAuth 2.1 asks every client for one; a `state` is optional alongside a challenge, and required without one
22
- * `Client#authorization_code_request`, `#refresh_token_request`, and `#client_credentials_request` for the token endpoint
23
- * `Client#revocation_request` for the revocation endpoint (RFC 7009)
24
- * `client_secret_basic` and `client_secret_post` authentication for confidential clients, and public clients without a secret
25
- * `PKCE` verifiers with `S256` and `plain` challenges (RFC 7636)
26
- * `AuthorizationResponse.parse`, which reads the response an authorization server returns to the redirect URI: it raises the error the server reported, a `state` that is not the one the request sent, an `iss` that is not the expected issuer (RFC 9207), a repeated parameter, or a response with no code
27
- * A `params` option on every request builder, for what an extension adds to a request, such as the resource indicator of RFC 8707
28
- * `Token.from_response` and `Error.from_response` for token and error responses, rejecting a response whose access token is missing, null, or empty, or whose lifetime is not a number of seconds
32
+ - OAuth 2.0 request builders and response parsers in `SimpleOAuth::OAuth2`, which make no HTTP requests:
33
+ - `Client#authorization_url` for the authorization code flow, which names `pkce` so that a client either sends a challenge or says `pkce: nil`, as OAuth 2.1 asks every client for one; a `state` is optional alongside a challenge, and required without one
34
+ - `Client#authorization_code_request`, `#refresh_token_request`, and `#client_credentials_request` for the token endpoint
35
+ - `Client#revocation_request` for the revocation endpoint (RFC 7009)
36
+ - `client_secret_basic` and `client_secret_post` authentication for confidential clients, and public clients without a secret
37
+ - `PKCE` verifiers with `S256` and `plain` challenges (RFC 7636)
38
+ - `AuthorizationResponse.parse`, which reads the response an authorization server returns to the redirect URI: it raises the error the server reported, a `state` that is not the one the request sent, an `iss` that is not the expected issuer (RFC 9207), a repeated parameter, or a response with no code
39
+ - A `params` option on every request builder, for what an extension adds to a request, such as the resource indicator of RFC 8707
40
+ - `Token.from_response` and `Error.from_response` for token and error responses, rejecting a response whose access token is missing, null, or empty, or whose lifetime is not a number of seconds
41
+
42
+ ### Changed
43
+
44
+ - Every file declares `frozen_string_literal: true`, and the explicit `freeze` calls on string literals it makes redundant are gone
45
+ - Drop the `base64` runtime dependency: `pack("m0")` and `unpack1("m")` encode and decode Base64 in core Ruby, so the gem now has no runtime dependencies at all
46
+ - Drop the `cgi` runtime dependency: query strings and form bodies are read with `URI.decode_www_form`, which Ruby ships in every supported version
47
+ - **Breaking:** Raise `ParseError` when an Authorization header, form body, or query string repeats an OAuth protocol parameter, which RFC 5849 Section 3.2 does not allow; `Header.parse` previously kept the last value and `Header.parse_form_body` the first, so the two disagreed about which one a request carried
48
+ - **Breaking:** Rename `Signature.methods` to `Signature.registered_methods`, so that `Signature.methods` is the module's own method list again
49
+ - **Breaking:** Define `VERSION` in `SimpleOAuth`, the module the rest of the library uses, rather than in a second `SimpleOauth` module; `SimpleOauth::VERSION` is gone and `SimpleOAuth::VERSION` now resolves
50
+ - **Breaking:** `Signature.rsa?` raises `ArgumentError` for a signature method that is not registered, as `Signature.digest`, `sign`, and `verify` already do, rather than answering false
51
+ - `SimpleOAuth::Error` is the base of every error the library raises, so `rescue SimpleOAuth::Error` catches `ParseError`, `InvalidOptionsError`, and `OAuth2::Error` alike
52
+
53
+ ### Fixed
54
+
55
+ - Treat only `&` as a parameter separator when reading a query string or form body; a `;` is part of the value, as every current server reads it, so `?a=1;b=2` no longer signs two parameters where the server sees one
29
56
 
30
57
  ## [0.5.1] - 2026-09-12
31
58
 
32
59
  ### Fixed
33
60
 
34
- * Build the signature base string with a String conversion that every supported Ruby offers; `Header#url` called `URI::Generic#to_str`, which arrived in `uri` 0.13, so it raised `NoMethodError` on a stock Ruby 3.2, the oldest version the gem claims to support
35
- * Sign a parameter that carries no value, such as a bare `?flag` in the query string or the `c2` of the RFC 5849 Section 3.4.1.3.1 example, as `name=` rather than dropping it from the signature base string; a request carrying one signed differently than the server computes, so it was rejected
61
+ - Build the signature base string with a String conversion that every supported Ruby offers; `Header#url` called `URI::Generic#to_str`, which arrived in `uri` 0.13, so it raised `NoMethodError` on a stock Ruby 3.2, the oldest version the gem claims to support
62
+ - Sign a parameter that carries no value, such as a bare `?flag` in the query string or the `c2` of the RFC 5849 Section 3.4.1.3.1 example, as `name=` rather than dropping it from the signature base string; a request carrying one signed differently than the server computes, so it was rejected
36
63
 
37
64
  ## [0.5.0] - 2026-09-12
38
65
 
39
66
  ### Added
40
67
 
41
- * `Header.from_request`, which builds a header for a request object such as a `Net::HTTPRequest`, signing its query parameters, its form-encoded body, or hashing any other body
42
- * `Header.parse_query`, for OAuth credentials sent in a query string
43
- * `Signature.digest`, and a `digest:` option on `Signature.register`, which gives the hash algorithm a signature method signs with
44
- * `Signature.verify` and a `verify:` option on `Signature.register`, for signature methods that cannot be verified by recomputing the signature
45
- * `Signature.decode_base64`
68
+ - `Header.from_request`, which builds a header for a request object such as a `Net::HTTPRequest`, signing its query parameters, its form-encoded body, or hashing any other body
69
+ - `Header.parse_query`, for OAuth credentials sent in a query string
70
+ - `Signature.digest`, and a `digest:` option on `Signature.register`, which gives the hash algorithm a signature method signs with
71
+ - `Signature.verify` and a `verify:` option on `Signature.register`, for signature methods that cannot be verified by recomputing the signature
72
+ - `Signature.decode_base64`
46
73
 
47
74
  ### Fixed
48
75
 
49
- * Check `oauth_body_hash` against the body a header was built with when verifying, so a body changed after signing no longer verifies against the hash its signature covers
50
- * Verify signatures without merging the given secrets into the header's own options, where anything else reading the header could see them
51
- * Compare signatures in constant time when verifying
52
- * Compute `oauth_body_hash` with the hash algorithm of the signature method, such as SHA-256 for HMAC-SHA256; it was always SHA-1
53
- * Sign a parameter whose value is an Array as one parameter per value, as a repeated parameter; the Array was previously signed as its Ruby representation
54
- * Verify RSA signatures with the signer's public key, which is all a verifier has; `Header#valid?` previously recomputed the signature and so needed the private key
55
- * Match the form-encoded media type exactly when signing a body, rather than by prefix, so a media type such as `application/x-www-form-urlencoded-json` is hashed instead of signed as parameters, and `Application/X-WWW-Form-Urlencoded` is recognized
76
+ - Check `oauth_body_hash` against the body a header was built with when verifying, so a body changed after signing no longer verifies against the hash its signature covers
77
+ - Verify signatures without merging the given secrets into the header's own options, where anything else reading the header could see them
78
+ - Compare signatures in constant time when verifying
79
+ - Compute `oauth_body_hash` with the hash algorithm of the signature method, such as SHA-256 for HMAC-SHA256; it was always SHA-1
80
+ - Sign a parameter whose value is an Array as one parameter per value, as a repeated parameter; the Array was previously signed as its Ruby representation
81
+ - Verify RSA signatures with the signer's public key, which is all a verifier has; `Header#valid?` previously recomputed the signature and so needed the private key
82
+ - Match the form-encoded media type exactly when signing a body, rather than by prefix, so a media type such as `application/x-www-form-urlencoded-json` is hashed instead of signed as parameters, and `Application/X-WWW-Form-Urlencoded` is recognized
56
83
 
57
84
  ## [0.4.2] - 2026-09-12
58
85
 
59
86
  ### Added
60
87
 
61
- * Document passing `Header` parameters as an Array of key-value pairs when a key repeats
88
+ - Document passing `Header` parameters as an Array of key-value pairs when a key repeats
62
89
 
63
90
  ### Fixed
64
91
 
65
- * Accept an Array of key-value pairs as `Header` parameters in the RBS signatures, which already worked at runtime
92
+ - Accept an Array of key-value pairs as `Header` parameters in the RBS signatures, which already worked at runtime
66
93
 
67
94
  ## [0.4.1] - 2026-04-20
68
95
 
69
96
  ### Fixed
70
97
 
71
- * Remove `URI::RFC2396_PARSER` stub from RBS signatures to avoid duplicate declaration error with RBS 4.0.2, which now ships the constant in its stdlib `uri` signatures
98
+ - Remove `URI::RFC2396_PARSER` stub from RBS signatures to avoid duplicate declaration error with RBS 4.0.2, which now ships the constant in its stdlib `uri` signatures
72
99
 
73
100
  ## [0.4.0] - 2026-02-01
74
101
 
75
102
  ### Added
76
103
 
77
- * Extensible signature method registry allowing custom signature methods to be registered at runtime
78
- * Support for RSA-SHA256 and HMAC-SHA256 signature methods
79
- * OAuth Request Body Hash support (`oauth_body_hash` parameter) for signing requests with non-form-encoded bodies
80
- * Support for parsing OAuth credentials from POST body via `Header.parse_form_body`
81
- * Support for `realm` parameter in OAuth Authorization header
82
-
83
- ### Fixed
84
-
85
- * Avoid symbolizing untrusted input in parse methods for security
86
- * Refactored `Header.parse` for improved robustness using StringScanner
104
+ - Extensible signature method registry allowing custom signature methods to be registered at runtime
105
+ - Support for RSA-SHA256 and HMAC-SHA256 signature methods
106
+ - OAuth Request Body Hash support (`oauth_body_hash` parameter) for signing requests with non-form-encoded bodies
107
+ - Support for parsing OAuth credentials from POST body via `Header.parse_form_body`
108
+ - Support for `realm` parameter in OAuth Authorization header
87
109
 
88
110
  ### Changed
89
111
 
90
- * **Breaking**: `Signature.rsa?` raises `ArgumentError` for a signature method that is not registered, as `Signature.digest`, `sign`, and `verify` already do, rather than answering false
91
- * `SimpleOAuth::Error` is the base of every error the library raises, so `rescue SimpleOAuth::Error` catches `ParseError`, `InvalidOptionsError`, and `OAuth2::Error` alike
112
+ - **Breaking:** Require Ruby 3.2 or later; supports Ruby 3.2, 3.3, 3.4, and 4.0
113
+ - Added `base64` and `cgi` as explicit runtime dependencies
114
+ - Migrated test suite from RSpec to Minitest
92
115
 
93
- * Supports Ruby 3.2, 3.3, 3.4, and 4.0
94
- * Added `base64` and `cgi` as explicit runtime dependencies
95
- * Migrated test suite from RSpec to Minitest
116
+ ### Fixed
96
117
 
118
+ - Avoid symbolizing untrusted input in parse methods for security
119
+ - Refactored `Header.parse` for improved robustness using StringScanner
120
+
121
+ [1.0.2]: https://github.com/laserlemon/simple_oauth/compare/v1.0.1...v1.0.2
122
+ [1.0.1]: https://github.com/laserlemon/simple_oauth/compare/v1.0.0...v1.0.1
123
+ [1.0.0]: https://github.com/laserlemon/simple_oauth/compare/v0.5.1...v1.0.0
124
+ [0.5.1]: https://github.com/laserlemon/simple_oauth/compare/v0.5.0...v0.5.1
125
+ [0.5.0]: https://github.com/laserlemon/simple_oauth/compare/v0.4.2...v0.5.0
126
+ [0.4.2]: https://github.com/laserlemon/simple_oauth/compare/v0.4.1...v0.4.2
127
+ [0.4.1]: https://github.com/laserlemon/simple_oauth/compare/v0.4.0...v0.4.1
128
+ [0.4.0]: https://github.com/laserlemon/simple_oauth/compare/v0.3.1...v0.4.0
data/README.md CHANGED
@@ -6,6 +6,7 @@
6
6
  [![Lint](https://github.com/laserlemon/simple_oauth/actions/workflows/lint.yml/badge.svg)](https://github.com/laserlemon/simple_oauth/actions/workflows/lint.yml)
7
7
  [![Typecheck](https://github.com/laserlemon/simple_oauth/actions/workflows/typecheck.yml/badge.svg)](https://github.com/laserlemon/simple_oauth/actions/workflows/typecheck.yml)
8
8
  [![Yardstick](https://github.com/laserlemon/simple_oauth/actions/workflows/yardstick.yml/badge.svg)](https://github.com/laserlemon/simple_oauth/actions/workflows/yardstick.yml)
9
+ [![Duel](https://raw.githubusercontent.com/laserlemon/simple_oauth/duel/badge/badge.svg)](https://github.com/sferik/duel-ruby)
9
10
 
10
11
  Simply builds and verifies OAuth 1.0 headers per [RFC 5849](https://tools.ietf.org/html/rfc5849), and builds OAuth 2.0 requests per [RFC 6749](https://www.rfc-editor.org/rfc/rfc6749), [RFC 7636](https://www.rfc-editor.org/rfc/rfc7636), and [RFC 7009](https://www.rfc-editor.org/rfc/rfc7009).
11
12
 
@@ -158,16 +159,18 @@ parsed = SimpleOAuth::Header.parse_query("oauth_consumer_key=key&status=hello")
158
159
  header = SimpleOAuth::Header.new(:get, request_url, params, authorization_header)
159
160
 
160
161
  # Verify the signature
161
- header.valid?(consumer_secret: "secret", token_secret: "token_secret")
162
+ header.valid?(consumer_secret: "secret", token_secret: "token_secret", signature_method: "HMAC-SHA1")
162
163
  # => true
163
164
  ```
164
165
 
166
+ Name the signature method you expect, and a header claiming any other method is not valid. The method a request claims is chosen by whoever sent it, so verifying with it unchecked would let anyone holding an RSA public key pass it off as an HMAC or PLAINTEXT secret. Leaving `signature_method` out is deprecated: the header must then claim an RSA method for an RSA key and an HMAC method for any other secret. PLAINTEXT signatures are accepted only when you name `PLAINTEXT`, and they send the secrets themselves, so accept them only over TLS.
167
+
165
168
  Verifying compares signatures in constant time and leaves the header's own options untouched, so the secrets stay with the caller.
166
169
 
167
170
  RSA signatures verify with the client's public key, which is all a server has:
168
171
 
169
172
  ```ruby
170
- header.valid?(consumer_secret: File.read("client_public_key.pem"))
173
+ header.valid?(consumer_secret: File.read("client_public_key.pem"), signature_method: "RSA-SHA1")
171
174
  ```
172
175
 
173
176
  Custom signature methods that cannot be verified by recomputing the signature register a `verify` block:
data/Rakefile CHANGED
@@ -41,3 +41,10 @@ task :steep do
41
41
  end
42
42
 
43
43
  task default: %i[test rubocop standard mutant yardstick steep]
44
+
45
+ begin
46
+ require "duel/rake_task"
47
+ Duel::RakeTask.new
48
+ rescue LoadError
49
+ # Duel requires Ruby 3.4 or later
50
+ end
data/Steepfile CHANGED
@@ -11,6 +11,7 @@ target :lib do
11
11
  library "uri"
12
12
  library "securerandom"
13
13
  library "json"
14
+ library "strscan"
14
15
 
15
16
  configure_code_diagnostics(D::Ruby.strict) do |hash|
16
17
  # Allow FallbackAny warnings for variables in ensure blocks
@@ -24,6 +24,9 @@ module SimpleOAuth
24
24
  # Default signature method per RFC 5849
25
25
  DEFAULT_SIGNATURE_METHOD = "HMAC-SHA1"
26
26
 
27
+ # The signature method that sends the secrets themselves, which verifying accepts only when named
28
+ PLAINTEXT_SIGNATURE_METHOD = "PLAINTEXT"
29
+
27
30
  # OAuth version
28
31
  OAUTH_VERSION = "1.0"
29
32
 
@@ -123,18 +126,30 @@ module SimpleOAuth
123
126
 
124
127
  # Validates the signature in the header against the provided secrets
125
128
  #
129
+ # The method a received header claims is chosen by whoever sent it, so it is checked before it
130
+ # is used. Given a signature_method, the header must claim that method. Without one, the header
131
+ # must claim a method that fits the key: an RSA method for an RSA key, and an HMAC method for any
132
+ # other secret. PLAINTEXT is accepted only when named, as it sends the secrets themselves.
133
+ # Without that check, a sender holding only an RSA public key could sign with it as an HMAC or
134
+ # PLAINTEXT secret.
135
+ #
126
136
  # @api public
127
- # @param secrets [Hash] the consumer_secret and token_secret for validation
137
+ # @param secrets [Hash] the consumer_secret and token_secret for validation, and the
138
+ # signature_method the header must use, which a later major version will require
128
139
  # @return [Boolean] true if the signature is valid, false otherwise
140
+ # @raise [ArgumentError] if the expected signature method is not registered
129
141
  # @note When the header was built with a body, the signed oauth_body_hash must match that body,
130
142
  # so a tampered body fails verification even though its signature covers the claimed hash
131
143
  # @example
132
144
  # parsed_header = SimpleOAuth::Header.new(:get, url, {}, authorization_header)
133
- # parsed_header.valid?(consumer_secret: "secret", token_secret: "token_secret")
145
+ # parsed_header.valid?(consumer_secret: "secret", token_secret: "token_secret",
146
+ # signature_method: "HMAC-SHA1")
134
147
  # # => true
135
148
  def valid?(secrets = {})
136
- body_hash_valid? && Signature.verify(options.fetch(:signature_method), signing_key(options.merge(secrets)),
137
- signature_base, options.fetch(:signature))
149
+ verifying = options.merge(secrets)
150
+ signature_method_acceptable?(verifying, secrets[:signature_method]) && body_hash_valid? &&
151
+ Signature.verify(options.fetch(:signature_method), signing_key(verifying), signature_base,
152
+ options.fetch(:signature))
138
153
  end
139
154
 
140
155
  # Returns the OAuth attributes including the signature
@@ -216,6 +231,50 @@ module SimpleOAuth
216
231
  Signature.rsa?(options.fetch(:signature_method)) ? options[:consumer_secret] : secret(options)
217
232
  end
218
233
 
234
+ # Checks that the header claims a signature method the verifier accepts
235
+ #
236
+ # @api private
237
+ # @param verifying [Hash] the header's options merged with the verifier's secrets
238
+ # @param expected [String, nil] the signature method the verifier expects, if it names one
239
+ # @return [Boolean] true if the claimed method is the expected one, or fits the key
240
+ # @raise [ArgumentError] if the expected signature method is not registered
241
+ def signature_method_acceptable?(verifying, expected)
242
+ claimed = options[:signature_method].to_s
243
+ return expected_signature_method?(claimed, expected) if expected
244
+
245
+ warn("SimpleOAuth::Header#valid? without a signature_method is deprecated. " \
246
+ "Name the method to verify with, such as signature_method: \"HMAC-SHA1\"", category: :deprecated)
247
+ Signature.registered?(claimed) && !PLAINTEXT_SIGNATURE_METHOD.casecmp?(claimed) &&
248
+ Signature.rsa?(claimed).eql?(rsa_key?(verifying[:consumer_secret]))
249
+ end
250
+
251
+ # Checks that the header claims the signature method the verifier names
252
+ #
253
+ # Signature method names are compared without regard to case, as the registry looks them up.
254
+ #
255
+ # @api private
256
+ # @param claimed [String] the signature method the header claims
257
+ # @param expected [String] the signature method the verifier expects
258
+ # @return [Boolean] true if the header claims the expected method
259
+ # @raise [ArgumentError] if the expected signature method is not registered
260
+ def expected_signature_method?(claimed, expected)
261
+ raise ArgumentError, "Unknown signature method: #{expected}" unless Signature.registered?(expected)
262
+
263
+ claimed.casecmp?(expected)
264
+ end
265
+
266
+ # Checks whether a verifying key is an RSA key
267
+ #
268
+ # @api private
269
+ # @param key [String, nil] the consumer_secret the verifier gave
270
+ # @return [Boolean] true if the key parses as an RSA key
271
+ def rsa_key?(key)
272
+ OpenSSL::PKey::RSA.new(key.to_s)
273
+ true
274
+ rescue OpenSSL::PKey::PKeyError
275
+ false
276
+ end
277
+
219
278
  # Checks the body against the oauth_body_hash the header carries
220
279
  #
221
280
  # A header parsed from a request claims a body hash that its signature covers, so the claim must be
@@ -3,5 +3,5 @@
3
3
  # OAuth 1.0 header generation library
4
4
  module SimpleOAuth
5
5
  # The current version of the SimpleOAuth gem
6
- VERSION = "1.0.0"
6
+ VERSION = "1.0.2"
7
7
  end
data/sig/manifest.yaml ADDED
@@ -0,0 +1,7 @@
1
+ # The standard libraries the signatures of simple_oauth refer to, which rbs collection loads for code that depends on it
2
+ dependencies:
3
+ - name: json
4
+ - name: openssl
5
+ - name: securerandom
6
+ - name: strscan
7
+ - name: uri
data/sig/simple_oauth.rbs CHANGED
@@ -62,6 +62,9 @@ module SimpleOAuth
62
62
  # Valid keys when parsing OAuth parameters (ATTRIBUTE_KEYS + signature)
63
63
  PARSE_KEYS: Array[Symbol]
64
64
 
65
+ # The signature method that sends the secrets themselves, which verifying accepts only when named
66
+ PLAINTEXT_SIGNATURE_METHOD: String
67
+
65
68
  # What from_request needs from a request object, such as a Net::HTTPRequest
66
69
  interface _Request
67
70
  def method: () -> String
@@ -148,6 +151,15 @@ module SimpleOAuth
148
151
  # The key for signing and verifying
149
152
  def signing_key: (oauth_options options) -> String?
150
153
 
154
+ # Checks that the header claims a signature method the verifier accepts
155
+ def signature_method_acceptable?: (oauth_options verifying, String? expected) -> bool
156
+
157
+ # Checks that the header claims the signature method the verifier names
158
+ def expected_signature_method?: (String claimed, String expected) -> bool
159
+
160
+ # Checks whether a verifying key is an RSA key
161
+ def rsa_key?: (String? key) -> bool
162
+
151
163
  # Checks the body against the oauth_body_hash the header carries
152
164
  def body_hash_valid?: () -> bool
153
165
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple_oauth
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steve Richert
@@ -43,16 +43,13 @@ files:
43
43
  - lib/simple_oauth/parser.rb
44
44
  - lib/simple_oauth/signature.rb
45
45
  - lib/simple_oauth/version.rb
46
- - mutant.yml
47
- - sig/matchdata_ext.rbs
48
- - sig/openssl_ext.rbs
46
+ - sig/manifest.yaml
49
47
  - sig/simple_oauth.rbs
50
48
  - sig/simple_oauth/header/class_methods.rbs
51
49
  - sig/simple_oauth/header/params.rbs
52
50
  - sig/simple_oauth/oauth2.rbs
53
51
  - sig/simple_oauth/parser.rbs
54
52
  - sig/simple_oauth/signature.rbs
55
- - sig/strscan.rbs
56
53
  homepage: https://github.com/laserlemon/simple_oauth
57
54
  licenses:
58
55
  - MIT
@@ -76,7 +73,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
76
73
  - !ruby/object:Gem::Version
77
74
  version: '0'
78
75
  requirements: []
79
- rubygems_version: 4.0.20
76
+ rubygems_version: 4.0.24
80
77
  specification_version: 4
81
78
  summary: Simply builds and verifies OAuth 1.0 headers and builds OAuth 2.0 requests
82
79
  test_files: []
data/mutant.yml DELETED
@@ -1,17 +0,0 @@
1
- usage: opensource
2
- integration:
3
- name: minitest
4
- includes:
5
- - lib
6
- - test
7
- requires:
8
- - simple_oauth
9
- mutation:
10
- operators: full
11
- timeout: 5.0
12
- matcher:
13
- subjects:
14
- - SimpleOAuth*
15
- coverage_criteria:
16
- process_abort: true
17
- test_result: true
@@ -1,6 +0,0 @@
1
- # Extension to MatchData for indexed access with Integer
2
- class MatchData
3
- # Override [] to return non-nil String for Integer index
4
- # (when we know the group exists)
5
- def []: (Integer index) -> String
6
- end
data/sig/openssl_ext.rbs DELETED
@@ -1,15 +0,0 @@
1
- # Extensions to OpenSSL types
2
- module OpenSSL
3
- # Compare two strings in constant time
4
- def self.secure_compare: (String a, String b) -> bool
5
-
6
- module PKey
7
- class PKey
8
- # Sign with digest name as String (in addition to Digest object)
9
- def sign: (String | OpenSSL::Digest digest, String data) -> String
10
-
11
- # Verify with digest name as String (in addition to Digest object)
12
- def verify: (String | OpenSSL::Digest digest, String signature, String data) -> bool
13
- end
14
- end
15
- end
data/sig/strscan.rbs DELETED
@@ -1,9 +0,0 @@
1
- # Minimal StringScanner type declarations for simple_oauth
2
- class StringScanner
3
- def initialize: (String string) -> void
4
- def scan: (Regexp pattern) -> String?
5
- def eos?: () -> bool
6
- def rest: () -> String
7
- def pos: () -> Integer
8
- def []: (Integer index) -> String?
9
- end