Files
QR-master/src/lib/session-edge.ts
2026-07-11 22:09:10 +02:00

63 lines
1.7 KiB
TypeScript

/**
* Edge-runtime variant of the signed-session verification used by middleware.
*
* Next.js middleware runs on the Edge runtime, where Node's `crypto` module is
* unavailable, so we verify the HMAC signature with Web Crypto (SubtleCrypto).
* Keep this in sync with `src/lib/session.ts` (same secret, same algorithm).
*/
function base64url(bytes: ArrayBuffer): string {
const view = new Uint8Array(bytes);
let bin = '';
for (let i = 0; i < view.length; i++) {
bin += String.fromCharCode(view[i]);
}
return btoa(bin).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}
function timingSafeEqual(a: string, b: string): boolean {
if (a.length !== b.length) return false;
let diff = 0;
for (let i = 0; i < a.length; i++) {
diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
}
return diff === 0;
}
/**
* Verify a signed cookie value in the Edge runtime.
* Returns the user id when the signature is valid, otherwise null.
*/
export async function verifySignedUserIdEdge(
value: string | undefined | null
): Promise<string | null> {
if (!value) return null;
const secret = process.env.NEXTAUTH_SECRET;
if (!secret) return null;
const separator = value.lastIndexOf('.');
if (separator <= 0 || separator === value.length - 1) {
return null;
}
const userId = value.slice(0, separator);
const providedSig = value.slice(separator + 1);
const key = await crypto.subtle.importKey(
'raw',
new TextEncoder().encode(secret),
{ name: 'HMAC', hash: 'SHA-256' },
false,
['sign']
);
const signature = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(userId));
const expectedSig = base64url(signature);
if (!timingSafeEqual(providedSig, expectedSig)) {
return null;
}
return userId;
}