hellhound
Hellhound: a typed OpenID Connect relying party and OAuth client.
This module is the canonical owner of verified identity and sessions. A
VerifiedIdentity exists only after Hellhound verified an ID token for a
login transaction it consumed atomically, and compared the verified
issuer, audience, authorized party, subject and nonce with that
transaction. A Session exists only after custody confirmed the
installation of that identity and its tokens.
let assert Ok(client) = hellhound.new(config)
let assert Ok(Nil) = hellhound.start(client) // or hellhound.supervised(client)
// GET /login
let assert Ok(redirect) = hellhound.begin_login(client, request, hellhound.default_login())
hellhound.login_response(response.new(303), redirect) // Location, binding cookie
// GET or POST /auth/callback
case hellhound.complete_login(client, request) {
Ok(session) -> hellhound.session_reference(session) // keep in the app's session
Error(error) -> hellhound.login_error_action(error) // Reauthenticate, RejectRequest, ...
}
// Any later request
use session <- result.try(hellhound.restore_session(client, reference))
use access <- result.map(hellhound.access_token(client, session))
hellhound.authorize(api_request, access.token)
Error unions may gain variants. Branch on the Action that
login_error_action and session_error_action return, and log with the
describe_* functions.
Types
A usable access token for the session, with the session at its current revision.
pub type Access {
Access(
session: Session,
token: AccessToken,
expires_at: option.Option(timestamp.Timestamp),
scopes: List(String),
)
}
Constructors
-
Access( session: Session, token: AccessToken, expires_at: option.Option(timestamp.Timestamp), scopes: List(String), )Arguments
- expires_at
-
When the provider stated a lifetime.
An access token. string.inspect shows no token value.
pub opaque type AccessToken
What a caller should do about an error. Closed.
pub type Action {
Reauthenticate
RetryLater
Recover
FixConfiguration
RejectRequest
}
Constructors
-
ReauthenticateSend the user through login again.
-
RetryLaterNothing was decided; the same operation may be tried again later.
-
RecoverPass the carried recovery value to
recover_custodyorrecover_refresh; neither contacts the provider again. -
FixConfigurationA configuration or provider-compatibility problem an operator must fix.
-
RejectRequestThe incoming request is invalid (answer 400); do not retry it.
pub type BindingProblem {
UnknownState
BrowserBindingMissing
BrowserBindingMismatch
CallbackIssuerMismatch
CallbackIssuerMissing
}
Constructors
-
UnknownStateNo pending login has this state.
-
BrowserBindingMissingThe browser-binding cookie is absent.
-
BrowserBindingMismatchThe browser binding does not belong to this login.
-
CallbackIssuerMismatchissdiffers from the configured issuer (RFC 9207). -
CallbackIssuerMissingissis required by policy but absent.
pub type CallbackProblem {
CallbackTooLarge
CallbackEncodingInvalid
DuplicateCallbackParameter
MissingState
MissingCode
EmptyCode
AmbiguousCallback
InvalidCallbackValue
UnexpectedResponseMode
}
Constructors
-
CallbackTooLarge -
CallbackEncodingInvalid -
DuplicateCallbackParameter -
MissingState -
MissingCode -
EmptyCode -
AmbiguousCallbackBoth
codeanderrorare present. -
InvalidCallbackValue -
UnexpectedResponseModeA query callback arrived while form-post is configured, the reverse, or another HTTP method.
A Hellhound client. new validates the configuration and allocates the
names of the client’s processes once, so the value stays valid when a
supervisor restarts them.
pub type Client =
@internal Client
May gain variants.
pub type ClientCredentialsError {
ClientCredentialsNeedConfidentialClient
ClientCredentialsInvalidScope(String)
ClientCredentialsNotSent(ProviderFailure)
ClientCredentialsRejected(OAuthError)
ClientCredentialsOutcomeUnknown
}
Constructors
-
ClientCredentialsNeedConfidentialClientPublic clients and resource servers cannot use the grant.
-
ClientCredentialsInvalidScope(String) -
ClientCredentialsNotSent(ProviderFailure) -
ClientCredentialsRejected(OAuthError) -
ClientCredentialsOutcomeUnknownThe request may have reached the provider; its outcome is unknown.
A token for the client itself. Hellhound does not cache it; call
client_credentials again near expires_at.
pub type ClientToken {
ClientToken(
access_token: AccessToken,
expires_at: option.Option(timestamp.Timestamp),
scopes: List(String),
)
}
Constructors
-
ClientToken( access_token: AccessToken, expires_at: option.Option(timestamp.Timestamp), scopes: List(String), )Arguments
- expires_at
-
When the provider stated a lifetime.
Recovery for an installation whose outcome is unknown. It retains the exact installation command, including token material; recovering never exchanges the authorization code again.
pub opaque type CustodyRecovery
Provider error codes (RFC 6749 §4.1.2.1, OIDC Core §3.1.2.6). May gain variants. Descriptions are discarded.
pub type Denial {
AccessDenied
LoginRequired
ConsentRequired
InteractionRequired
AccountSelectionRequired
TemporarilyUnavailable
ServerError
InvalidRequestDenial
UnauthorizedClientDenial
UnsupportedResponseType
InvalidScopeDenial
OtherDenial
}
Constructors
-
AccessDenied -
LoginRequired -
ConsentRequired -
InteractionRequired -
AccountSelectionRequired -
TemporarilyUnavailable -
ServerError -
InvalidRequestDenial -
UnauthorizedClientDenial -
UnsupportedResponseType -
InvalidScopeDenial -
OtherDenial
Whether a failed request may have reached the provider.
pub type Evidence {
NotSent
MaybeSent
}
Constructors
-
NotSentProven not sent.
-
MaybeSentMay have been sent.
pub type IdentityKey {
IdentityKey(issuer: String, subject: String)
}
Constructors
-
IdentityKey(issuer: String, subject: String)
Why a verified-looking ID token was not accepted. May gain variants.
pub type IdentityProblem {
MissingIdToken
BadSignature
AlgorithmNotAllowed
UnsignedIdToken
EncryptedUnsignedIdToken
EncryptedIdTokenUnsupported
UnknownSigningKey
IdTokenIssuerMismatch
IdTokenAudienceMismatch
AuthorizedPartyMismatch
IdTokenExpired
IdTokenNotYetValid
NonceMismatch
AccessTokenHashMismatch
MissingClaim(String)
SubjectMismatch
AuthenticationTooOld
MalformedIdToken
UnclassifiedIdentityFailure
}
Constructors
-
MissingIdTokenThe token response had no ID token.
-
BadSignature -
AlgorithmNotAllowedThe ID token uses an algorithm outside the configured allowlist.
-
UnsignedIdTokenalg: none. -
EncryptedUnsignedIdTokenAn encrypted ID token without a nested signature.
-
EncryptedIdTokenUnsupportedEncrypted ID tokens are not supported.
-
UnknownSigningKeyThe signing key is unknown even after refreshing the provider keys.
-
IdTokenIssuerMismatch -
IdTokenAudienceMismatch -
AuthorizedPartyMismatch -
IdTokenExpired -
IdTokenNotYetValid -
NonceMismatch -
AccessTokenHashMismatch -
MissingClaim(String) -
SubjectMismatch -
AuthenticationTooOldmax_agewas requested andauth_timeis absent, in the future, or too old. -
MalformedIdToken -
UnclassifiedIdentityFailure
A provider capability Hellhound’s policy requires. May gain variants (PAR, JAR support).
pub type Incompatibility {
NoS256
AuthorizationCodeGrantUnsupported
ResponseModeUnsupported
AuthenticationMethodUnsupported
NoCommonSigningAlgorithm
NoCommonAssertionAlgorithm
RequiresPushedAuthorization
RequiresRequestObjects
InsecureAuthorizationEndpoint
InsecureEndSessionEndpoint
MissingTokenEndpoint
}
Constructors
-
NoS256The provider does not advertise PKCE
S256(withAssumeS256WhenUnadvertised: it lists other methods only). -
AuthorizationCodeGrantUnsupported -
ResponseModeUnsupportedThe configured response mode is not advertised.
-
AuthenticationMethodUnsupportedThe configured client authentication method is not advertised.
-
NoCommonSigningAlgorithmNo configured ID-token algorithm is advertised.
-
NoCommonAssertionAlgorithmNo configured client-assertion algorithm is advertised.
-
RequiresPushedAuthorizationThe provider requires pushed authorization requests.
-
RequiresRequestObjectsThe provider requires signed request objects.
-
InsecureAuthorizationEndpointThe authorization endpoint is not an absolute
httpsURI. -
InsecureEndSessionEndpointThe end-session endpoint is not an absolute
httpsURI. -
MissingTokenEndpoint
pub type Introspection {
ActiveToken(TokenInfo)
InactiveToken
}
Constructors
-
ActiveToken(TokenInfo)The provider reports the token active, and Hellhound checked
exp(strictly) andnbf(with the clock tolerance). Introspection does not itself authorise a resource request: checkaudiencesandscopes. -
InactiveToken
May gain variants.
pub type IntrospectionError {
IntrospectionNotSupported
IntrospectionTokenTooLarge
IntrospectionFailed(ProviderFailure)
}
Constructors
-
IntrospectionNotSupportedThe provider has no introspection endpoint, or this client has no credentials to call it with.
-
IntrospectionTokenTooLargeThe token is longer than
max_token_bytes; nothing was sent. -
IntrospectionFailed(ProviderFailure)
Why a login did not start, complete or recover. May gain variants;
branch on login_error_action.
pub type LoginError {
InvalidLoginOption(LoginOptionProblem)
LoginNotConfigured
LoginProviderUnavailable(ProviderFailure)
LoginProviderIncompatible(List(Incompatibility))
LoginStoreUnavailable
TooManyPendingLogins
CallbackMalformed(CallbackProblem)
CallbackRejected(BindingProblem)
LoginExpired
LoginReplayed
LoginChanged
TransactionStoreUnavailable
LoginRecordUnreadable
ProviderDenied(Denial)
ProviderUnavailableBeforeExchange(ProviderFailure)
ExchangeRejected(OAuthError)
ExchangeOutcomeUnknown
IdentityRejected(IdentityProblem)
LoginTimedOut
CustodyUnconfirmed(CustodyRecovery)
RecoveryForeign
RecoveryEnded
RecoveryExpired
}
Constructors
-
InvalidLoginOption(LoginOptionProblem)A login option is invalid.
-
LoginNotConfiguredThe client was built with
service_clientorresource_server. -
LoginProviderUnavailable(ProviderFailure)Provider metadata is unavailable.
-
LoginProviderIncompatible(List(Incompatibility))Provider metadata is no longer compatible with Hellhound’s policy.
-
LoginStoreUnavailableThe login store did not confirm the new login.
-
TooManyPendingLoginsThe login store is at capacity.
-
CallbackMalformed(CallbackProblem)The callback is not a well-formed authorization response. No login was touched.
-
CallbackRejected(BindingProblem)The callback does not match a pending login of this browser. No login was consumed.
-
LoginExpiredThe login expired before its callback was consumed.
-
LoginReplayedThe login was already consumed by another callback.
-
LoginChangedThe stored login changed between check and consumption.
-
TransactionStoreUnavailableThe login store did not answer; the login may or may not have been consumed. Nothing was sent to the provider.
-
LoginRecordUnreadableThe stored login did not open (tampering, or an unknown sealing key).
-
ProviderDenied(Denial)The provider returned an error response; the login is consumed.
-
ProviderUnavailableBeforeExchange(ProviderFailure)The token request was proven not to have been sent. The login is consumed; start a new login.
-
ExchangeRejected(OAuthError)The token endpoint rejected the exchange.
-
ExchangeOutcomeUnknownThe exchange may have reached the provider but its outcome is unknown. The code is never retried; start a new login.
-
IdentityRejected(IdentityProblem)Tokens were returned but the identity was not accepted.
-
LoginTimedOutThe login timeout ran out before the code was exchanged; nothing was sent. The login is consumed.
-
CustodyUnconfirmed(CustodyRecovery)Identity was verified and custody installation was submitted, but its outcome is unknown. Call
recover_custody; the authorization code is never exchanged again. -
RecoveryForeignThe recovery belongs to a different client configuration.
-
RecoveryEndedThe session this recovery installed has since ended; the recovery cannot bring it back.
-
RecoveryExpiredThe recovery is older than the login lifetime; it is neither confirmed nor installed again. Start a new login.
pub type LoginOptionProblem {
InvalidOptionScope(String)
ReservedParameter(String)
InvalidParameterValue(String)
OptionTooLong(String)
InvalidMaxAge
RedirectUriNotAllowed
}
Constructors
-
InvalidOptionScope(String) -
ReservedParameter(String) -
InvalidParameterValue(String) -
OptionTooLong(String)A login option value longer than 2 KiB.
-
InvalidMaxAge -
RedirectUriNotAllowedredirect_uriis neither the configured redirect URI nor one ofconfig.with_allowed_redirect_uris, compared exactly.
Per-login options. Build from default_login() with record update, so
a new option never breaks the call.
pub type LoginOptions {
LoginOptions(
scopes: List(String),
prompt: List(Prompt),
max_age: option.Option(duration.Duration),
login_hint: option.Option(String),
acr_values: List(String),
ui_locales: List(String),
extra_parameters: List(#(String, String)),
redirect_uri: option.Option(String),
)
}
Constructors
-
LoginOptions( scopes: List(String), prompt: List(Prompt), max_age: option.Option(duration.Duration), login_hint: option.Option(String), acr_values: List(String), ui_locales: List(String), extra_parameters: List(#(String, String)), redirect_uri: option.Option(String), )Arguments
- scopes
-
Scopes added to the configured scopes for this login.
- max_age
-
Maximum authentication age. When set, the ID token must carry
auth_timewithin this age. - extra_parameters
-
Extension parameters, such as RFC 8707
resource. Reserved OAuth/OIDC parameter names are rejected. - redirect_uri
-
The redirect URI for this login.
Noneuses the one configured withconfig.new;Some(uri)must equal that URI or one given toconfig.with_allowed_redirect_urisexactly, or the login fails withInvalidLoginOption(RedirectUriNotAllowed). The callback is exchanged with the same URI.
A started login: the provider URL and the browser binding to set.
string.inspect shows neither.
pub opaque type LoginRedirect
May gain variants.
pub type LogoutError {
LogoutSession(SessionError)
InvalidPostLogoutRedirect
InvalidLogoutState
LogoutStoreUnavailable
}
Constructors
-
LogoutSession(SessionError)SessionNotFound(no live session with that reference) orSessionForeign. -
InvalidPostLogoutRedirect -
InvalidLogoutState -
LogoutStoreUnavailableCustody removal was not confirmed; the session may still exist.
Per-logout options. Build from default_logout() with record update.
pub type LogoutOptions {
LogoutOptions(
post_logout_redirect_uri: option.Option(String),
state: option.Option(String),
revocation: RevocationPolicy,
)
}
Constructors
-
LogoutOptions( post_logout_redirect_uri: option.Option(String), state: option.Option(String), revocation: RevocationPolicy, )Arguments
- post_logout_redirect_uri
-
Must be registered with the provider; validated like redirect URIs.
The session is ended in custody; these report what happened at the provider.
pub type LogoutOutcome {
LoggedOut(
provider_logout: ProviderLogout,
revocation: RevocationOutcome,
)
}
Constructors
-
LoggedOut( provider_logout: ProviderLogout, revocation: RevocationOutcome, )
The browser redirect to the provider’s end-session endpoint. Its URL
carries the ID token as id_token_hint, so string.inspect shows no
URL.
pub opaque type LogoutRedirect
OAuth error codes from provider error responses (IANA registry; may gain variants). Descriptions and URIs are discarded.
pub type OAuthError {
InvalidRequest
InvalidClient
InvalidGrant
UnauthorizedClient
UnsupportedGrantType
InvalidScope
InvalidToken
InsufficientScope
OtherOAuthError
NoOAuthError
}
Constructors
-
InvalidRequest -
InvalidClient -
InvalidGrant -
UnauthorizedClient -
UnsupportedGrantType -
InvalidScope -
InvalidToken -
InsufficientScope -
OtherOAuthError -
NoOAuthError
pub type Prompt {
PromptNone
PromptLogin
PromptConsent
PromptSelectAccount
}
Constructors
-
PromptNone -
PromptLogin -
PromptConsent -
PromptSelectAccount
A provider interaction failure, without provider content. May gain variants.
pub type ProviderFailure {
ProviderNotReady
TransportFailure(evidence: Evidence, reason: TransportReason)
ProviderStatus(status: Int, error: OAuthError)
MalformedProviderResponse
IssuerMismatch
UnclassifiedBackendFailure
}
Constructors
-
ProviderNotReadyMetadata or keys are not loaded (startup in progress, or the provider is unreachable); nothing was sent.
-
TransportFailure(evidence: Evidence, reason: TransportReason)Transport failure, with evidence of whether the request may have reached the provider.
-
ProviderStatus(status: Int, error: OAuthError)The provider answered with an error status.
-
MalformedProviderResponseThe provider’s response could not be used.
-
IssuerMismatchThe provider’s metadata issuer differs from the configured issuer.
-
UnclassifiedBackendFailureA backend failure Hellhound does not classify further.
pub type ProviderLogout {
RedirectToProvider(LogoutRedirect)
NoEndSessionEndpoint
ProviderLogoutUnavailable(ProviderFailure)
}
Constructors
-
RedirectToProvider(LogoutRedirect)Redirect the browser here to end the provider session (RP-Initiated Logout).
-
NoEndSessionEndpointThe provider has no end-session endpoint.
-
ProviderLogoutUnavailable(ProviderFailure)Provider metadata is unavailable, so no redirect could be built.
pub type QuarantineReason {
ProviderOutcomeUnknown
ResponseRejected(RefreshValidationError)
RefresherLost
}
Constructors
-
ProviderOutcomeUnknownThe refresh request may have reached the provider; its outcome is unknown.
-
ResponseRejected(RefreshValidationError)The provider answered, but the response was not acceptable.
-
RefresherLostThe refreshing request’s lease ran out before it settled.
Publication-phase recovery: the exact publication command, with the new tokens. It cannot authorise another provider call.
pub opaque type RefreshRecovery
Why a refresh response was not accepted. May gain variants.
pub type RefreshValidationError {
RefreshedSubjectMismatch
RefreshedIssuerMismatch
RefreshedAudienceMismatch
RefreshedAuthorizedPartyMismatch
RefreshedNonceMismatch
RefreshedAuthenticationTimeMismatch
RefreshedScopeChangeUnsupported
RefreshedIdTokenInvalid(IdentityProblem)
RefreshResponseMalformed
}
Constructors
-
RefreshedSubjectMismatchThe refreshed ID token names a different subject.
-
RefreshedIssuerMismatch -
RefreshedAudienceMismatch -
RefreshedAuthorizedPartyMismatch -
RefreshedNonceMismatch -
RefreshedAuthenticationTimeMismatch -
RefreshedScopeChangeUnsupported -
RefreshedIdTokenInvalid(IdentityProblem) -
RefreshResponseMalformed
pub type RevocationOutcome {
Revoked
RevocationUnsupported
RevocationFailed(ProviderFailure)
RevocationSkipped
}
Constructors
-
Revoked -
RevocationUnsupportedThe provider has no revocation endpoint.
-
RevocationFailed(ProviderFailure)The revocation request failed; custody stays removed.
-
RevocationSkipped
Whether logout revokes the session’s tokens at the provider (RFC 7009).
pub type RevocationPolicy {
RevokeRefreshToken
SkipRevocation
}
Constructors
-
RevokeRefreshTokenRevoke the refresh token, or the access token when the session has no refresh token. The default.
-
SkipRevocation
A session whose installation custody confirmed. It carries the verified identity and a custody reference and revision; tokens stay in custody.
pub opaque type Session
Why a session operation failed. May gain variants; branch on
session_error_action.
pub type SessionError {
SessionNotFound
SessionLost
SessionForeign
SessionStoreUnavailable
SessionRecordUnreadable
SessionHasNoAccessToken
RefreshTokenUnavailable
RefreshRevoked
RefreshRejected(OAuthError)
RefreshNotSent(ProviderFailure)
RefreshQuarantined(QuarantineReason)
RefreshWaitTimedOut
RefreshUnconfirmed(RefreshRecovery)
RefreshRecoveryForeign
}
Constructors
-
SessionNotFoundNo live session has this reference: unknown, logged out, or past its absolute or idle lifetime.
-
SessionLostThe session was held by the in-memory custody, which restarted since.
-
SessionForeignThe session belongs to another client configuration.
-
SessionStoreUnavailableThe custody store did not answer.
-
SessionRecordUnreadableThe stored session did not open (tampering, or an unknown sealing key).
-
SessionHasNoAccessTokenThe session has no access token.
-
RefreshTokenUnavailableThe access token expired and the session has no refresh token.
-
RefreshRevokedThe refresh token was rejected (
invalid_grant): revoked or expired. -
RefreshRejected(OAuthError)The token endpoint refused the refresh before processing the grant (RFC 6749 §5.2); the refresh token may be sent again later.
-
RefreshNotSent(ProviderFailure)The refresh request was proven not sent; it may be tried again.
-
RefreshQuarantined(QuarantineReason)The provider may have rotated the refresh token, so it is never sent again. Reauthenticate.
-
RefreshWaitTimedOutAnother request’s refresh of this session did not settle within the refresh wait (default 5 s).
-
RefreshUnconfirmed(RefreshRecovery)New tokens were received but custody did not confirm publishing them. Call
recover_refresh; the provider is not called again. -
RefreshRecoveryForeignThe refresh recovery belongs to another client configuration.
Why a client did not start. May gain variants.
pub type StartError {
InvalidConfig(List(config.ConfigError))
DiscoveryFailed(ProviderFailure)
ProviderIncompatible(List(Incompatibility))
StartupTimedOut
AlreadyStarted
ProcessStartFailed
}
Constructors
-
InvalidConfig(List(config.ConfigError))The configuration is invalid (
config.describe_config_error). -
DiscoveryFailed(ProviderFailure)Discovery through Hellhound’s transport failed.
-
ProviderIncompatible(List(Incompatibility))The provider’s metadata is incompatible with Hellhound’s policy.
-
StartupTimedOutDiscovery or previous-process cleanup exceeded the startup timeout.
-
AlreadyStartedThe client’s processes are already running.
-
ProcessStartFailedA Hellhound process failed to start.
The full introspection response, held in a closure.
pub opaque type TokenClaims
An active token’s introspection response (RFC 7662). Read fields by
label; decode other claims with decode_token_claims.
pub type TokenInfo {
TokenInfo(
client_id: option.Option(String),
subject: option.Option(String),
username: option.Option(String),
scopes: List(String),
audiences: List(String),
expires_at: option.Option(timestamp.Timestamp),
issued_at: option.Option(timestamp.Timestamp),
not_before: option.Option(timestamp.Timestamp),
token_type: option.Option(String),
issuer: option.Option(String),
claims: TokenClaims,
)
}
Constructors
-
TokenInfo( client_id: option.Option(String), subject: option.Option(String), username: option.Option(String), scopes: List(String), audiences: List(String), expires_at: option.Option(timestamp.Timestamp), issued_at: option.Option(timestamp.Timestamp), not_before: option.Option(timestamp.Timestamp), token_type: option.Option(String), issuer: option.Option(String), claims: TokenClaims, )Arguments
- audiences
-
aud, whether the provider sent a string or a list.
Closed transport failure reasons. May gain variants as HTTP Gun does.
pub type TransportReason {
DestinationRejected
InsecureScheme
InvalidDestination
ResolutionFailed
ConnectionRefused
ConnectionFailed
TlsRejected
Timeout
ResponseTooLarge
ResponseHeadersTooLarge
MalformedHttp
ReceiveFailed
UnsupportedContentEncoding
OtherTransportFailure
}
Constructors
-
DestinationRejected -
InsecureScheme -
InvalidDestination -
ResolutionFailed -
ConnectionRefused -
ConnectionFailed -
TlsRejected -
Timeout -
ResponseTooLarge -
ResponseHeadersTooLarge -
MalformedHttp -
ReceiveFailedThe response failed or ended early after the request may have been sent.
-
UnsupportedContentEncoding -
OtherTransportFailure
Userinfo claims whose sub equals the session’s subject.
pub opaque type UserInfo
May gain variants.
pub type UserinfoError {
UserinfoSession(SessionError)
UserinfoNotSupported
UserinfoSubjectMismatch
UserinfoFailed(ProviderFailure)
}
Constructors
-
UserinfoSession(SessionError) -
UserinfoNotSupported -
UserinfoSubjectMismatchThe response
subdiffers from the session’s subject. -
UserinfoFailed(ProviderFailure)
A verified identity. Constructed only by this module after verification
and Hellhound’s binding checks. The stable key is (issuer, subject);
email is an optional claim, never a key.
pub opaque type VerifiedIdentity
Values
pub fn access_token(
client: Client,
session: Session,
) -> Result(Access, SessionError)
A usable access token for the session. When the current token expires
within the refresh margin (default 30 s) it refreshes first; when
another request is already refreshing this session it waits for that
refresh (default at most 5 s) instead of sending a second one; when the
session value is older than custody’s, it uses the current revision. If
a refresh decides nothing (RetryLater) while the current token has not
expired, the current token is returned.
pub fn access_token_type(token: AccessToken) -> String
pub fn access_token_value(token: AccessToken) -> String
The token value, for a client that does not take a gleam_http request. Treat it as a secret: do not log or persist it.
pub fn acr(identity: VerifiedIdentity) -> option.Option(String)
pub fn amr(identity: VerifiedIdentity) -> List(String)
pub fn authentication_time(
identity: VerifiedIdentity,
) -> option.Option(timestamp.Timestamp)
auth_time, when the provider included it.
pub fn authorize(
request: request.Request(a),
token: AccessToken,
) -> request.Request(a)
Set Authorization: Bearer <token> on a resource request.
pub fn begin_login(
client: Client,
request: request.Request(a),
options: LoginOptions,
) -> Result(LoginRedirect, LoginError)
Begin a login for the browser that sent request: generate state, nonce
and PKCE verifier from the OS CSPRNG, store the pending login and return
the redirect. A browser that already holds a binding cookie keeps it, so
concurrent logins in several tabs stay bound to one cookie.
pub fn client_credentials(
client: Client,
scopes: List(String),
) -> Result(ClientToken, ClientCredentialsError)
Obtain an access token for the client itself (RFC 6749 §4.4).
pub fn complete_login(
client: Client,
request: request.Request(String),
) -> Result(Session, LoginError)
Complete a login from the callback request (a GET with the query, or
a form POST, per the configured response mode) and the browser’s
binding cookie.
Order: parse strictly; find the pending login by state; compare state, browser binding and issuer without consuming; consume atomically; only a consumption confirmed by the store proceeds to exchange the code; verify the identity; install custody. The whole operation is bounded by the login timeout (default 30 s).
pub fn decode_claims(
identity: VerifiedIdentity,
decoder: decode.Decoder(a),
) -> Result(a, List(decode.DecodeError))
Decode verified ID-token claims into a caller-owned type.
pub fn decode_token_claims(
info: TokenInfo,
decoder: decode.Decoder(a),
) -> Result(a, List(decode.DecodeError))
Decode the introspection response into a caller-owned type.
pub fn decode_userinfo(
info: UserInfo,
decoder: decode.Decoder(a),
) -> Result(a, List(decode.DecodeError))
pub fn default_login() -> LoginOptions
pub fn default_logout() -> LogoutOptions
pub fn describe_client_credentials_error(
error: ClientCredentialsError,
) -> String
pub fn describe_introspection_error(
error: IntrospectionError,
) -> String
pub fn describe_login_error(error: LoginError) -> String
pub fn describe_logout_error(error: LogoutError) -> String
pub fn describe_provider_failure(
failure: ProviderFailure,
) -> String
pub fn describe_session_error(error: SessionError) -> String
pub fn describe_start_error(error: StartError) -> String
pub fn describe_userinfo_error(error: UserinfoError) -> String
pub fn email(identity: VerifiedIdentity) -> option.Option(String)
pub fn email_verified(
identity: VerifiedIdentity,
) -> option.Option(Bool)
pub fn identity_key(identity: VerifiedIdentity) -> IdentityKey
pub fn introspect(
client: Client,
token: String,
) -> Result(Introspection, IntrospectionError)
Ask the provider whether token is active (RFC 7662).
pub fn issuer(identity: VerifiedIdentity) -> String
pub fn login_error_action(error: LoginError) -> Action
What to do about a login error.
pub fn login_response(
response: response.Response(b),
redirect: LoginRedirect,
) -> response.Response(b)
Turn response into the login redirect: 303 See Other to the provider,
the browser-binding cookie (__Host-, Secure, HttpOnly, Path=/,
SameSite=Lax, or None for form-post callbacks, outliving the pending
login by five minutes) and Cache-Control: no-store.
pub fn login_url(redirect: LoginRedirect) -> String
The provider authorization URL to send the browser to.
pub fn logout(
client: Client,
session: Session,
options: LogoutOptions,
) -> Result(LogoutOutcome, LogoutError)
End the session: remove its custody first (by reference, whatever its
revision), then revoke its token at the provider (RFC 7009, unless
skipped), then build the provider logout redirect with the logout-only
ID-token hint. A failed revocation never restores the session.
LogoutSession(SessionNotFound) reports that no live session had that
reference, which callers may treat as signed out.
pub fn logout_response(
response: response.Response(b),
redirect: LogoutRedirect,
) -> response.Response(b)
Turn response into the redirect: 303 See Other with Location and
Cache-Control: no-store.
pub fn logout_url(redirect: LogoutRedirect) -> String
pub const max_token_bytes: Int
The largest token introspect sends; a larger one is refused without a
request.
pub fn name(identity: VerifiedIdentity) -> option.Option(String)
pub fn new(config: config.Config) -> Result(Client, StartError)
Validate a configuration and prepare a client. Starts nothing.
pub fn preferred_username(
identity: VerifiedIdentity,
) -> option.Option(String)
pub fn recover_custody(
client: Client,
recovery: CustodyRecovery,
) -> Result(Session, LoginError)
Resubmit an installation whose outcome was unknown. Custody returns the original session if it already accepted the command.
pub fn recover_refresh(
client: Client,
recovery: RefreshRecovery,
) -> Result(Access, SessionError)
Resubmit an unconfirmed publication. The provider is not called; custody returns the published session if it already accepted it.
pub fn refresh(
client: Client,
session: Session,
) -> Result(Access, SessionError)
Refresh the session now, for example after a resource server answered
401. If the session was already refreshed since this value was read, the
current token is returned without another refresh. Concurrent callers
share one refresh as in access_token.
pub fn restore_session(
client: Client,
reference: String,
) -> Result(Session, SessionError)
Load a session from its custody reference (the application’s session cookie).
pub fn session_error_action(error: SessionError) -> Action
What to do about a session error. An uncertain refresh never maps to a retry of the same request.
pub fn session_identity(session: Session) -> VerifiedIdentity
pub fn session_reference(session: Session) -> String
The custody reference, for the application’s own session cookie or
store. It is a random bearer value: keep it server-side or in an
encrypted, HttpOnly cookie. Custody stores only its digest.
pub fn start(client: Client) -> Result(Nil, StartError)
Start the client’s processes under a supervisor linked to the caller, and wait for its first discovery and metadata compatibility check. Waits at most the startup timeout (default 15 s).
pub fn stop(client: Client) -> Nil
Request shutdown of the client’s processes and wait up to five seconds
for supervisor exit. Returning does not prove shutdown if that wait expires.
Cache exit triggers cancellation of its background fetch independently
of the provider timeout or a blocked telemetry handler; this call does
not synchronously join the worker.
A client started with supervised belongs to its parent supervisor:
stop it there, or the parent restarts it.
pub fn subject(identity: VerifiedIdentity) -> String
pub fn supervised(
client: Client,
) -> supervision.ChildSpecification(Client)
A child specification for an application supervision tree. The child
starts at once and discovers the provider in the background, retrying
with backoff from 1 s to 60 s; until discovery succeeds and the metadata
is compatible, operations fail with ProviderNotReady. A restart keeps
the same names, so client stays valid. Before reusing those names,
startup joins previous child processes within the configured startup
timeout. Background provider discovery then retries independently.
pub fn userinfo(
client: Client,
session: Session,
) -> Result(UserInfo, UserinfoError)
The session’s userinfo claims, with the access token access_token
would return (refreshed when near expiry).
pub fn userinfo_subject(info: UserInfo) -> String
pub fn with_correlation(
client: Client,
correlation: correlation.Correlation,
) -> Client
A view of the client whose events and provider requests carry
correlation (hellhound/telemetry, HTTP Gun’s events). Pure: the
underlying processes are shared.