Class: Himari::AccessToken
- Inherits:
-
Object
- Object
- Himari::AccessToken
- Includes:
- TokenString
- Defined in:
- lib/himari/access_token.rb
Defined Under Namespace
Classes: Bearer
Instance Attribute Summary collapse
-
#claims ⇒ Object
readonly
Returns the value of attribute claims.
-
#client_id ⇒ Object
readonly
Returns the value of attribute client_id.
-
#expiry ⇒ Object
readonly
Returns the value of attribute expiry.
-
#handle ⇒ Object
readonly
Returns the value of attribute handle.
-
#scopes ⇒ Object
readonly
Returns the value of attribute scopes.
-
#session_handle ⇒ Object
readonly
Returns the value of attribute session_handle.
Attributes included from TokenString
Class Method Summary collapse
- .default_lifetime ⇒ Object
- .from_authz(authz) ⇒ Object
- .magic_header ⇒ Object
-
.parse(str, signing_key_provider: nil) ⇒ Object
Parse a presented access token into its opaque Format (handle + secret) for verification against storage.
- .parse_jwt(str, signing_key_provider) ⇒ Object
Instance Method Summary collapse
- #as_json ⇒ Object
- #as_log ⇒ Object
-
#initialize(handle:, client_id:, claims:, expiry:, scopes: [], session_handle: nil, secret: nil, secret_hash: nil) ⇒ AccessToken
constructor
A new instance of AccessToken.
- #to_bearer(token_string: format.to_s) ⇒ Object
-
#to_jwt(signing_key:, issuer:, now: Time.now) ⇒ Object
Render this token as an RFC 9068 JWT (Himari::AccessTokenJwt).
- #userinfo ⇒ Object
Methods included from TokenString
#format, hash_secret, included, #magic_header, #secret, #secret_hash, #secret_hash_prev, #verify!, #verify_expiry!, #verify_secret!
Constructor Details
#initialize(handle:, client_id:, claims:, expiry:, scopes: [], session_handle: nil, secret: nil, secret_hash: nil) ⇒ AccessToken
Returns a new instance of AccessToken.
76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/himari/access_token.rb', line 76 def initialize(handle:, client_id:, claims:, expiry:, scopes: [], session_handle: nil, secret: nil, secret_hash: nil) @handle = handle @client_id = client_id @claims = claims @scopes = scopes @session_handle = session_handle @expiry = expiry @secret = secret @secret_hash = secret_hash @secret_hash_prev = nil @verification = nil end |
Instance Attribute Details
#claims ⇒ Object (readonly)
Returns the value of attribute claims.
90 91 92 |
# File 'lib/himari/access_token.rb', line 90 def claims @claims end |
#client_id ⇒ Object (readonly)
Returns the value of attribute client_id.
90 91 92 |
# File 'lib/himari/access_token.rb', line 90 def client_id @client_id end |
#expiry ⇒ Object (readonly)
Returns the value of attribute expiry.
90 91 92 |
# File 'lib/himari/access_token.rb', line 90 def expiry @expiry end |
#handle ⇒ Object (readonly)
Returns the value of attribute handle.
90 91 92 |
# File 'lib/himari/access_token.rb', line 90 def handle @handle end |
#scopes ⇒ Object (readonly)
Returns the value of attribute scopes.
90 91 92 |
# File 'lib/himari/access_token.rb', line 90 def scopes @scopes end |
#session_handle ⇒ Object (readonly)
Returns the value of attribute session_handle.
90 91 92 |
# File 'lib/himari/access_token.rb', line 90 def session_handle @session_handle end |
Class Method Details
.default_lifetime ⇒ Object
26 27 28 |
# File 'lib/himari/access_token.rb', line 26 def self.default_lifetime 3600 end |
.from_authz(authz) ⇒ Object
66 67 68 69 70 71 72 73 74 |
# File 'lib/himari/access_token.rb', line 66 def self.from_authz(authz) make( client_id: authz.client_id, claims: authz.claims, scopes: authz.scopes, session_handle: authz.session_handle, lifetime: authz.lifetime.access_token, ) end |
.magic_header ⇒ Object
22 23 24 |
# File 'lib/himari/access_token.rb', line 22 def self.magic_header 'hmat' end |
.parse(str, signing_key_provider: nil) ⇒ Object
Parse a presented access token into its opaque Format (handle + secret) for verification against storage. Two on-the-wire shapes are accepted:
-
the opaque token “hmat.<handle>.<secret>” (TokenString format), or
-
an RFC 9068 JWT (Himari::AccessTokenJwt) carrying the opaque token in its
hmatclaim.
For a JWT, the signature is verified first (requires signing_key_provider to resolve the kid), then the embedded opaque token is returned so the caller validates the secret against storage exactly as for an opaque token. Any malformed/unverifiable JWT becomes TokenString::InvalidFormat so callers handle one failure type.
42 43 44 45 46 |
# File 'lib/himari/access_token.rb', line 42 def self.parse(str, signing_key_provider: nil) return TokenString::Format.parse(magic_header, str) if str.to_s.start_with?("#{magic_header}.") parse_jwt(str, signing_key_provider) end |
.parse_jwt(str, signing_key_provider) ⇒ Object
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/himari/access_token.rb', line 48 def self.parse_jwt(str, signing_key_provider) raise TokenString::InvalidFormat, 'signing keys are required to verify a JWT access token' unless signing_key_provider jwt = JSON::JWT.decode(str, :skip_verification) key = jwt.kid && signing_key_provider.find(id: jwt.kid) raise TokenString::InvalidFormat, 'unknown or missing signing key (kid)' unless key jwt.verify!(key.pkey) hmat = jwt[magic_header] raise TokenString::InvalidFormat, 'missing hmat claim' unless hmat.is_a?(String) && hmat.start_with?("#{magic_header}.") TokenString::Format.parse(magic_header, hmat) rescue JSON::JWT::Exception, JSON::ParserError => e raise TokenString::InvalidFormat, "invalid JWT access token: #{e.class}" end |
Instance Method Details
#as_json ⇒ Object
135 136 137 138 139 140 141 142 143 144 145 |
# File 'lib/himari/access_token.rb', line 135 def as_json { handle: handle, secret_hash: secret_hash, client_id: client_id, claims: claims, scopes: scopes, session_handle: session_handle, expiry: expiry.to_i, } end |
#as_log ⇒ Object
124 125 126 127 128 129 130 131 132 133 |
# File 'lib/himari/access_token.rb', line 124 def as_log { handle: handle, client_id: client_id, claims: claims, scopes: scopes, session_handle: session_handle, expiry: expiry, } end |
#to_bearer(token_string: format.to_s) ⇒ Object
100 101 102 103 104 105 |
# File 'lib/himari/access_token.rb', line 100 def to_bearer(token_string: format.to_s) Bearer.new( access_token: token_string, expires_in: (expiry - Time.now.to_i).to_i, ) end |
#to_jwt(signing_key:, issuer:, now: Time.now) ⇒ Object
Render this token as an RFC 9068 JWT (Himari::AccessTokenJwt). The opaque secret travels in the JWT’s hmat claim, so the token validates against storage the same way either form does. exp is tied to this token’s own expiry rather than recomputed, keeping both forms in sync.
112 113 114 115 116 117 118 119 120 121 122 |
# File 'lib/himari/access_token.rb', line 112 def to_jwt(signing_key:, issuer:, now: Time.now) AccessTokenJwt.new( access: self, claims: claims, client_id: client_id, signing_key: signing_key, issuer: issuer, time: now, lifetime: expiry - now.to_i, ).to_jwt end |
#userinfo ⇒ Object
92 93 94 95 96 |
# File 'lib/himari/access_token.rb', line 92 def userinfo claims.merge( aud: client_id, ) end |