Class: Mpp::Challenge
- Inherits:
-
Data
- Object
- Data
- Mpp::Challenge
- Defined in:
- lib/mpp/challenge.rb
Instance Attribute Summary collapse
-
#description ⇒ Object
readonly
Returns the value of attribute description.
-
#digest ⇒ Object
readonly
Returns the value of attribute digest.
-
#expires ⇒ Object
readonly
Returns the value of attribute expires.
-
#id ⇒ Object
readonly
Returns the value of attribute id.
-
#intent ⇒ Object
readonly
Returns the value of attribute intent.
-
#method ⇒ Object
readonly
Returns the value of attribute method.
-
#opaque ⇒ Object
readonly
Returns the value of attribute opaque.
-
#realm ⇒ Object
readonly
Returns the value of attribute realm.
-
#request ⇒ Object
readonly
Returns the value of attribute request.
-
#request_b64 ⇒ Object
readonly
Returns the value of attribute request_b64.
Class Method Summary collapse
-
.create(secret_key:, realm:, method:, intent:, request:, expires: nil, digest: nil, description: nil, meta: nil) ⇒ Object
Create a Challenge with an HMAC-bound ID.
- .each_auth_scheme_index(header, offset = 0) ⇒ Object
-
.from_www_authenticate(header) ⇒ Object
Parse a Challenge from a WWW-Authenticate header value.
-
.from_www_authenticate_list(header) ⇒ Object
Parse multiple Payment challenges from a merged WWW-Authenticate header.
- .next_auth_scheme_index(header, offset) ⇒ Object
- .payment_scheme_indices(header) ⇒ Object
- .scheme_boundary?(header, index) ⇒ Boolean
Instance Method Summary collapse
-
#initialize(id:, method:, intent:, request:, realm: "", request_b64: "", digest: nil, expires: nil, description: nil, opaque: nil) ⇒ Challenge
constructor
A new instance of Challenge.
-
#to_echo ⇒ Object
Create a ChallengeEcho for use in credentials.
-
#to_www_authenticate(realm) ⇒ Object
Serialize to a WWW-Authenticate header value.
-
#verify(secret_key, realm) ⇒ Object
Verify the challenge ID matches the expected HMAC.
Constructor Details
#initialize(id:, method:, intent:, request:, realm: "", request_b64: "", digest: nil, expires: nil, description: nil, opaque: nil) ⇒ Challenge
Returns a new instance of Challenge.
21 22 23 24 |
# File 'lib/mpp/challenge.rb', line 21 def initialize(id:, method:, intent:, request:, realm: "", request_b64: "", digest: nil, expires: nil, description: nil, opaque: nil) super end |
Instance Attribute Details
#description ⇒ Object (readonly)
Returns the value of attribute description
9 10 11 |
# File 'lib/mpp/challenge.rb', line 9 def description @description end |
#digest ⇒ Object (readonly)
Returns the value of attribute digest
9 10 11 |
# File 'lib/mpp/challenge.rb', line 9 def digest @digest end |
#expires ⇒ Object (readonly)
Returns the value of attribute expires
9 10 11 |
# File 'lib/mpp/challenge.rb', line 9 def expires @expires end |
#id ⇒ Object (readonly)
Returns the value of attribute id
9 10 11 |
# File 'lib/mpp/challenge.rb', line 9 def id @id end |
#intent ⇒ Object (readonly)
Returns the value of attribute intent
9 10 11 |
# File 'lib/mpp/challenge.rb', line 9 def intent @intent end |
#method ⇒ Object (readonly)
Returns the value of attribute method
9 10 11 |
# File 'lib/mpp/challenge.rb', line 9 def method @method end |
#opaque ⇒ Object (readonly)
Returns the value of attribute opaque
9 10 11 |
# File 'lib/mpp/challenge.rb', line 9 def opaque @opaque end |
#realm ⇒ Object (readonly)
Returns the value of attribute realm
9 10 11 |
# File 'lib/mpp/challenge.rb', line 9 def realm @realm end |
#request ⇒ Object (readonly)
Returns the value of attribute request
9 10 11 |
# File 'lib/mpp/challenge.rb', line 9 def request @request end |
#request_b64 ⇒ Object (readonly)
Returns the value of attribute request_b64
9 10 11 |
# File 'lib/mpp/challenge.rb', line 9 def request_b64 @request_b64 end |
Class Method Details
.create(secret_key:, realm:, method:, intent:, request:, expires: nil, digest: nil, description: nil, meta: nil) ⇒ Object
Create a Challenge with an HMAC-bound ID.
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/mpp/challenge.rb', line 27 def self.create(secret_key:, realm:, method:, intent:, request:, expires: nil, digest: nil, description: nil, meta: nil) challenge_id = Mpp.generate_challenge_id( secret_key: secret_key, realm: realm, method: method, intent: intent, request: request, expires: expires, digest: digest, opaque: ) request_json = Mpp::Json.compact_encode(request) request_b64 = Mpp.b64url_encode(request_json) new( id: challenge_id, method: method, intent: intent, request: request, realm: realm, request_b64: request_b64, digest: digest, expires: expires, description: description, opaque: ) end |
.each_auth_scheme_index(header, offset = 0) ⇒ Object
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/mpp/challenge.rb', line 89 def self.each_auth_scheme_index(header, offset = 0) in_quote = T.let(false, T::Boolean) escaped = T.let(false, T::Boolean) i = offset while i < header.length char = T.must(header[i]) if in_quote if escaped escaped = false elsif char == "\\" escaped = true elsif char == "\"" in_quote = false end i += 1 next end if char == "\"" in_quote = true i += 1 next end if scheme_boundary?(header, i) match = T.must(header[i..]).match(/\A([A-Za-z][A-Za-z0-9._~+\/-]*)\s+/) # An auth-param permits OWS around "=" (key\s*=\s*value), so a token # followed by whitespace then "=" is a parameter, not a new scheme. if match && header[i + T.must(match[0]).length] != "=" yield i, T.must(match[1]) i += T.must(match[0]).length next end end i += 1 end end |
.from_www_authenticate(header) ⇒ Object
Parse a Challenge from a WWW-Authenticate header value.
57 58 59 |
# File 'lib/mpp/challenge.rb', line 57 def self.from_www_authenticate(header) Mpp::Parsing.parse_www_authenticate(header) end |
.from_www_authenticate_list(header) ⇒ Object
Parse multiple Payment challenges from a merged WWW-Authenticate header. Handles RFC 9110 ยง11.6.1 comma-separated authentication schemes.
63 64 65 66 67 68 69 70 71 72 |
# File 'lib/mpp/challenge.rb', line 63 def self.from_www_authenticate_list(header) payment_scheme_indices(header).map do |start_idx| # End each chunk at the next scheme boundary of any kind, so an # interleaved non-Payment scheme (e.g. "Payment ..., Bearer ..., # Payment ...") is not folded into the preceding Payment challenge. end_idx = next_auth_scheme_index(header, start_idx + "Payment".length) || header.length chunk = T.must(header[start_idx...end_idx]).sub(/,\s*$/, "") from_www_authenticate(chunk) end end |
.next_auth_scheme_index(header, offset) ⇒ Object
82 83 84 85 86 87 |
# File 'lib/mpp/challenge.rb', line 82 def self.next_auth_scheme_index(header, offset) each_auth_scheme_index(header, offset) do |index, _scheme| return index end nil end |
.payment_scheme_indices(header) ⇒ Object
74 75 76 77 78 79 80 |
# File 'lib/mpp/challenge.rb', line 74 def self.payment_scheme_indices(header) indices = [] each_auth_scheme_index(header) do |index, scheme| indices << index if scheme.casecmp("Payment").zero? end indices end |
.scheme_boundary?(header, index) ⇒ Boolean
130 131 132 133 134 135 136 137 138 |
# File 'lib/mpp/challenge.rb', line 130 def self.scheme_boundary?(header, index) previous = T.must(header[0...index]).rstrip # Start of the header, including the case where only optional whitespace # (RFC 9110 OWS) precedes the first scheme; otherwise a scheme is a # boundary only immediately after a comma separating two schemes. return true if previous.empty? previous.end_with?(",") end |
Instance Method Details
#to_echo ⇒ Object
Create a ChallengeEcho for use in credentials.
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/mpp/challenge.rb', line 161 def to_echo opaque_b64 = nil if opaque opaque_json = Mpp::Json.compact_encode(opaque) opaque_b64 = Mpp.b64url_encode(opaque_json) end ChallengeEcho.new( id: id, realm: realm, method: method, intent: intent, request: request_b64, expires: expires, digest: digest, opaque: opaque_b64 ) end |
#to_www_authenticate(realm) ⇒ Object
Serialize to a WWW-Authenticate header value.
141 142 143 |
# File 'lib/mpp/challenge.rb', line 141 def to_www_authenticate(realm) Mpp::Parsing.format_www_authenticate(self, realm) end |
#verify(secret_key, realm) ⇒ Object
Verify the challenge ID matches the expected HMAC.
146 147 148 149 150 151 152 153 154 155 156 157 158 |
# File 'lib/mpp/challenge.rb', line 146 def verify(secret_key, realm) expected_id = Mpp.generate_challenge_id( secret_key: secret_key, realm: realm, method: method, intent: intent, request: request, expires: expires, digest: digest, opaque: opaque ) Mpp.secure_compare(id, expected_id) end |