this repo has no description
1/**
2 * OAuth flow helpers for e2e tests
3 */
4
5import { randomBytes } from 'node:crypto';
6import { DpopClient } from './dpop.js';
7
8const BASE = 'http://localhost:8787';
9
10/**
11 * Get an OAuth token with a specific scope via full PAR -> authorize -> token flow
12 * @param {string} scope - The scope to request
13 * @param {string} did - The DID to authenticate as
14 * @param {string} password - The password for authentication
15 * @returns {Promise<{accessToken: string, refreshToken: string, dpop: DpopClient}>}
16 */
17export async function getOAuthTokenWithScope(scope, did, password) {
18 const dpop = await DpopClient.create();
19 const clientId = 'http://localhost:3000';
20 const redirectUri = 'http://localhost:3000/callback';
21 const codeVerifier = randomBytes(32).toString('base64url');
22 const challengeBuffer = await crypto.subtle.digest(
23 'SHA-256',
24 new TextEncoder().encode(codeVerifier),
25 );
26 const codeChallenge = Buffer.from(challengeBuffer).toString('base64url');
27
28 // PAR request
29 const parProof = await dpop.createProof('POST', `${BASE}/oauth/par`);
30 const parRes = await fetch(`${BASE}/oauth/par`, {
31 method: 'POST',
32 headers: {
33 'Content-Type': 'application/x-www-form-urlencoded',
34 DPoP: parProof,
35 },
36 body: new URLSearchParams({
37 client_id: clientId,
38 redirect_uri: redirectUri,
39 response_type: 'code',
40 scope: scope,
41 code_challenge: codeChallenge,
42 code_challenge_method: 'S256',
43 login_hint: did,
44 }).toString(),
45 });
46 const parData = await parRes.json();
47
48 // Authorize
49 const authRes = await fetch(`${BASE}/oauth/authorize`, {
50 method: 'POST',
51 headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
52 body: new URLSearchParams({
53 request_uri: parData.request_uri,
54 client_id: clientId,
55 password: password,
56 }).toString(),
57 redirect: 'manual',
58 });
59 const location = authRes.headers.get('location');
60 const authCode = new URL(location).searchParams.get('code');
61
62 // Token exchange
63 const tokenProof = await dpop.createProof('POST', `${BASE}/oauth/token`);
64 const tokenRes = await fetch(`${BASE}/oauth/token`, {
65 method: 'POST',
66 headers: {
67 'Content-Type': 'application/x-www-form-urlencoded',
68 DPoP: tokenProof,
69 },
70 body: new URLSearchParams({
71 grant_type: 'authorization_code',
72 code: authCode,
73 client_id: clientId,
74 redirect_uri: redirectUri,
75 code_verifier: codeVerifier,
76 }).toString(),
77 });
78 const tokenData = await tokenRes.json();
79
80 return {
81 accessToken: tokenData.access_token,
82 refreshToken: tokenData.refresh_token,
83 dpop,
84 };
85}