Skip to main content

SessionLiveness

Trait SessionLiveness 

Source
pub trait SessionLiveness:
    Debug
    + Send
    + Sync {
    // Required method
    fn check<'life0, 'life1, 'async_trait>(
        &'life0 self,
        sid: &'life1 SessionId,
    ) -> Pin<Box<dyn Future<Output = Result<(), SessionLivenessError>> + Send + 'async_trait>>
       where 'life0: 'async_trait,
             'life1: 'async_trait,
             Self: 'async_trait;
}
Expand description

Per-request session-row liveness check.

Wired into [crate::PasJwtVerifier::with_session_liveness] as a verifier slot symmetric to [crate::PasJwtVerifier::with_epoch_revocation]. With no port wired, the verifier short-circuits the L2 check (matches pre-0.10.0 behavior).

§3-state contract

§Lenient on no-sid claim

When the bearer’s sid claim is None (machine credentials, AI-agent flows, R6 legacy admit per [crate::AuthSession::session_id]), the verifier admits without consulting this port — non-session-bound tokens have no row to look up. RFC_2026-05-08 §4.2 lock decision (lenient — matches the existing AuthSession::session_id invariant).

§Implementations

Consumer-side adapters: RCW ships PgSessionLiveness over scrcall.user_sessions; CTW ships the same shape over scctime.user_sessions. Each is ~10 lines of consumer-local code — schema name + DB pool are deployment-specific and never shipped from the SDK.

use async_trait::async_trait;
use pas_external::session_liveness::{SessionLiveness, SessionLivenessError};
use pas_external::types::SessionId;
use sqlx::PgPool;

pub struct PgSessionLiveness { pool: PgPool }

#[async_trait]
impl SessionLiveness for PgSessionLiveness {
    async fn check(&self, sid: &SessionId) -> Result<(), SessionLivenessError> {
        let row: Option<(Option<time::OffsetDateTime>,)> =
            sqlx::query_as("SELECT revoked_at FROM scrcall.user_sessions WHERE id = $1")
                .bind(&sid.0)
                .fetch_optional(&self.pool)
                .await
                .map_err(|e| SessionLivenessError::Transient(format!("session lookup: {e}")))?;
        match row {
            None | Some((Some(_),)) => Err(SessionLivenessError::Revoked),
            Some((None,)) => Ok(()),
        }
    }
}

Required Methods§

Source

fn check<'life0, 'life1, 'async_trait>( &'life0 self, sid: &'life1 SessionId, ) -> Pin<Box<dyn Future<Output = Result<(), SessionLivenessError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, Self: 'async_trait,

Implementors§