You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

56 lines
2.0 KiB

import { AUTH_DEFAULTS, AUTH_EVENT_NAMES, AUTH_REVOKE_REASONS } from '$libs/auth/consts';
import type { AuthClockPort, AuthCryptoPort, AuthStoreAdapter } from '$libs/auth/contracts';
import type { AuthRefreshFamilyId, AuthRefreshTokenId } from '$libs/auth/types';
import { AuthTokenReuseDetectedError } from '$libs/auth/errors';
import { createAuthRefreshTokenId } from '$libs/auth/ids';
import { createAuthRandomToken, hashAuthToken } from '$libs/auth/tokens';
export interface AuthRefreshRotationInput {
readonly store: AuthStoreAdapter;
readonly crypto: AuthCryptoPort;
readonly clock: AuthClockPort;
readonly token: string;
}
export interface AuthRefreshRotationResult {
readonly refreshToken: string;
readonly refreshTokenId: AuthRefreshTokenId;
readonly familyId: AuthRefreshFamilyId;
}
export async function rotateAuthRefreshToken(
input: AuthRefreshRotationInput
): Promise<AuthRefreshRotationResult> {
const nowMs = input.clock.nowMs();
const tokenHash = await hashAuthToken(input.crypto, input.token);
const current = await input.store.findRefreshTokenForUpdate({ tokenHash });
if (!current || current.revokedAt)
throw new AuthTokenReuseDetectedError(undefined, {
eventName: AUTH_EVENT_NAMES.REFRESH_REUSE_DETECTED
});
if (current.consumedAt && nowMs - current.consumedAt > AUTH_DEFAULTS.REFRESH_REUSE_GRACE_MS) {
await input.store.revokeRefreshFamily({
familyId: current.familyId,
nowMs,
reason: AUTH_REVOKE_REASONS.REFRESH_REUSE
});
throw new AuthTokenReuseDetectedError(undefined, {
eventName: AUTH_EVENT_NAMES.REFRESH_REUSE_DETECTED
});
}
const refreshToken = createAuthRandomToken(input.crypto);
const child = {
id: createAuthRefreshTokenId(input.crypto),
familyId: current.familyId,
tokenHash: await hashAuthToken(input.crypto, refreshToken),
parentTokenId: current.id,
issuedAt: nowMs
};
const result = await input.store.rotateRefreshToken({
currentTokenId: current.id,
childToken: child,
consumedAt: nowMs
});
return { refreshToken, refreshTokenId: result.child.id, familyId: result.child.familyId };
}

Powered by TurnKey Linux.