AttestoPhoenix.PrincipalStore behaviour (AttestoPhoenix v0.10.0)

Copy Markdown View Source

The host-owned subject/principal contract.

The library resolves the subject during protected-resource authentication and builds the principal map minted into issued tokens, but the subject source (the host's user store) and the claim shaping are host policy. A host implements this behaviour and wires each callback into AttestoPhoenix.Config; this module is the contract those keys install and the recommended production shape.

Each @callback corresponds to the identically named AttestoPhoenix.Config key:

  • load_principal/1 (:load_principal, required)
  • build_principal/3 (:build_principal)
  • resolve_jwt_bearer_subject/1 (:resolve_jwt_bearer_subject, required only when the ID-JAG jwt-bearer grant is enabled)

Summary

Types

The host's opaque principal/subject representation.

Callbacks

Build the principal map passed to Attesto.Token.mint/3 for an authorization-code or client_credentials grant. Receives the resolved client, the subject identifier, and the granted scope. The returned map carries at least :sub and any host-owned claims.

Resolve the subject/principal by its identifier during protected-resource authentication. Returns {:ok, principal} or {:error, :not_found}.

Map a validated Identity Assertion JWT Authorization Grant (ID-JAG) to a local subject for the urn:ietf:params:oauth:grant-type:jwt-bearer grant (draft-ietf-oauth-identity-assertion-authz-grant-04).

Types

principal()

@type principal() :: term()

The host's opaque principal/subject representation.

Callbacks

build_principal(client, subject, scope)

(optional)
@callback build_principal(
  client :: term(),
  subject :: String.t(),
  scope :: [String.t()]
) :: map()

Build the principal map passed to Attesto.Token.mint/3 for an authorization-code or client_credentials grant. Receives the resolved client, the subject identifier, and the granted scope. The returned map carries at least :sub and any host-owned claims.

The returned :sub MUST be namespaced with the matching Attesto.PrincipalKind sub_prefix - Attesto.Token rejects an unprefixed subject at mint time (:invalid_sub). For the client_credentials grant (RFC 6749 §4.4) the subject handed in is the OAuth client_id, and Dynamic Client Registration (RFC 7591 §3.2.1) issues that id unprefixed: this callback is the sole place the kind prefix is applied. The prefix is mint-time defense-in-depth (a token's sub stays unambiguous across principal kinds), so namespacing here is mandatory rather than cosmetic.

load_principal(subject_id)

@callback load_principal(subject_id :: String.t()) ::
  {:ok, principal()} | {:error, :not_found}

Resolve the subject/principal by its identifier during protected-resource authentication. Returns {:ok, principal} or {:error, :not_found}.

resolve_jwt_bearer_subject(claims)

(optional)
@callback resolve_jwt_bearer_subject(claims :: map()) ::
  {:ok, subject :: String.t()} | String.t() | {:error, term()}

Map a validated Identity Assertion JWT Authorization Grant (ID-JAG) to a local subject for the urn:ietf:params:oauth:grant-type:jwt-bearer grant (draft-ietf-oauth-identity-assertion-authz-grant-04).

Receives the string-keyed, already-verified assertion claims (the signature, trusted iss, aud, client_id binding, exp/iat, and jti replay have all been checked). The host maps the asserted external identity - typically claims["sub"] (unique when scoped with claims["iss"]) and/or claims["email"] - to the local subject the issued token is minted for, the same subject string build_principal/3 then receives.

Returns {:ok, subject} (or a bare subject string) to authorize, or {:error, reason} (or any non-subject value) to deny - a deny becomes RFC 6749 §5.2 invalid_grant. Required only when the jwt-bearer grant is enabled (AttestoPhoenix.Config enforces this at boot).