Class: MCPClient::Auth::Token
- Inherits:
-
Object
- Object
- MCPClient::Auth::Token
- Defined in:
- lib/mcp_client/auth.rb
Overview
OAuth token model representing access/refresh tokens
Constant Summary collapse
- UNREADABLE_EXPIRY =
An expiry this client cannot read is recorded as this instant. An unreadable expiry is not "no expiry": read that way, a mangled lifetime would make a token that never expires and is never refreshed. Read as an instant long past, the record is refreshed or re-authorized instead — and says so again after a round trip through storage.
Time.at(0).utc.freeze
- RETIRED_ISSUER =
Issuer value marking a token that must never be presented again (retired after an authorization server change, on a storage backend that cannot delete it).
'urn:mcp:retired-token'
Instance Attribute Summary collapse
-
#access_token ⇒ Object
readonly
Returns the value of attribute access_token.
-
#expires_at ⇒ Object
readonly
Returns the value of attribute expires_at.
-
#expires_in ⇒ Object
readonly
Returns the value of attribute expires_in.
-
#issuer ⇒ Object
readonly
Returns the value of attribute issuer.
-
#refresh_token ⇒ Object
readonly
Returns the value of attribute refresh_token.
-
#scope ⇒ Object
readonly
Returns the value of attribute scope.
-
#token_type ⇒ Object
readonly
Returns the value of attribute token_type.
Class Method Summary collapse
-
.from_h(data) ⇒ Token
Create token from hash.
-
.usable_expiry(value) ⇒ Time?
An expiry instant, as persisted by #to_h (an ISO 8601 string) or as given.
-
.usable_lifetime(value) ⇒ Integer, ...
A lifetime that can be added to a Time.
Instance Method Summary collapse
-
#expired? ⇒ Boolean
Check if the token is expired.
-
#expires_soon? ⇒ Boolean
Check if the token is close to expiring (within 5 minutes).
-
#initialize(access_token:, token_type: 'Bearer', expires_in: nil, scope: nil, refresh_token: nil, issuer: nil, expires_at: nil) ⇒ Token
constructor
A new instance of Token.
-
#retired? ⇒ Boolean
Whether the token was retired.
-
#to_h ⇒ Hash
Convert to hash for serialization.
-
#to_header ⇒ String
Convert token to authorization header value.
-
#with_issuer(issuer) ⇒ Token
A copy of this token bound to an authorization server (or retired).
Constructor Details
#initialize(access_token:, token_type: 'Bearer', expires_in: nil, scope: nil, refresh_token: nil, issuer: nil, expires_at: nil) ⇒ Token
Returns a new instance of Token.
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
# File 'lib/mcp_client/auth.rb', line 26 def initialize(access_token:, token_type: 'Bearer', expires_in: nil, scope: nil, refresh_token: nil, issuer: nil, expires_at: nil) @access_token = access_token @token_type = token_type # A record read back from a storage backend that persists plain # hashes carries whatever was written (or edited) there. The lifetime # is validated BEFORE it is added to a Time: `Time.now + "3600"` is a # TypeError out of `OAuthProvider#access_token` — a crash in the # request path, over a record the read-path checks were about to # refuse anyway. @expires_in = self.class.usable_lifetime(expires_in) @scope = scope @issuer = issuer @refresh_token = refresh_token @expires_at = resolve_expiry(expires_in, expires_at) end |
Instance Attribute Details
#access_token ⇒ Object (readonly)
Returns the value of attribute access_token.
15 16 17 |
# File 'lib/mcp_client/auth.rb', line 15 def access_token @access_token end |
#expires_at ⇒ Object (readonly)
Returns the value of attribute expires_at.
15 16 17 |
# File 'lib/mcp_client/auth.rb', line 15 def expires_at @expires_at end |
#expires_in ⇒ Object (readonly)
Returns the value of attribute expires_in.
15 16 17 |
# File 'lib/mcp_client/auth.rb', line 15 def expires_in @expires_in end |
#issuer ⇒ Object (readonly)
Returns the value of attribute issuer.
15 16 17 |
# File 'lib/mcp_client/auth.rb', line 15 def issuer @issuer end |
#refresh_token ⇒ Object (readonly)
Returns the value of attribute refresh_token.
15 16 17 |
# File 'lib/mcp_client/auth.rb', line 15 def refresh_token @refresh_token end |
#scope ⇒ Object (readonly)
Returns the value of attribute scope.
15 16 17 |
# File 'lib/mcp_client/auth.rb', line 15 def scope @scope end |
#token_type ⇒ Object (readonly)
Returns the value of attribute token_type.
15 16 17 |
# File 'lib/mcp_client/auth.rb', line 15 def token_type @token_type end |
Class Method Details
.from_h(data) ⇒ Token
Create token from hash
155 156 157 158 159 160 161 162 163 164 165 |
# File 'lib/mcp_client/auth.rb', line 155 def self.from_h(data) new( access_token: data[:access_token] || data['access_token'], token_type: data[:token_type] || data['token_type'] || 'Bearer', expires_in: data[:expires_in] || data['expires_in'], scope: data[:scope] || data['scope'], refresh_token: data[:refresh_token] || data['refresh_token'], issuer: data[:issuer] || data['issuer'], expires_at: data[:expires_at] || data['expires_at'] ) end |
.usable_expiry(value) ⇒ Time?
An expiry instant, as persisted by #to_h (an ISO 8601 string) or as given. Unparseable text, or a value of another type, is no expiry.
63 64 65 66 67 68 69 70 71 72 |
# File 'lib/mcp_client/auth.rb', line 63 def self.usable_expiry(value) return value if value.is_a?(Time) return nil unless value.is_a?(String) begin Time.parse(value) rescue ArgumentError nil end end |
.usable_lifetime(value) ⇒ Integer, ...
A lifetime that can be added to a Time. RFC 6749 Section 5.1 makes expires_in a number of seconds; anything else says nothing.
55 56 57 |
# File 'lib/mcp_client/auth.rb', line 55 def self.usable_lifetime(value) value if value.is_a?(Integer) || value.is_a?(Float) end |
Instance Method Details
#expired? ⇒ Boolean
Check if the token is expired
76 77 78 79 80 |
# File 'lib/mcp_client/auth.rb', line 76 def expired? return false unless @expires_at Time.now >= @expires_at end |
#expires_soon? ⇒ Boolean
Check if the token is close to expiring (within 5 minutes)
84 85 86 87 88 |
# File 'lib/mcp_client/auth.rb', line 84 def expires_soon? return false unless @expires_at Time.now >= (@expires_at - 300) # 5 minutes buffer end |
#retired? ⇒ Boolean
Returns whether the token was retired.
103 104 105 |
# File 'lib/mcp_client/auth.rb', line 103 def retired? @issuer == RETIRED_ISSUER end |
#to_h ⇒ Hash
Convert to hash for serialization
115 116 117 118 119 120 121 122 123 124 |
# File 'lib/mcp_client/auth.rb', line 115 def to_h { access_token: @access_token, token_type: @token_type, expires_in: @expires_in, scope: @scope, refresh_token: @refresh_token, expires_at: @expires_at&.iso8601 }.tap { |hash| hash[:issuer] = @issuer if @issuer } end |
#to_header ⇒ String
Convert token to authorization header value
109 110 111 |
# File 'lib/mcp_client/auth.rb', line 109 def to_header "#{@token_type.capitalize} #{@access_token}" end |
#with_issuer(issuer) ⇒ Token
A copy of this token bound to an authorization server (or retired).
98 99 100 |
# File 'lib/mcp_client/auth.rb', line 98 def with_issuer(issuer) self.class.from_h(to_h.merge(issuer: issuer)) end |