feat(oidc): impl back channel logout
This commit is contained in:
@@ -18,6 +18,11 @@ pub struct Session {
|
||||
/// RP-initiated logout URL so the IdP can terminate its own SSO session.
|
||||
/// `None` for password / magic-link sessions.
|
||||
oidc_id_token: Option<String>,
|
||||
/// OIDC session identifier (sid claim). Populated only when the IdP
|
||||
/// emits it. Enables per-device Back-Channel Logout — without it, a
|
||||
/// BCL notification would revoke all of the user's sessions rather
|
||||
/// than just the one that logged out on the far end.
|
||||
oidc_sid: Option<String>,
|
||||
}
|
||||
|
||||
impl Session {
|
||||
@@ -45,6 +50,7 @@ impl Session {
|
||||
revoked: false,
|
||||
family_id,
|
||||
oidc_id_token: None,
|
||||
oidc_sid: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +62,16 @@ impl Session {
|
||||
self
|
||||
}
|
||||
|
||||
/// Attach the OIDC session identifier from the id_token's `sid` claim.
|
||||
/// Optional even for OIDC sessions — only present when the IdP emits
|
||||
/// sid (Keycloak requires "Backchannel Logout Session Required" on the
|
||||
/// client). Without it, Back-Channel Logout falls back to sub-based
|
||||
/// revocation which is coarser (all of the user's OxiCloud sessions).
|
||||
pub fn with_oidc_sid(mut self, sid: String) -> Self {
|
||||
self.oidc_sid = Some(sid);
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn from_raw(
|
||||
id: Uuid,
|
||||
@@ -68,6 +84,7 @@ impl Session {
|
||||
revoked: bool,
|
||||
family_id: Uuid,
|
||||
oidc_id_token: Option<String>,
|
||||
oidc_sid: Option<String>,
|
||||
) -> Self {
|
||||
Self {
|
||||
id,
|
||||
@@ -80,6 +97,7 @@ impl Session {
|
||||
revoked,
|
||||
family_id,
|
||||
oidc_id_token,
|
||||
oidc_sid,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,4 +149,8 @@ impl Session {
|
||||
pub fn oidc_id_token(&self) -> Option<&str> {
|
||||
self.oidc_id_token.as_deref()
|
||||
}
|
||||
|
||||
pub fn oidc_sid(&self) -> Option<&str> {
|
||||
self.oidc_sid.as_deref()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,28 @@ pub trait SessionRepository: Send + Sync + 'static {
|
||||
/// Revokes all sessions in a token family (theft response)
|
||||
async fn revoke_session_family(&self, family_id: Uuid) -> SessionRepositoryResult<u64>;
|
||||
|
||||
/// Revokes every OxiCloud session whose OIDC sid claim matches.
|
||||
///
|
||||
/// Used by the Back-Channel Logout handler when the IdP sends a
|
||||
/// logout_token with a `sid` — this is the per-device path and
|
||||
/// matches (in the typical case) exactly one session row. Returns
|
||||
/// user IDs of every affected session so the caller can dispatch
|
||||
/// per-user lifecycle hooks.
|
||||
async fn revoke_sessions_by_oidc_sid(&self, sid: &str) -> SessionRepositoryResult<Vec<Uuid>>;
|
||||
|
||||
/// Revokes every session belonging to the user identified by
|
||||
/// `(oidc_provider, oidc_subject)`.
|
||||
///
|
||||
/// Fallback path for the Back-Channel Logout handler when the IdP
|
||||
/// omits `sid` from the logout_token — coarser than sid-based
|
||||
/// revocation (kills the user's other devices too). Returns the
|
||||
/// user id of the affected account, or `None` if no matching user.
|
||||
async fn revoke_user_sessions_by_oidc_subject(
|
||||
&self,
|
||||
oidc_provider: &str,
|
||||
oidc_subject: &str,
|
||||
) -> SessionRepositoryResult<Option<Uuid>>;
|
||||
|
||||
/// Deletes expired sessions
|
||||
async fn delete_expired_sessions(&self) -> SessionRepositoryResult<u64>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user