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§
Sourcetype Store: SessionStore
type Store: SessionStore
Session storage adapter.
Sourcetype Error: From<AuthError> + Debug + Send + Sync + 'static
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.
Sourcetype Role: Display + Serialize + Clone + Send + Sync + 'static
type Role: Display + Serialize + Clone + Send + Sync + 'static
Backend’s role type. The handler holds it opaquely between ACL lookup and JWT minting.
Displayso the handler can render it into the JWTroleclaim (which is a plain string per the canonical spec).Serializeso the audit hook can include it in structured logs.
Required Methods§
Sourcefn sessions(&self) -> &Self::Store
fn sessions(&self) -> &Self::Store
Session store handle. The handler invokes the
SessionStore methods through this.
Sourcefn 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 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.
Sourcefn 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 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.
Sourcefn challenge_ttl(&self) -> u64
fn challenge_ttl(&self) -> u64
Challenge TTL in seconds. Typical: 60.
Sourcefn access_token_ttl(&self) -> u64
fn access_token_ttl(&self) -> u64
Access-token TTL in seconds. Typical: 900 (15 min).
Sourcefn refresh_token_ttl(&self) -> u64
fn refresh_token_ttl(&self) -> u64
Refresh-token TTL in seconds. Typical: 86400 (24 h).
Provided Methods§
Sourcefn 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 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.
Sourcefn 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 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.
Sourcefn max_pending_challenges_per_did(&self) -> usize
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.
Sourcefn audit(&self, event: AuthAuditEvent<'_>)
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.
Sourcefn access_token_ttl_for_aal2(&self) -> u64
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).
Sourcefn didcomm_freshness_window(&self) -> u64
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".