Atex.ServiceAuth (atex v0.9.1)

View Source

Validating and working with inter-service authentication tokens.

Provides functions for validating ATProto inter-service authentication JWTs, either from a raw token string or directly from an incoming Plug.Conn.

Validation covers:

  • Token timing (iat not in the future, exp not in the past).
  • Audience (aud) matching the caller-supplied expected value.
  • Optional lexicon method (lxm) matching the caller-supplied expected value.
  • Issuer DID resolution and signing-key verification via Atex.IdentityResolver.
  • Replay prevention via Atex.ServiceAuth.JTICache - each jti nonce may only be accepted once.

Configuration

The JTI cache implementation is pluggable. See Atex.ServiceAuth.JTICache for details.

Summary

Functions

Validate a service auth token from a Plug.Conn request.

Validate a raw service auth JWT string.

Types

validate_option()

@type validate_option() :: {:lxm, String.t()} | {:aud, String.t()}

Options accepted by validate_conn/2 and validate_jwt/2.

  • :aud - required. The expected audience string. The token's aud claim must equal this value exactly.
  • :lxm - optional. When provided, the token's lxm claim must match. If the token omits lxm the check is skipped; if the token carries lxm but no expected value is configured, validation fails with :lxm_not_configured.

Functions

validate_conn(conn, opts \\ [])

@spec validate_conn(Plug.Conn.t(), [validate_option()]) ::
  {:ok, jwt :: JOSE.JWT.t()} | {:error, reason :: atom()}

Validate a service auth token from a Plug.Conn request.

Extracts the Authorization: Bearer <jwt> header and delegates to validate_jwt/2. Returns {:error, :missing_token} when the header is absent or malformed.

Options

See validate_option/0.

Examples

iex> Atex.ServiceAuth.validate_conn(conn, aud: "did:web:my-service.example")
{:ok, %JOSE.JWT{}}

iex> Atex.ServiceAuth.validate_conn(conn, aud: "did:web:my-service.example", lxm: "app.bsky.feed.getTimeline")
{:error, :lxm_mismatch}

validate_jwt(jwt, opts \\ [])

@spec validate_jwt(String.t(), [validate_option()]) ::
  {:ok, jwt :: JOSE.JWT.t()} | {:error, reason :: atom()}

Validate a raw service auth JWT string.

Performs the full validation pipeline:

  1. Decodes the JWT payload (without verifying the signature yet) to extract claims.
  2. Validates :aud and :lxm against the provided options.
  3. Validates token timing (iat, exp).
  4. Resolves the issuer DID and retrieves the ATProto signing key from their DID document.
  5. Verifies the JWT signature with the resolved key.
  6. Records the jti nonce in Atex.ServiceAuth.JTICache - returns {:error, :replayed_token} if it has already been seen.

Options

See validate_option/0.

Error reasons

  • :aud_mismatch - aud claim does not match the expected audience.
  • :lxm_mismatch - lxm claim does not match the expected lexicon method.
  • :lxm_not_configured - token carries an lxm claim but no expected value was provided via :lxm opt.
  • :future_iat - iat is in the future.
  • :expired - exp is in the past.
  • :replayed_token - jti has already been used.

Examples

iex> Atex.ServiceAuth.validate_jwt(jwt, aud: "did:web:my-service.example")
{:ok, %JOSE.JWT{}}

iex> Atex.ServiceAuth.validate_jwt(expired_jwt, aud: "did:web:my-service.example")
{:error, :expired}