diff --git a/src/api/auth.rs b/src/api/auth.rs index 7a92daa..747b426 100644 --- a/src/api/auth.rs +++ b/src/api/auth.rs @@ -224,6 +224,7 @@ async fn issue_session( state .db .create_session(NewSession { + id: session_id, user_id, refresh_token_hash, platform: platform.to_string(), diff --git a/src/api/website.rs b/src/api/website.rs index ed0e92e..f2c7695 100644 --- a/src/api/website.rs +++ b/src/api/website.rs @@ -302,6 +302,7 @@ pub async fn website_verify( state .db .create_session(NewSession { + id: session_id, user_id: user.id, refresh_token_hash, platform: Platform::Web.to_string(), diff --git a/src/infra/db/sessions.rs b/src/infra/db/sessions.rs index 92985a8..afe82fc 100644 --- a/src/infra/db/sessions.rs +++ b/src/infra/db/sessions.rs @@ -22,11 +22,15 @@ impl Database { #[instrument(skip(self, input), fields(user_id = %input.user_id, platform = %input.platform))] pub async fn create_session(&self, input: NewSession) -> Result { let sql = format!( + // `id` is bound explicitly: the access token carries it as `sid`, + // so the row and the token must agree or revocation checks reject + // every request. "INSERT INTO sessions \ - (user_id, refresh_token_hash, platform, device_info, expires_at) \ - VALUES ($1, $2, $3, $4, $5) RETURNING {SESSION_COLS}" + (id, user_id, refresh_token_hash, platform, device_info, expires_at) \ + VALUES ($1, $2, $3, $4, $5, $6) RETURNING {SESSION_COLS}" ); let session = sqlx::query_as::<_, Session>(&sql) + .bind(input.id) .bind(input.user_id) .bind(&input.refresh_token_hash) .bind(&input.platform) diff --git a/src/models/db.rs b/src/models/db.rs index f5b9646..e8be03d 100644 --- a/src/models/db.rs +++ b/src/models/db.rs @@ -208,6 +208,11 @@ pub struct NewUser { #[derive(Debug, Clone)] pub struct NewSession { + /// The session's primary key. Supplied by the caller (NOT defaulted by the + /// DB) because the access token embeds it as `sid` for revocation: if the + /// row got a different id than the token, every authenticated request would + /// fail the liveness check. + pub id: Uuid, pub user_id: Uuid, pub refresh_token_hash: String, pub platform: String,