Lockspire. Protocol. TokenExchange. Internal. AccessTokenSigner
(lockspire v1.6.1)
Copy Markdown
Shared access-token issuance for all Lockspire grant paths.
Owns the single RFC 9068 at+jwt signing site, the opaque-token delegate, and
the one-place format decision (per-client override -> server default -> :jwt).
Format resolution
The effective access-token format is resolved in exactly one place
(resolve_format/2):
- a per-client
access_token_formatof:jwtor:opaquewins; - otherwise (
nil) the server-wideServerPolicy.access_token_formatsupplied through the explicit dependency bundle is used; - otherwise it falls back to
:jwt.
Audience derivation
The four grant paths (authorization-code, refresh, device-code, CIBA) emit a
LIST aud: the requested resource(s) when present, otherwise [client_id].
The RFC 8693 token-exchange path keeps a BARE-STRING aud == client_id via
issue_exchange/4, preserving the historical exchange wire shape.
Security
The signing alg/kid are taken ONLY from the active signing key — never from
client-controlled input — and none is never emitted. On a missing or invalid
key the error path logs inspect(reason) only: no key material reaches logs.
Summary
Functions
Issue an access token for a standard grant path.
Issue a signed at+jwt access token for the RFC 8693 token-exchange path.
Types
@type token_for_issuance() :: %Lockspire.Domain.Token{ account_id: term(), audience: term(), client_id: term(), cnf: term(), code_challenge: term(), code_challenge_method: term(), consent_grant_id: term(), expires_at: term(), family_id: term(), generation: term(), id: term(), inserted_at: term(), interaction_id: term(), issued_at: term(), jti: term(), parent_token_id: term(), redeemed_at: term(), redirect_uri: term(), reuse_detected_at: term(), revoked_at: term(), scopes: term(), sid: term(), token_hash: String.t() | nil, token_type: term(), updated_at: term() }
Functions
@spec issue(token_for_issuance(), Lockspire.Domain.Client.t(), map()) :: result()
@spec issue(token_for_issuance(), Lockspire.Domain.Client.t(), map(), map()) :: result()
Issue an access token for a standard grant path.
Resolves the effective format and returns {:ok, raw, hash} where
hash == Lockspire.Security.Policy.hash_token(raw), or a 500
:token_signing_failed error when the :jwt branch cannot sign.
The :jwt branch emits a LIST aud derived from token.audience.
@spec issue_exchange(token_for_issuance(), Lockspire.Domain.Client.t(), map(), map()) :: result()
@spec issue_exchange( token_for_issuance(), Lockspire.Domain.Client.t(), map(), map(), map() ) :: result()
Issue a signed at+jwt access token for the RFC 8693 token-exchange path.
Always :jwt. Emits a BARE-STRING aud == client.client_id (the exchange
carve-out) and merges custom_claims over the base claims after dropping the
restricted claims iss sub aud exp iat jti client_id.