Skip to main content

AuthBackend

Trait AuthBackend 

Source
pub trait AuthBackend:
    Send
    + Sync
    + 'static {
    type Store: SessionStore;
    type Error: From<AuthError> + Debug + Send + Sync + 'static;
    type Role: Display + Serialize + Clone + Send + Sync + 'static;

    // Required methods
    fn sessions(&self) -> &Self::Store;
    fn mint_access_token<'life0, 'life1, 'life2, 'life3, 'life4, 'life5, 'life6, 'life7, 'async_trait>(
        &'life0 self,
        subject: &'life1 str,
        session_id: &'life2 str,
        role: &'life3 Self::Role,
        contexts: &'life4 [String],
        amr: &'life5 [String],
        acr: &'life6 str,
        tee_attested: bool,
        ttl_secs: u64,
        jti: &'life7 str,
    ) -> Pin<Box<dyn Future<Output = Result<String, Self::Error>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait,
             'life2: 'async_trait,
             'life3: 'async_trait,
             'life4: 'async_trait,
             'life5: 'async_trait,
             'life6: 'async_trait,
             'life7: 'async_trait;
    fn check_acl<'life0, 'life1, 'async_trait>(
        &'life0 self,
        did: &'life1 str,
    ) -> Pin<Box<dyn Future<Output = Result<RoleResolution<Self::Role>, Self::Error>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait;
    fn challenge_ttl(&self) -> u64;
    fn access_token_ttl(&self) -> u64;
    fn refresh_token_ttl(&self) -> u64;

    // Provided methods
    fn validate_did<'life0, 'life1, 'async_trait>(
        &'life0 self,
        _did: &'life1 str,
    ) -> Pin<Box<dyn Future<Output = Result<(), Self::Error>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait { ... }
    fn attest_challenge<'life0, 'life1, 'async_trait>(
        &'life0 self,
        _challenge_bytes: &'life1 [u8; 32],
    ) -> Pin<Box<dyn Future<Output = Result<AttestationOutcome, Self::Error>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait { ... }
    fn max_pending_challenges_per_did(&self) -> usize { ... }
    fn audit(&self, event: AuthAuditEvent<'_>) { ... }
    fn access_token_ttl_for_aal2(&self) -> u64 { ... }
    fn didcomm_freshness_window(&self) -> u64 { ... }
}
Expand description

Pluggable backend for the canonical /auth/* handlers.

One implementation per service. Most methods have safe defaults; implementors override only the policy hooks their service actually exercises (TEE attestation, DID-method allowlist, etc.).

Required Associated Types§

Source

type Store: SessionStore

Session storage adapter.

Source

type Error: From<AuthError> + Debug + Send + Sync + 'static

Backend-local error type. Must convert from AuthError so the canonical handler can raise auth-specific failures. Must implement IntoResponse at the route boundary; the trait does not bound that here (would force an axum dependency on every backend), but the canonical handler surfaces the error verbatim and the route layer renders it via its existing path.

Source

type Role: Display + Serialize + Clone + Send + Sync + 'static

Backend’s role type. The handler holds it opaquely between ACL lookup and JWT minting.

  • Display so the handler can render it into the JWT role claim (which is a plain string per the canonical spec).
  • Serialize so the audit hook can include it in structured logs.

Required Methods§

Source

fn sessions(&self) -> &Self::Store

Session store handle. The handler invokes the SessionStore methods through this.

Source

fn mint_access_token<'life0, 'life1, 'life2, 'life3, 'life4, 'life5, 'life6, 'life7, 'async_trait>( &'life0 self, subject: &'life1 str, session_id: &'life2 str, role: &'life3 Self::Role, contexts: &'life4 [String], amr: &'life5 [String], acr: &'life6 str, tee_attested: bool, ttl_secs: u64, jti: &'life7 str, ) -> Pin<Box<dyn Future<Output = Result<String, Self::Error>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, 'life4: 'async_trait, 'life5: 'async_trait, 'life6: 'async_trait, 'life7: 'async_trait,

Mint an access token JWT for an authenticated session.

The trait abstracts over the concrete JWT minter — VTA + VTC use vti_common::auth::jwt::JwtKeys; did-hosting has its own minter type with the same shape but a separate AppError surface. Each backend implements this method using whatever minter it holds; the canonical handler treats the return value as an opaque base64url-encoded JWS.

Source

fn check_acl<'life0, 'life1, 'async_trait>( &'life0 self, did: &'life1 str, ) -> Pin<Box<dyn Future<Output = Result<RoleResolution<Self::Role>, Self::Error>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Resolve a DID to a role + context scope. Returning an error (typically AuthError::Forbidden) rejects the request before any other gate fires.

Source

fn challenge_ttl(&self) -> u64

Challenge TTL in seconds. Typical: 60.

Source

fn access_token_ttl(&self) -> u64

Access-token TTL in seconds. Typical: 900 (15 min).

Source

fn refresh_token_ttl(&self) -> u64

Refresh-token TTL in seconds. Typical: 86400 (24 h).

Provided Methods§

Source

fn validate_did<'life0, 'life1, 'async_trait>( &'life0 self, _did: &'life1 str, ) -> Pin<Box<dyn Future<Output = Result<(), Self::Error>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Optional DID-method validation gate. Default: accept any method (backends with no allowlist). VTA overrides in TEE mode to enforce allowed_did_methods.

Source

fn attest_challenge<'life0, 'life1, 'async_trait>( &'life0 self, _challenge_bytes: &'life1 [u8; 32], ) -> Pin<Box<dyn Future<Output = Result<AttestationOutcome, Self::Error>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Optional TEE attestation hook. Returns the attestation report (if produced) and whether attestation succeeded. Default: no attestation (None, false). VTA overrides in TEE mode; in TeeMode::Required a failure here must be raised as AuthError::AttestationFailed.

Source

fn max_pending_challenges_per_did(&self) -> usize

Cap on concurrent ChallengeSent sessions per DID. Default 10. Setting to 0 disables per-DID rate limiting (still IP-rate-limited at the tower-governor layer). Backends with low-trust callers may want this higher; backends with admin-only callers can keep it at 10.

Source

fn audit(&self, event: AuthAuditEvent<'_>)

Audit hook fired at the end of each handler. Default impl emits via tracing::info!(audit=true); backends with structured audit pipelines (e.g. VTC’s audit log with HMAC actor hashing) can override.

Source

fn access_token_ttl_for_aal2(&self) -> u64

Access-token TTL in seconds for a stepped-up (acr=aal2) session. Default: 1/3 of Self::access_token_ttl floored to a minimum of 60 seconds — closes M2 from the May 2026 security review, which observed that a leaked aal2 token has the same 15-minute window as a aal1 token despite the elevated privileges it grants.

Backends can override to set their own ratio or to disable the elevation (return access_token_ttl() for a uniform TTL).

Source

fn didcomm_freshness_window(&self) -> u64

DIDComm created_time freshness window in seconds. The canonical handler rejects messages older than this against session.created_at to bound replay risk. Default 60s.

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§