mirror of
https://github.com/Such-Software/smirk-backend-core.git
synced 2026-10-17 07:21:32 +02:00
fix(auth): the session row must carry the id the token claims
The sid revocation check added in e2589d4 rejected EVERY authenticated request
with 401. issue_session generated session_id = Uuid::new_v4(), minted the access
token with it as sid, then called create_session WITHOUT passing it, so Postgres
assigned the row a different primary key. is_session_live(sid) therefore never
matched and the liveness check failed closed on every call.
Both halves were individually correct, which is why unit tests did not catch it:
303 backend tests passed throughout. The e2e suite caught it, as a wallet that
authenticated successfully and then showed 0 for every asset.
NewSession now carries the id explicitly and create_session binds it, at all
three call sites (extension/website login and the refresh rotation, which shares
the helper).
Verified: fiat-balance e2e goes from all-zero to $108.21.
This commit is contained in:
@@ -224,6 +224,7 @@ async fn issue_session(
|
|||||||
state
|
state
|
||||||
.db
|
.db
|
||||||
.create_session(NewSession {
|
.create_session(NewSession {
|
||||||
|
id: session_id,
|
||||||
user_id,
|
user_id,
|
||||||
refresh_token_hash,
|
refresh_token_hash,
|
||||||
platform: platform.to_string(),
|
platform: platform.to_string(),
|
||||||
|
|||||||
@@ -302,6 +302,7 @@ pub async fn website_verify(
|
|||||||
state
|
state
|
||||||
.db
|
.db
|
||||||
.create_session(NewSession {
|
.create_session(NewSession {
|
||||||
|
id: session_id,
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
refresh_token_hash,
|
refresh_token_hash,
|
||||||
platform: Platform::Web.to_string(),
|
platform: Platform::Web.to_string(),
|
||||||
|
|||||||
@@ -22,11 +22,15 @@ impl Database {
|
|||||||
#[instrument(skip(self, input), fields(user_id = %input.user_id, platform = %input.platform))]
|
#[instrument(skip(self, input), fields(user_id = %input.user_id, platform = %input.platform))]
|
||||||
pub async fn create_session(&self, input: NewSession) -> Result<Session, AppError> {
|
pub async fn create_session(&self, input: NewSession) -> Result<Session, AppError> {
|
||||||
let sql = format!(
|
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 \
|
"INSERT INTO sessions \
|
||||||
(user_id, refresh_token_hash, platform, device_info, expires_at) \
|
(id, user_id, refresh_token_hash, platform, device_info, expires_at) \
|
||||||
VALUES ($1, $2, $3, $4, $5) RETURNING {SESSION_COLS}"
|
VALUES ($1, $2, $3, $4, $5, $6) RETURNING {SESSION_COLS}"
|
||||||
);
|
);
|
||||||
let session = sqlx::query_as::<_, Session>(&sql)
|
let session = sqlx::query_as::<_, Session>(&sql)
|
||||||
|
.bind(input.id)
|
||||||
.bind(input.user_id)
|
.bind(input.user_id)
|
||||||
.bind(&input.refresh_token_hash)
|
.bind(&input.refresh_token_hash)
|
||||||
.bind(&input.platform)
|
.bind(&input.platform)
|
||||||
|
|||||||
@@ -208,6 +208,11 @@ pub struct NewUser {
|
|||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct NewSession {
|
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 user_id: Uuid,
|
||||||
pub refresh_token_hash: String,
|
pub refresh_token_hash: String,
|
||||||
pub platform: String,
|
pub platform: String,
|
||||||
|
|||||||
Reference in New Issue
Block a user