Class: AgentAdmit::TokensClient
- Inherits:
-
Object
- Object
- AgentAdmit::TokensClient
- Defined in:
- lib/agentadmit/tokens_client.rb
Overview
TokensClient — issue, exchange, and revoke connection tokens via the AgentAdmit hosted service.
Constant Summary collapse
- UNSET =
Sentinel for issue_token's duration_seconds: leave the field out of the request entirely, so AgentAdmit applies its default (30 days). Pass nil instead for an until-revoked connection (explicit JSON null).
Object.new.freeze
- PURPOSE_MAX_LENGTH =
Maximum length of a declared purpose (matches the hosted API contract).
300- USER_INTENT_MAX_LENGTH =
Maximum length of a user-declared intent (matches the hosted API contract: optional string, 1..300 characters).
300
Instance Method Summary collapse
-
#exchange(connection_token, agent_label: nil, agent_id: nil) ⇒ Hash
Exchange a single-use connection token for an access token.
-
#initialize(config = nil) ⇒ TokensClient
constructor
A new instance of TokensClient.
-
#issue_token(user_id:, scopes:, role: nil, duration_seconds: UNSET, purpose: nil, user_intent: nil, presence: nil) ⇒ Hash
Issue a connection token for one of your users.
-
#revoke(connection_id, reason: nil) ⇒ Hash
Revoke a connection (and its access tokens).
Constructor Details
#initialize(config = nil) ⇒ TokensClient
Returns a new instance of TokensClient.
25 26 27 28 |
# File 'lib/agentadmit/tokens_client.rb', line 25 def initialize(config = nil) @config = config || AgentAdmit.configuration || Config.new @config.validate_api_key! end |
Instance Method Details
#exchange(connection_token, agent_label: nil, agent_id: nil) ⇒ Hash
Exchange a single-use connection token for an access token. Calls POST /api/v1/exchange — unauthenticated by design: the connection token itself is the credential, so the operator API key is NOT sent.
117 118 119 120 121 122 123 |
# File 'lib/agentadmit/tokens_client.rb', line 117 def exchange(connection_token, agent_label: nil, agent_id: nil) body = { "token" => connection_token } body["agent_label"] = agent_label if agent_label body["agent_id"] = agent_id if agent_id post("/api/v1/exchange", body, authenticated: false, op: "exchange") end |
#issue_token(user_id:, scopes:, role: nil, duration_seconds: UNSET, purpose: nil, user_intent: nil, presence: nil) ⇒ Hash
Issue a connection token for one of your users. Calls POST /api/v1/apps/app_id/token.
The duration is tri-state:
- omit the argument — field omitted; AgentAdmit applies its default (30 days)
- nil — explicit JSON null; the connection lasts until revoked
- Integer — explicit duration in seconds (60–31536000)
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/agentadmit/tokens_client.rb', line 70 def issue_token(user_id:, scopes:, role: nil, duration_seconds: UNSET, purpose: nil, user_intent: nil, presence: nil) if purpose && purpose.length > PURPOSE_MAX_LENGTH raise ArgumentError, "purpose must be at most #{PURPOSE_MAX_LENGTH} characters" end # User-declared intent is validated like purpose: reject out-of-contract # values before any request rather than silently discarding the user's # typed words (data loss). Empty/whitespace-only normalizes to nil-omit. unless user_intent.nil? || user_intent.is_a?(String) raise ArgumentError, "user_intent must be a String or nil" end if user_intent && user_intent.length > USER_INTENT_MAX_LENGTH raise ArgumentError, "user_intent must be at most #{USER_INTENT_MAX_LENGTH} characters" end user_intent = nil if user_intent && user_intent.strip.empty? # Presence is typed-only: a raw Hash is rejected so the wire contract # (literal-true verified/uv, offset-carrying verified_at) stays owned # by AppAttestedPresence, never hand-rolled at call sites. unless presence.nil? || presence.is_a?(AppAttestedPresence) raise ArgumentError, "presence must be an AgentAdmit::AppAttestedPresence or nil" end body = { "user_id" => user_id, "scopes" => scopes } body["role"] = role if role body["purpose"] = purpose if purpose body["user_intent"] = user_intent if user_intent body["presence"] = presence.to_wire if presence # Tri-state: the UNSET sentinel omits the key entirely; nil survives # JSON.generate as explicit JSON null (no compact, no nil-guard). body["duration_seconds"] = duration_seconds unless duration_seconds.equal?(UNSET) post("/api/v1/apps/#{@config.app_id}/token", body, authenticated: true, op: "issue_token") end |
#revoke(connection_id, reason: nil) ⇒ Hash
Revoke a connection (and its access tokens). Calls POST /api/v1/revoke.
134 135 136 137 138 139 |
# File 'lib/agentadmit/tokens_client.rb', line 134 def revoke(connection_id, reason: nil) body = { "connection_id" => connection_id } body["reason"] = reason if reason post("/api/v1/revoke", body, authenticated: true, op: "revoke") end |