This repository has no description
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2026 sol pbc
3
4import { base64urlEncode } from "../auth";
5
6const NONCE_WINDOW_SECONDS = 300;
7
8function assertNonceSecret(secret: string): void {
9 if (!secret) {
10 throw new Error("OAuth nonce secret is required");
11 }
12}
13
14// `now` is epoch seconds.
15export async function deriveDpopNonce(secret: string, now: number): Promise<string> {
16 assertNonceSecret(secret);
17 const key = await crypto.subtle.importKey(
18 "raw",
19 new TextEncoder().encode(secret),
20 { name: "HMAC", hash: "SHA-256" },
21 false,
22 ["sign"],
23 );
24 const window = Math.floor(now / NONCE_WINDOW_SECONDS);
25 const signature = await crypto.subtle.sign(
26 "HMAC",
27 key,
28 new TextEncoder().encode(String(window)),
29 );
30 return base64urlEncode(signature);
31}
32
33export async function isValidDpopNonce(
34 secret: string,
35 nonce: string,
36 now: number,
37): Promise<boolean> {
38 assertNonceSecret(secret);
39 const current = await deriveDpopNonce(secret, now);
40 if (nonce === current) return true;
41 const previous = await deriveDpopNonce(secret, now - NONCE_WINDOW_SECONDS);
42 return nonce === previous;
43}