All files / realm/blong-access/adapter/db accessSessionRotate.ts

100% Statements 32/32
50% Branches 2/4
100% Functions 1/1
100% Lines 32/32

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 331x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 7x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 7x  
import {type IMeta, handler} from '@feasibleone/blong';
 
import * as model from './accessModel.ts';
 
type KnexQb = any;
 
/**
 * `access.session.rotate` — rotate a session's refresh-token hash and update
 * its last-activity timestamp after a successful renewal.
 *
 * The `tokenHash` column holds the SHA-256 hex of the CURRENT refresh token.
 * Renewal mints a fresh refresh token, so the hash must be rotated — this is
 * what makes a stolen, already-used refresh token useless (replay detection on
 * next use: the presented token's hash will not match the stored one).
 *
 * Wire: `access.session.rotate` — called by `login.token.refresh`.
 */
export default handler(() => ({
    async accessSessionRotate(
        params: {sessionId: string; tokenHash: string},
        $meta: IMeta,
    ): Promise<{success: boolean}> {
        const qb: KnexQb = this.config?.context?.queryBuilder;
        if (!qb) throw new Error('Database not available');
        const hex = model.binHex(params.sessionId);
        if (!hex) return {success: false};
        const updated = await qb('access_session')
            .where('sessionId', Buffer.from(hex, 'hex'))
            .update({tokenHash: params.tokenHash, lastActivityAt: new Date()});
        return {success: !!updated};
    },
}));