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

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

  • Reauthenticate

    Send the user through login again.

  • RetryLater

    Nothing was decided; the same operation may be tried again later.

  • Recover

    Pass the carried recovery value to recover_custody or recover_refresh; neither contacts the provider again.

  • FixConfiguration

    A configuration or provider-compatibility problem an operator must fix.

  • RejectRequest

    The incoming request is invalid (answer 400); do not retry it.

pub type BindingProblem {
  UnknownState
  BrowserBindingMissing
  BrowserBindingMismatch
  CallbackIssuerMismatch
  CallbackIssuerMissing
}

Constructors

  • UnknownState

    No pending login has this state.

  • BrowserBindingMissing

    The browser-binding cookie is absent.

  • BrowserBindingMismatch

    The browser binding does not belong to this login.

  • CallbackIssuerMismatch

    iss differs from the configured issuer (RFC 9207).

  • CallbackIssuerMissing

    iss is required by policy but absent.

pub type CallbackProblem {
  CallbackTooLarge
  CallbackEncodingInvalid
  DuplicateCallbackParameter
  MissingState
  MissingCode
  EmptyCode
  AmbiguousCallback
  InvalidCallbackValue
  UnexpectedResponseMode
}

Constructors

  • CallbackTooLarge
  • CallbackEncodingInvalid
  • DuplicateCallbackParameter
  • MissingState
  • MissingCode
  • EmptyCode
  • AmbiguousCallback

    Both code and error are present.

  • InvalidCallbackValue
  • UnexpectedResponseMode

    A 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

  • ClientCredentialsNeedConfidentialClient

    Public clients and resource servers cannot use the grant.

  • ClientCredentialsInvalidScope(String)
  • ClientCredentialsNotSent(ProviderFailure)
  • ClientCredentialsRejected(OAuthError)
  • ClientCredentialsOutcomeUnknown

    The 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

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

  • NotSent

    Proven not sent.

  • MaybeSent

    May 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

  • MissingIdToken

    The token response had no ID token.

  • BadSignature
  • AlgorithmNotAllowed

    The ID token uses an algorithm outside the configured allowlist.

  • UnsignedIdToken

    alg: none.

  • EncryptedUnsignedIdToken

    An encrypted ID token without a nested signature.

  • EncryptedIdTokenUnsupported

    Encrypted ID tokens are not supported.

  • UnknownSigningKey

    The signing key is unknown even after refreshing the provider keys.

  • IdTokenIssuerMismatch
  • IdTokenAudienceMismatch
  • AuthorizedPartyMismatch
  • IdTokenExpired
  • IdTokenNotYetValid
  • NonceMismatch
  • AccessTokenHashMismatch
  • MissingClaim(String)
  • SubjectMismatch
  • AuthenticationTooOld

    max_age was requested and auth_time is 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

  • NoS256

    The provider does not advertise PKCE S256 (with AssumeS256WhenUnadvertised: it lists other methods only).

  • AuthorizationCodeGrantUnsupported
  • ResponseModeUnsupported

    The configured response mode is not advertised.

  • AuthenticationMethodUnsupported

    The configured client authentication method is not advertised.

  • NoCommonSigningAlgorithm

    No configured ID-token algorithm is advertised.

  • NoCommonAssertionAlgorithm

    No configured client-assertion algorithm is advertised.

  • RequiresPushedAuthorization

    The provider requires pushed authorization requests.

  • RequiresRequestObjects

    The provider requires signed request objects.

  • InsecureAuthorizationEndpoint

    The authorization endpoint is not an absolute https URI.

  • InsecureEndSessionEndpoint

    The end-session endpoint is not an absolute https URI.

  • MissingTokenEndpoint
pub type Introspection {
  ActiveToken(TokenInfo)
  InactiveToken
}

Constructors

  • ActiveToken(TokenInfo)

    The provider reports the token active, and Hellhound checked exp (strictly) and nbf (with the clock tolerance). Introspection does not itself authorise a resource request: check audiences and scopes.

  • InactiveToken

May gain variants.

pub type IntrospectionError {
  IntrospectionNotSupported
  IntrospectionTokenTooLarge
  IntrospectionFailed(ProviderFailure)
}

Constructors

  • IntrospectionNotSupported

    The provider has no introspection endpoint, or this client has no credentials to call it with.

  • IntrospectionTokenTooLarge

    The 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.

  • LoginNotConfigured

    The client was built with service_client or resource_server.

  • LoginProviderUnavailable(ProviderFailure)

    Provider metadata is unavailable.

  • LoginProviderIncompatible(List(Incompatibility))

    Provider metadata is no longer compatible with Hellhound’s policy.

  • LoginStoreUnavailable

    The login store did not confirm the new login.

  • TooManyPendingLogins

    The 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.

  • LoginExpired

    The login expired before its callback was consumed.

  • LoginReplayed

    The login was already consumed by another callback.

  • LoginChanged

    The stored login changed between check and consumption.

  • TransactionStoreUnavailable

    The login store did not answer; the login may or may not have been consumed. Nothing was sent to the provider.

  • LoginRecordUnreadable

    The 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.

  • ExchangeOutcomeUnknown

    The 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.

  • LoginTimedOut

    The 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.

  • RecoveryForeign

    The recovery belongs to a different client configuration.

  • RecoveryEnded

    The session this recovery installed has since ended; the recovery cannot bring it back.

  • RecoveryExpired

    The 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
  • RedirectUriNotAllowed

    redirect_uri is neither the configured redirect URI nor one of config.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_time within 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. None uses the one configured with config.new; Some(uri) must equal that URI or one given to config.with_allowed_redirect_uris exactly, or the login fails with InvalidLoginOption(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) or SessionForeign.

  • InvalidPostLogoutRedirect
  • InvalidLogoutState
  • LogoutStoreUnavailable

    Custody 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

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

  • ProviderNotReady

    Metadata 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.

  • MalformedProviderResponse

    The provider’s response could not be used.

  • IssuerMismatch

    The provider’s metadata issuer differs from the configured issuer.

  • UnclassifiedBackendFailure

    A 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).

  • NoEndSessionEndpoint

    The 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

  • ProviderOutcomeUnknown

    The refresh request may have reached the provider; its outcome is unknown.

  • ResponseRejected(RefreshValidationError)

    The provider answered, but the response was not acceptable.

  • RefresherLost

    The 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

  • RefreshedSubjectMismatch

    The 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
  • RevocationUnsupported

    The 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

  • RevokeRefreshToken

    Revoke 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

  • SessionNotFound

    No live session has this reference: unknown, logged out, or past its absolute or idle lifetime.

  • SessionLost

    The session was held by the in-memory custody, which restarted since.

  • SessionForeign

    The session belongs to another client configuration.

  • SessionStoreUnavailable

    The custody store did not answer.

  • SessionRecordUnreadable

    The stored session did not open (tampering, or an unknown sealing key).

  • SessionHasNoAccessToken

    The session has no access token.

  • RefreshTokenUnavailable

    The access token expired and the session has no refresh token.

  • RefreshRevoked

    The 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.

  • RefreshWaitTimedOut

    Another 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.

  • RefreshRecoveryForeign

    The 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.

  • StartupTimedOut

    Discovery or previous-process cleanup exceeded the startup timeout.

  • AlreadyStarted

    The client’s processes are already running.

  • ProcessStartFailed

    A 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

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
  • ReceiveFailed

    The 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
  • UserinfoSubjectMismatch

    The response sub differs 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.

✨ Search Document