@oauth42/next 0.5.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,14 +1,102 @@
1
+ var __typeError = (msg) => {
2
+ throw TypeError(msg);
3
+ };
4
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
5
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
6
+ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
7
+ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
8
+
9
+ // src/server/client-assertion.ts
10
+ import { createHash, createPublicKey, generateKeyPairSync, randomBytes, sign } from "crypto";
11
+ var _key, _public;
12
+ var _OAuth42ClientAssertion = class _OAuth42ClientAssertion {
13
+ constructor(kid, key) {
14
+ __privateAdd(this, _key);
15
+ __privateAdd(this, _public);
16
+ if (typeof kid !== "string" || !/^[\x21-\x7e]{1,128}$/.test(kid)) throw new Error("Invalid client signing key ID");
17
+ if (key.type !== "private" || key.asymmetricKeyType !== "ec" || key.asymmetricKeyDetails?.namedCurve !== "prime256v1") {
18
+ throw new Error("Client assertions require a private P-256 key");
19
+ }
20
+ __privateSet(this, _key, key);
21
+ this.kid = kid;
22
+ const publicKey = createPublicKey(key).export({ format: "jwk" });
23
+ __privateSet(this, _public, { crv: "P-256", kty: "EC", x: publicKey.x, y: publicKey.y });
24
+ this.thumbprint = createHash("sha256").update(JSON.stringify(__privateGet(this, _public))).digest("base64url");
25
+ }
26
+ static generate(kid) {
27
+ return new _OAuth42ClientAssertion(kid, generateKeyPairSync("ec", { namedCurve: "prime256v1" }).privateKey);
28
+ }
29
+ get publicJwk() {
30
+ return { ...__privateGet(this, _public), kid: this.kid };
31
+ }
32
+ /** Private keys for the server-only NextAuth/openid-client configuration, never registration. */
33
+ get oidcJwks() {
34
+ return { keys: [{
35
+ kty: "EC",
36
+ crv: "P-256",
37
+ x: __privateGet(this, _public).x,
38
+ y: __privateGet(this, _public).y,
39
+ d: __privateGet(this, _key).export({ format: "jwk" }).d,
40
+ kid: this.kid
41
+ }] };
42
+ }
43
+ static validateEndpoint(target) {
44
+ const url = new URL(target);
45
+ if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || url.hash || url.protocol !== "https:" && !["localhost", "127.0.0.1", "[::1]"].includes(url.hostname)) {
46
+ throw new Error("Client authentication requires HTTPS outside loopback development");
47
+ }
48
+ }
49
+ assertion(clientId, audience) {
50
+ _OAuth42ClientAssertion.validateEndpoint(audience);
51
+ if (!clientId || Buffer.byteLength(clientId) > 256) throw new Error("Invalid client identity");
52
+ const now = Math.floor(Date.now() / 1e3);
53
+ const header = { alg: "ES256", typ: "JWT", kid: this.kid };
54
+ const payload = { iss: clientId, sub: clientId, aud: audience, iat: now, exp: now + 60, jti: randomBytes(32).toString("base64url") };
55
+ const data = `${Buffer.from(JSON.stringify(header)).toString("base64url")}.${Buffer.from(JSON.stringify(payload)).toString("base64url")}`;
56
+ return `${data}.${sign("sha256", Buffer.from(data), { key: __privateGet(this, _key), dsaEncoding: "ieee-p1363" }).toString("base64url")}`;
57
+ }
58
+ async fetch(clientId, target, init, dpop) {
59
+ if (init.method?.toUpperCase() !== "POST" || !(typeof init.body === "string" || init.body instanceof URLSearchParams)) {
60
+ throw new Error("Client assertions require a form POST");
61
+ }
62
+ const prepare = () => {
63
+ const body = new URLSearchParams(init.body);
64
+ body.delete("client_secret");
65
+ body.set("client_id", clientId);
66
+ body.set("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer");
67
+ body.set("client_assertion", this.assertion(clientId, target));
68
+ const headers = new Headers(init.headers);
69
+ headers.delete("Authorization");
70
+ headers.delete("Content-Length");
71
+ headers.delete("Transfer-Encoding");
72
+ headers.set("Content-Type", "application/x-www-form-urlencoded");
73
+ return { ...init, body, headers, redirect: "error", cache: "no-store", signal: init.signal || AbortSignal.timeout(3e4) };
74
+ };
75
+ return dpop ? dpop.fetch(target, init, void 0, prepare) : fetch(target, prepare());
76
+ }
77
+ };
78
+ _key = new WeakMap();
79
+ _public = new WeakMap();
80
+ var OAuth42ClientAssertion = _OAuth42ClientAssertion;
81
+
1
82
  // src/provider.ts
2
83
  function OAuth42Provider(options) {
3
84
  const issuer = options.issuer || process.env.OAUTH42_ISSUER || "https://api.oauth42.com";
4
85
  const baseUrl = issuer.replace(/\/$/, "");
86
+ const { dpop, clientAssertionKey, ...providerOptions } = options;
87
+ if (clientAssertionKey) {
88
+ OAuth42ClientAssertion.validateEndpoint(`${baseUrl}/oauth2/token`);
89
+ providerOptions.clientSecret = void 0;
90
+ }
91
+ if (options.pkceEnabled === false) throw new Error("OAuth42 requires S256 PKCE");
5
92
  return {
6
93
  id: "oauth42",
7
94
  name: "OAuth42",
8
95
  type: "oauth",
9
96
  version: "2.0",
10
- // Use OIDC discovery to automatically find endpoints
11
- wellKnown: `${baseUrl}/.well-known/openid-configuration`,
97
+ // Pin O42 metadata: NextAuth v4 discovery does not compare the returned issuer.
98
+ // Constructing its OIDC client from these trusted endpoints preserves issuer binding.
99
+ jwks_endpoint: `${baseUrl}/.well-known/jwks.json`,
12
100
  // Use ID token for user profile (standard OIDC pattern)
13
101
  // This avoids an extra HTTP call to /userinfo on every auth flow
14
102
  // Apps can call /userinfo separately when they need extended claims (groups, company_id, etc.)
@@ -21,17 +109,26 @@ function OAuth42Provider(options) {
21
109
  response_type: "code"
22
110
  }
23
111
  },
24
- token: `${baseUrl}/oauth2/token`,
112
+ token: dpop ? {
113
+ url: `${baseUrl}/oauth2/token`,
114
+ async request({ client, provider, params, checks }) {
115
+ const tokens = await client.callback(provider.callbackUrl, params, checks, { DPoP: dpop.oidcKey });
116
+ dpop.assertTokenResponse(tokens);
117
+ return { tokens };
118
+ }
119
+ } : `${baseUrl}/oauth2/token`,
25
120
  userinfo: `${baseUrl}/oauth2/userinfo`,
121
+ ...clientAssertionKey ? { jwks: clientAssertionKey.oidcJwks } : {},
26
122
  client: {
27
123
  id: options.clientId,
28
- secret: options.clientSecret,
29
- token_endpoint_auth_method: "client_secret_post",
124
+ secret: clientAssertionKey ? void 0 : options.clientSecret,
125
+ token_endpoint_auth_method: clientAssertionKey ? "private_key_jwt" : "client_secret_post",
126
+ ...clientAssertionKey ? { token_endpoint_auth_signing_alg: "ES256" } : {},
30
127
  id_token_signed_response_alg: "RS256"
31
128
  // OAuth42 uses RS256 for ID tokens
32
129
  },
33
- issuer: baseUrl,
34
- checks: options.pkceEnabled !== false ? ["pkce", "state"] : ["state"],
130
+ issuer,
131
+ checks: ["pkce", "state", "nonce"],
35
132
  profile(profile, tokens) {
36
133
  return {
37
134
  id: profile.sub || profile.id || profile.email,
@@ -47,45 +144,127 @@ function OAuth42Provider(options) {
47
144
  bg: "#1e40af",
48
145
  text: "#ffffff"
49
146
  },
50
- options
147
+ // NextAuth v4 types require a string even when private_key_jwt sends no secret.
148
+ options: { ...providerOptions, clientSecret: providerOptions.clientSecret || "" }
149
+ };
150
+ }
151
+
152
+ // src/server/par.ts
153
+ async function pushAuthorizationURL(value, config) {
154
+ const base = config.issuer.replace(/\/$/, "");
155
+ let url;
156
+ try {
157
+ url = new URL(value);
158
+ } catch {
159
+ return value;
160
+ }
161
+ if (`${url.origin}${url.pathname}` !== `${base}/oauth2/authorize`) return value;
162
+ if (url.username || url.password || url.hash) throw new Error("Invalid authorization endpoint");
163
+ const body = new URLSearchParams(url.search);
164
+ if (body.getAll("client_id").length !== 1 || body.get("client_id") !== config.clientId || body.get("response_type") !== "code" || !body.get("state") || !body.get("nonce") || body.get("code_challenge_method") !== "S256" || !/^[A-Za-z0-9_-]{43}$/.test(body.get("code_challenge") || "") || ["request_uri", "client_secret", "client_assertion", "client_assertion_type"].some((k) => body.has(k))) {
165
+ throw new Error("Invalid PAR authorization transaction");
166
+ }
167
+ const target = `${base}/oauth2/par`;
168
+ OAuth42ClientAssertion.validateEndpoint(target);
169
+ if (!config.clientAssertionKey) body.set("client_secret", config.clientSecret || "");
170
+ const init = {
171
+ method: "POST",
172
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
173
+ body,
174
+ redirect: "error",
175
+ cache: "no-store",
176
+ signal: AbortSignal.timeout(3e4)
177
+ };
178
+ const response = config.clientAssertionKey ? await config.clientAssertionKey.fetch(config.clientId, target, init, config.dpop) : config.dpop ? await config.dpop.fetch(target, init) : await fetch(target, init);
179
+ if (response.status !== 201) throw new Error("Pushed authorization failed");
180
+ const pushed = await response.json();
181
+ if (typeof pushed.request_uri !== "string" || !/^urn:ietf:params:oauth:request_uri:[A-Za-z0-9_-]{43}$/.test(pushed.request_uri) || !Number.isInteger(pushed.expires_in) || pushed.expires_in <= 0 || pushed.expires_in > 600) throw new Error("Invalid PAR response");
182
+ url.search = new URLSearchParams({ client_id: config.clientId, request_uri: pushed.request_uri }).toString();
183
+ return url.href;
184
+ }
185
+ function withPushedAuthorization(handler, config) {
186
+ return async (request, context) => {
187
+ if (!(request instanceof Request)) throw new Error("PAR handlers require Next.js App Router Request/Response");
188
+ const response = await handler(request, context);
189
+ const location = response.headers.get("Location");
190
+ if (location) {
191
+ const next = await pushAuthorizationURL(location, config);
192
+ if (next === location) return response;
193
+ const headers = new Headers(response.headers);
194
+ headers.set("Location", next);
195
+ headers.delete("content-length");
196
+ return new Response(response.body, { status: response.status, headers });
197
+ }
198
+ if (response.headers.get("content-type")?.includes("application/json")) {
199
+ const body = await response.clone().json();
200
+ if (typeof body?.url === "string") {
201
+ const next = await pushAuthorizationURL(body.url, config);
202
+ if (next !== body.url) {
203
+ const headers = new Headers(response.headers);
204
+ headers.delete("content-length");
205
+ return new Response(JSON.stringify({ ...body, url: next }), { status: response.status, headers });
206
+ }
207
+ }
208
+ }
209
+ return response;
51
210
  };
52
211
  }
53
212
 
213
+ // src/server/verified-userinfo.ts
214
+ async function verifiedUserInfo(issuer, accessToken, subject, dpop) {
215
+ if (typeof accessToken !== "string" || !accessToken || typeof subject !== "string" || !subject) return null;
216
+ try {
217
+ const target = `${issuer.replace(/\/$/, "")}/oauth2/userinfo`;
218
+ const fetcher = dpop ? (url, init) => dpop.fetch(url, init, accessToken) : fetch;
219
+ const response = await fetcher(target, {
220
+ headers: { Authorization: `Bearer ${accessToken}` },
221
+ redirect: "error",
222
+ cache: "no-store",
223
+ signal: AbortSignal.timeout(3e4)
224
+ });
225
+ if (!response.ok) return null;
226
+ const user = await response.json();
227
+ return parseVerifiedUserInfo(user, subject);
228
+ } catch {
229
+ return null;
230
+ }
231
+ }
232
+ function parseVerifiedUserInfo(value, subject) {
233
+ if (typeof subject !== "string" || !subject || !value || typeof value !== "object" || Array.isArray(value)) return null;
234
+ const user = value;
235
+ if (user.sub !== subject) return null;
236
+ if (user.groups !== void 0 && (!Array.isArray(user.groups) || user.groups.some((group) => typeof group !== "string"))) return null;
237
+ if (user.email !== void 0 && typeof user.email !== "string") return null;
238
+ if (user.email_verified !== void 0 && typeof user.email_verified !== "boolean") return null;
239
+ const profile = { sub: user.sub };
240
+ for (const field of ["email", "name", "preferred_username", "username", "given_name", "family_name", "company_id", "company_name"]) {
241
+ if (user[field] != null) {
242
+ if (typeof user[field] !== "string") return null;
243
+ profile[field] = user[field];
244
+ }
245
+ }
246
+ if (user.email_verified !== void 0) profile.email_verified = user.email_verified;
247
+ if (user.groups !== void 0) profile.groups = user.groups;
248
+ return profile;
249
+ }
250
+ async function verifiedBackendSessionUserInfo(issuer, session, subject, dpop) {
251
+ if (typeof session.access_token !== "string" || !session.access_token) return null;
252
+ if (Object.prototype.hasOwnProperty.call(session, "user_info")) {
253
+ return parseVerifiedUserInfo(session.user_info, subject);
254
+ }
255
+ return verifiedUserInfo(issuer, session.access_token, subject, dpop);
256
+ }
257
+
54
258
  // src/server/auth.ts
55
259
  import NextAuthDefault from "next-auth";
56
260
 
57
261
  // src/server/session.ts
58
262
  import { getServerSession as getNextAuthSession } from "next-auth";
59
- import { headers } from "next/headers";
60
- var REFRESHED_TOKEN_HEADER = "x-oauth42-refreshed-token";
61
263
  async function getOAuth42Session(...args) {
62
264
  const session = await getNextAuthSession(...args);
63
- if (!session) {
265
+ if (!session || session.error) {
64
266
  return null;
65
267
  }
66
- let refreshedToken = null;
67
- try {
68
- if (args.length === 1) {
69
- const headersList = await headers();
70
- refreshedToken = headersList.get(REFRESHED_TOKEN_HEADER);
71
- } else if (args.length === 3) {
72
- const req = args[0];
73
- refreshedToken = req.headers[REFRESHED_TOKEN_HEADER] || null;
74
- }
75
- } catch {
76
- }
77
- if (refreshedToken) {
78
- const sessionToken = session.accessToken;
79
- console.log("[OAuth42 Session] Using refreshed token from middleware:", {
80
- refreshedPrefix: refreshedToken.substring(0, 20),
81
- sessionPrefix: sessionToken?.substring(0, 20),
82
- tokensMatch: refreshedToken === sessionToken
83
- });
84
- return {
85
- ...session,
86
- accessToken: refreshedToken
87
- };
88
- }
89
268
  return session;
90
269
  }
91
270
  function withOAuth42Session(handler, authOptions) {
@@ -123,66 +302,59 @@ function isBackendSessionMode(mode) {
123
302
  function sessionEndpoint(issuer) {
124
303
  return `${issuer.replace(/\/$/, "")}/api/sdk/sessions`;
125
304
  }
126
- async function createBackendSession(params) {
127
- const response = await fetch(sessionEndpoint(params.issuer), {
128
- method: "POST",
305
+ async function sessionFetch(params, target, body, credential, method = "POST") {
306
+ OAuth42ClientAssertion.validateEndpoint(target);
307
+ const prepare = () => ({
308
+ method,
309
+ redirect: "error",
310
+ cache: "no-store",
311
+ signal: AbortSignal.timeout(3e4),
129
312
  headers: { "Content-Type": "application/json" },
130
313
  body: JSON.stringify({
131
- access_token: params.accessToken,
132
- refresh_token: params.refreshToken,
133
- expires_at: params.expiresAt ?? Math.floor(Date.now() / 1e3) + 3600,
134
- id_token: params.idToken,
314
+ ...body,
135
315
  client_id: params.clientId,
136
- client_secret: params.clientSecret,
137
- issuer: params.issuer,
138
- user: params.user
316
+ ...params.clientAssertionKey ? {
317
+ client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
318
+ client_assertion: params.clientAssertionKey.assertion(params.clientId, target)
319
+ } : { client_secret: params.clientSecret }
139
320
  })
140
321
  });
141
- if (!response.ok) {
142
- throw new Error(`OAuth42 backend session create failed: ${response.status}`);
143
- }
144
- return response.json();
322
+ return params.dpop ? params.dpop.fetch(target, {}, credential, prepare) : fetch(target, prepare());
323
+ }
324
+ async function sessionResponse(response, params) {
325
+ if (!response.ok) throw new Error(`OAuth42 backend session request failed: ${response.status}`);
326
+ const session = await response.json();
327
+ if (params.dpop) params.dpop.assertTokenResponse(session);
328
+ else if (session.token_type?.toLowerCase() === "dpop") throw new Error("Backend session requires its retained proof key");
329
+ return session;
330
+ }
331
+ async function createBackendSession(params) {
332
+ return sessionResponse(await sessionFetch(params, sessionEndpoint(params.issuer), {
333
+ access_token: params.accessToken,
334
+ refresh_token: params.refreshToken,
335
+ id_token: params.idToken,
336
+ user: params.user
337
+ }, params.accessToken), params);
145
338
  }
146
339
  async function resolveBackendSession(params) {
147
- const response = await fetch(`${sessionEndpoint(params.issuer)}/resolve`, {
148
- method: "POST",
149
- headers: { "Content-Type": "application/json" },
150
- body: JSON.stringify({
151
- session_id: params.sessionId,
152
- client_id: params.clientId,
153
- client_secret: params.clientSecret,
154
- issuer: params.issuer
155
- })
156
- });
157
- if (response.status === 401 || response.status === 403 || response.status === 404) {
158
- return null;
159
- }
160
- if (!response.ok) {
161
- throw new Error(`OAuth42 backend session resolve failed: ${response.status}`);
162
- }
163
- return response.json();
340
+ const response = await sessionFetch(params, `${sessionEndpoint(params.issuer)}/resolve`, { session_id: params.sessionId }, params.sessionId);
341
+ if ([401, 403, 404].includes(response.status)) return null;
342
+ return sessionResponse(response, params);
164
343
  }
165
344
  async function revokeBackendSession(params) {
166
- await fetch(sessionEndpoint(params.issuer), {
167
- method: "DELETE",
168
- headers: { "Content-Type": "application/json" },
169
- body: JSON.stringify({
170
- session_id: params.sessionId,
171
- client_id: params.clientId,
172
- client_secret: params.clientSecret
173
- })
174
- });
345
+ const response = await sessionFetch(params, sessionEndpoint(params.issuer), { session_id: params.sessionId }, params.sessionId, "DELETE");
346
+ if (!response.ok) throw new Error(`OAuth42 backend session revoke failed: ${response.status}`);
175
347
  }
176
348
 
177
349
  // src/server/auth.ts
178
350
  var NextAuth = NextAuthDefault.default || NextAuthDefault;
179
- var activeRefresh = null;
351
+ var activeRefreshes = /* @__PURE__ */ new Map();
180
352
  function createAuth(options = {}) {
181
353
  const clientId = options.clientId || process.env.OAUTH42_CLIENT_ID;
182
- const clientSecret = options.clientSecret || process.env.OAUTH42_CLIENT_SECRET;
354
+ const clientSecret = options.clientAssertionKey ? "" : options.clientSecret || process.env.OAUTH42_CLIENT_SECRET;
183
355
  const issuer = options.issuer || process.env.NEXT_PUBLIC_OAUTH_ISSUER || process.env.OAUTH42_ISSUER || "https://localhost:8443";
184
356
  const useBackendSessions = isBackendSessionMode(options.sessionStore);
185
- if (!clientId || !clientSecret) {
357
+ if (!clientId || !clientSecret && !options.clientAssertionKey) {
186
358
  throw new Error(
187
359
  "OAuth42 client credentials are required. Set OAUTH42_CLIENT_ID and OAUTH42_CLIENT_SECRET environment variables or pass them in the options."
188
360
  );
@@ -191,14 +363,17 @@ function createAuth(options = {}) {
191
363
  providers: [
192
364
  OAuth42Provider({
193
365
  clientId,
194
- clientSecret,
366
+ clientSecret: clientSecret || "",
195
367
  issuer,
196
368
  scopes: options.scopes,
197
- pkceEnabled: options.pkceEnabled
369
+ pkceEnabled: options.pkceEnabled,
370
+ dpop: options.dpop,
371
+ clientAssertionKey: options.clientAssertionKey
198
372
  })
199
373
  ],
200
374
  callbacks: {
201
375
  async jwt({ token, account, profile }) {
376
+ if (options.clientAssertionKey) delete token.clientSecret;
202
377
  console.log("[OAuth42 SDK] JWT callback called", { hasAccount: !!account, hasProfile: !!profile });
203
378
  if (account) {
204
379
  if (useBackendSessions) {
@@ -207,9 +382,11 @@ function createAuth(options = {}) {
207
382
  }
208
383
  const oauth42Profile = profile;
209
384
  const backendSession = await createBackendSession({
385
+ dpop: options.dpop,
386
+ clientAssertionKey: options.clientAssertionKey,
210
387
  issuer,
211
388
  clientId,
212
- clientSecret,
389
+ clientSecret: clientSecret || "",
213
390
  accessToken: account.access_token,
214
391
  refreshToken: account.refresh_token,
215
392
  expiresAt: account.expires_at,
@@ -233,7 +410,8 @@ function createAuth(options = {}) {
233
410
  token.expiresAt = account.expires_at;
234
411
  token.idToken = account.id_token;
235
412
  token.clientId = clientId;
236
- token.clientSecret = clientSecret;
413
+ if (options.clientAssertionKey) delete token.clientSecret;
414
+ else token.clientSecret = clientSecret;
237
415
  token.issuer = issuer;
238
416
  }
239
417
  }
@@ -252,25 +430,36 @@ function createAuth(options = {}) {
252
430
  },
253
431
  async session({ session, token }) {
254
432
  console.log("[OAuth42 SDK] Session callback called", { hasToken: !!token, hasSession: !!session });
433
+ const revokedSession = () => {
434
+ session.error = "SessionRevoked";
435
+ delete session.accessToken;
436
+ delete session.idToken;
437
+ delete session.userInfo;
438
+ delete session.user;
439
+ return session;
440
+ };
441
+ if (token.error) return revokedSession();
442
+ let verified;
255
443
  if (useBackendSessions) {
256
444
  const sessionId = token.oauth42SessionId;
257
445
  if (!sessionId) {
258
- session.error = "SessionRevoked";
259
- return session;
446
+ return revokedSession();
260
447
  }
261
448
  const backendSession = await resolveBackendSession({
449
+ dpop: options.dpop,
450
+ clientAssertionKey: options.clientAssertionKey,
262
451
  issuer,
263
452
  clientId,
264
- clientSecret,
453
+ clientSecret: clientSecret || "",
265
454
  sessionId
266
455
  });
267
456
  if (!backendSession) {
268
- session.error = "SessionRevoked";
269
- return session;
457
+ return revokedSession();
270
458
  }
271
459
  session.accessToken = backendSession.access_token;
272
460
  session.idToken = backendSession.id_token;
273
461
  token.expiresAt = backendSession.expires_at;
462
+ verified = await verifiedBackendSessionUserInfo(issuer, backendSession, token.sub, options.dpop);
274
463
  if (backendSession.user && session.user) {
275
464
  session.user.email = backendSession.user.email;
276
465
  session.user.name = backendSession.user.name;
@@ -281,6 +470,23 @@ function createAuth(options = {}) {
281
470
  } else {
282
471
  session.accessToken = token.accessToken;
283
472
  session.idToken = token.idToken;
473
+ verified = await verifiedUserInfo(issuer, session.accessToken, token.sub, options.dpop);
474
+ }
475
+ if (!verified) {
476
+ return revokedSession();
477
+ }
478
+ session.userInfo = verified;
479
+ token.email = verified.email;
480
+ token.name = verified.name;
481
+ token.username = verified.preferred_username ?? verified.username;
482
+ token.emailVerified = verified.email_verified;
483
+ token.groups = verified.groups ?? [];
484
+ if (session.user) {
485
+ session.user.email = verified.email;
486
+ session.user.name = verified.name;
487
+ session.user.username = verified.preferred_username ?? verified.username;
488
+ session.user.emailVerified = verified.email_verified;
489
+ session.user.groups = verified.groups ?? [];
284
490
  }
285
491
  if (token.error) {
286
492
  session.error = token.error;
@@ -315,9 +521,11 @@ function createAuth(options = {}) {
315
521
  const sessionId = message.token?.oauth42SessionId;
316
522
  if (sessionId) {
317
523
  await revokeBackendSession({
524
+ dpop: options.dpop,
525
+ clientAssertionKey: options.clientAssertionKey,
318
526
  issuer,
319
527
  clientId,
320
- clientSecret,
528
+ clientSecret: clientSecret || "",
321
529
  sessionId
322
530
  }).catch((error) => {
323
531
  console.warn("[OAuth42 SDK] Failed to revoke backend session on signOut:", error);
@@ -404,31 +612,48 @@ function createAuth(options = {}) {
404
612
  }
405
613
  }
406
614
  };
407
- const handler = NextAuth(authOptions);
615
+ const nextAuthHandler = NextAuth(authOptions);
616
+ const handler = options.pushedAuthorization ? withPushedAuthorization(nextAuthHandler, { issuer, clientId, clientSecret, dpop: options.dpop, clientAssertionKey: options.clientAssertionKey }) : nextAuthHandler;
408
617
  return {
409
618
  auth: authOptions,
410
619
  handlers: { GET: handler, POST: handler }
411
620
  };
412
621
  }
413
622
  var getServerSession = getOAuth42Session;
414
- async function refreshAccessToken(token, clientId, clientSecret, issuer) {
415
- if (activeRefresh) {
416
- console.log("[OAuth42] Refresh already in progress, waiting...");
417
- return await activeRefresh;
623
+ async function refreshAccessToken(token, clientId, clientSecret, issuer, dpop, clientAssertionKey) {
624
+ if (typeof token.refreshToken !== "string" || !token.refreshToken) {
625
+ return { ...token, error: "RefreshAccessTokenError" };
626
+ }
627
+ const key = JSON.stringify([issuer || process.env.OAUTH42_ISSUER || "https://api.oauth42.com", clientId, clientSecret, token.sub, token.refreshToken, dpop?.thumbprint, clientAssertionKey?.thumbprint, clientAssertionKey?.kid]);
628
+ let refresh = activeRefreshes.get(key);
629
+ if (!refresh) {
630
+ refresh = doRefresh(token, clientId, clientSecret, issuer, dpop, clientAssertionKey);
631
+ activeRefreshes.set(key, refresh);
418
632
  }
419
- activeRefresh = doRefresh(token, clientId, clientSecret, issuer);
420
633
  try {
421
- return await activeRefresh;
634
+ const result = await refresh;
635
+ const updated = {
636
+ ...token,
637
+ accessToken: result.accessToken,
638
+ refreshToken: result.refreshToken,
639
+ expiresAt: result.expiresAt,
640
+ error: result.error
641
+ };
642
+ if (clientAssertionKey) delete updated.clientSecret;
643
+ return updated;
422
644
  } finally {
423
- activeRefresh = null;
645
+ if (activeRefreshes.get(key) === refresh) activeRefreshes.delete(key);
424
646
  }
425
647
  }
426
- async function doRefresh(token, clientId, clientSecret, issuer) {
648
+ async function doRefresh(token, clientId, clientSecret, issuer, dpop, clientAssertionKey) {
427
649
  try {
428
650
  const baseUrl = issuer || process.env.OAUTH42_ISSUER || "https://api.oauth42.com";
429
651
  const tokenUrl = `${baseUrl}/oauth2/token`;
430
652
  const fetchOptions = {
431
653
  method: "POST",
654
+ redirect: "error",
655
+ cache: "no-store",
656
+ signal: AbortSignal.timeout(3e4),
432
657
  headers: {
433
658
  "Content-Type": "application/x-www-form-urlencoded"
434
659
  },
@@ -439,17 +664,12 @@ async function doRefresh(token, clientId, clientSecret, issuer) {
439
664
  client_secret: clientSecret
440
665
  })
441
666
  };
442
- if (process.env.NODE_ENV !== "production" && tokenUrl.startsWith("https://")) {
443
- const https = await import("https");
444
- fetchOptions.agent = new https.Agent({
445
- rejectUnauthorized: false
446
- });
447
- }
448
- const response = await fetch(tokenUrl, fetchOptions);
667
+ const response = clientAssertionKey ? await clientAssertionKey.fetch(clientId, tokenUrl, fetchOptions, dpop) : dpop ? await dpop.fetch(tokenUrl, fetchOptions) : await fetch(tokenUrl, fetchOptions);
449
668
  const refreshedTokens = await response.json();
450
669
  if (!response.ok) {
451
670
  throw refreshedTokens;
452
671
  }
672
+ dpop?.assertTokenResponse(refreshedTokens);
453
673
  console.log("[OAuth42] Token refreshed successfully");
454
674
  return {
455
675
  ...token,
@@ -461,7 +681,7 @@ async function doRefresh(token, clientId, clientSecret, issuer) {
461
681
  error: void 0
462
682
  };
463
683
  } catch (error) {
464
- console.error("[OAuth42] Failed to refresh access token:", error);
684
+ console.error("[OAuth42] Failed to refresh access token");
465
685
  return {
466
686
  ...token,
467
687
  error: "RefreshAccessTokenError"
@@ -475,14 +695,20 @@ import { getToken, encode } from "next-auth/jwt";
475
695
  var ALLOWED_COOKIE_SIZE = 4096;
476
696
  var ESTIMATED_EMPTY_COOKIE_SIZE = 163;
477
697
  var CHUNK_SIZE = ALLOWED_COOKIE_SIZE - ESTIMATED_EMPTY_COOKIE_SIZE;
478
- var REFRESH_RACE_GRACE_SECONDS = 60;
479
698
  var DEFAULT_SESSION_MAX_AGE_SECONDS = 365 * 24 * 60 * 60;
480
699
  var pendingRefreshes = /* @__PURE__ */ new Map();
481
- function shouldPreserveSessionOnRefreshFailure(params) {
482
- if (params.error !== "invalid_grant" || !params.expiresAt) {
483
- return false;
700
+ function forwardedHeaders(req, cookieName, jwt) {
701
+ const headers = new Headers(req.headers);
702
+ headers.delete("x-oauth42-refreshed-token");
703
+ if (cookieName && jwt) {
704
+ const cookies = req.cookies.getAll().filter((cookie) => cookie.name !== cookieName && !cookie.name.startsWith(`${cookieName}.`));
705
+ if (jwt.length <= CHUNK_SIZE) cookies.push({ name: cookieName, value: jwt });
706
+ else for (let i = 0; i * CHUNK_SIZE < jwt.length; i++) {
707
+ cookies.push({ name: `${cookieName}.${i}`, value: jwt.slice(i * CHUNK_SIZE, (i + 1) * CHUNK_SIZE) });
708
+ }
709
+ headers.set("cookie", cookies.map((cookie) => `${encodeURIComponent(cookie.name)}=${encodeURIComponent(cookie.value)}`).join("; "));
484
710
  }
485
- return params.now - params.expiresAt <= REFRESH_RACE_GRACE_SECONDS;
711
+ return headers;
486
712
  }
487
713
  function resolveRefreshConfig(options, token) {
488
714
  return {
@@ -491,11 +717,15 @@ function resolveRefreshConfig(options, token) {
491
717
  issuer: options.issuer || token.issuer || process.env.OAUTH42_ISSUER || "https://localhost:8443"
492
718
  };
493
719
  }
494
- async function refreshTokens(refreshToken, clientId, clientSecret, issuer) {
720
+ async function refreshTokens(refreshToken, clientId, clientSecret, issuer, dpop, clientAssertionKey) {
495
721
  try {
496
722
  const tokenUrl = `${issuer}/oauth2/token`;
497
- const response = await fetch(tokenUrl, {
723
+ const fetcher = clientAssertionKey ? (url, init) => clientAssertionKey.fetch(clientId, url, init, dpop) : dpop ? dpop.fetch.bind(dpop) : fetch;
724
+ const response = await fetcher(tokenUrl, {
498
725
  method: "POST",
726
+ redirect: "error",
727
+ cache: "no-store",
728
+ signal: AbortSignal.timeout(3e4),
499
729
  headers: {
500
730
  "Content-Type": "application/x-www-form-urlencoded"
501
731
  },
@@ -508,9 +738,10 @@ async function refreshTokens(refreshToken, clientId, clientSecret, issuer) {
508
738
  });
509
739
  const data = await response.json();
510
740
  if (!response.ok) {
511
- console.error("[OAuth42 Middleware] Token refresh failed:", data);
741
+ console.error("[OAuth42 Middleware] Token refresh failed:", response.status);
512
742
  return { success: false, error: data.error || "refresh_failed" };
513
743
  }
744
+ dpop?.assertTokenResponse(data);
514
745
  console.log("[OAuth42 Middleware] Token refreshed successfully");
515
746
  return {
516
747
  success: true,
@@ -519,14 +750,14 @@ async function refreshTokens(refreshToken, clientId, clientSecret, issuer) {
519
750
  expiresAt: Math.floor(Date.now() / 1e3) + (data.expires_in || 3600)
520
751
  };
521
752
  } catch (error) {
522
- console.error("[OAuth42 Middleware] Token refresh error:", error);
753
+ console.error("[OAuth42 Middleware] Token refresh request failed");
523
754
  return { success: false, error: "refresh_error" };
524
755
  }
525
756
  }
526
757
  function withOAuth42Auth(options = {}) {
527
758
  const secret = process.env.NEXTAUTH_SECRET;
528
759
  if (!secret) {
529
- console.warn("[OAuth42 Middleware] NEXTAUTH_SECRET not set");
760
+ throw new Error("NEXTAUTH_SECRET is required for OAuth42 middleware");
530
761
  }
531
762
  return async function middleware(req) {
532
763
  const cookieName = options.cookiePrefix ? `${options.cookiePrefix}.session-token` : "next-auth.session-token";
@@ -537,22 +768,41 @@ function withOAuth42Auth(options = {}) {
537
768
  });
538
769
  const pathname = req.nextUrl.pathname;
539
770
  if (options.publicPaths?.some((path) => pathname.startsWith(path))) {
540
- return NextResponse.next();
771
+ return NextResponse.next({ request: { headers: forwardedHeaders(req) } });
541
772
  }
542
773
  const needsProtection = options.protectedPaths ? options.protectedPaths.some((path) => pathname.startsWith(path)) : true;
543
774
  if (!needsProtection) {
544
- return NextResponse.next();
775
+ return NextResponse.next({ request: { headers: forwardedHeaders(req) } });
545
776
  }
546
- if (!token) {
777
+ if (!token || token.error) {
547
778
  const signInUrl = options.pages?.signIn || "/auth/signin";
548
779
  const url = new URL(signInUrl, req.url);
549
780
  url.searchParams.set("callbackUrl", pathname);
550
781
  return NextResponse.redirect(url);
551
782
  }
552
783
  const refreshConfig = resolveRefreshConfig(options, token);
784
+ const denied = () => {
785
+ const url = new URL(options.pages?.signIn || "/auth/signin", req.url);
786
+ url.searchParams.set("callbackUrl", pathname);
787
+ const response = NextResponse.redirect(url);
788
+ for (const cookie of req.cookies.getAll()) {
789
+ if (cookie.name === cookieName || cookie.name.startsWith(`${cookieName}.`)) response.cookies.delete(cookie.name);
790
+ }
791
+ return response;
792
+ };
793
+ const verifyAndAuthorize = async (current, resolved) => {
794
+ const user = resolved === void 0 ? await verifiedUserInfo(refreshConfig.issuer, current.accessToken, token.sub, options.dpop) : resolved;
795
+ if (!user) return false;
796
+ current.groups = user.groups ?? [];
797
+ current.email = user.email;
798
+ current.name = user.name;
799
+ current.username = user.preferred_username ?? user.username;
800
+ current.emailVerified = user.email_verified;
801
+ return options.callbacks?.authorized ? options.callbacks.authorized({ token: current, req }) : true;
802
+ };
553
803
  if (isBackendSessionMode(options.sessionStore)) {
554
804
  const sessionId = token.oauth42SessionId;
555
- if (!sessionId || !refreshConfig.clientId || !refreshConfig.clientSecret) {
805
+ if (!sessionId || !refreshConfig.clientId || !refreshConfig.clientSecret && !options.clientAssertionKey) {
556
806
  const signInUrl = options.pages?.signIn || "/auth/signin";
557
807
  const url = new URL(signInUrl, req.url);
558
808
  url.searchParams.set("callbackUrl", pathname);
@@ -561,9 +811,11 @@ function withOAuth42Auth(options = {}) {
561
811
  return response;
562
812
  }
563
813
  const backendSession = await resolveBackendSession({
814
+ dpop: options.dpop,
815
+ clientAssertionKey: options.clientAssertionKey,
564
816
  issuer: refreshConfig.issuer,
565
817
  clientId: refreshConfig.clientId,
566
- clientSecret: refreshConfig.clientSecret,
818
+ clientSecret: refreshConfig.clientSecret || "",
567
819
  sessionId
568
820
  }).catch((error) => {
569
821
  console.error("[OAuth42 Middleware] Backend session resolve failed:", error);
@@ -577,8 +829,9 @@ function withOAuth42Auth(options = {}) {
577
829
  response.cookies.delete(cookieName);
578
830
  return response;
579
831
  }
580
- const requestHeaders = new Headers(req.headers);
581
- requestHeaders.set("x-oauth42-refreshed-token", backendSession.access_token);
832
+ const verified = await verifiedBackendSessionUserInfo(refreshConfig.issuer, backendSession, token.sub, options.dpop);
833
+ if (!await verifyAndAuthorize({ ...token, accessToken: backendSession.access_token }, verified)) return denied();
834
+ const requestHeaders = forwardedHeaders(req);
582
835
  return NextResponse.next({
583
836
  request: {
584
837
  headers: requestHeaders
@@ -594,13 +847,12 @@ function withOAuth42Auth(options = {}) {
594
847
  expiresAt,
595
848
  now,
596
849
  expiredAgo: expiresAt ? now - expiresAt : "n/a",
597
- accessTokenPrefix: token.accessToken?.substring(0, 20),
598
850
  path: pathname
599
851
  });
600
852
  }
601
- if (needsRefresh && token.refreshToken && refreshConfig.clientId && refreshConfig.clientSecret) {
602
- const userId = token.sub;
603
- let refreshPromise = pendingRefreshes.get(userId);
853
+ if (needsRefresh && token.refreshToken && refreshConfig.clientId && (refreshConfig.clientSecret || options.clientAssertionKey)) {
854
+ const grantKey = JSON.stringify([refreshConfig.issuer, refreshConfig.clientId, refreshConfig.clientSecret, token.sub, token.refreshToken, options.dpop?.thumbprint, options.clientAssertionKey?.thumbprint, options.clientAssertionKey?.kid]);
855
+ let refreshPromise = pendingRefreshes.get(grantKey);
604
856
  if (refreshPromise) {
605
857
  console.log("[OAuth42 Middleware] Waiting for in-flight refresh...");
606
858
  } else {
@@ -608,15 +860,17 @@ function withOAuth42Auth(options = {}) {
608
860
  refreshPromise = refreshTokens(
609
861
  token.refreshToken,
610
862
  refreshConfig.clientId,
611
- refreshConfig.clientSecret,
612
- refreshConfig.issuer
863
+ refreshConfig.clientSecret || "",
864
+ refreshConfig.issuer,
865
+ options.dpop,
866
+ options.clientAssertionKey
613
867
  ).finally(() => {
614
- const cleanupTimer = setTimeout(() => pendingRefreshes.delete(userId), 3e4);
868
+ const cleanupTimer = setTimeout(() => pendingRefreshes.delete(grantKey), 3e4);
615
869
  if (typeof cleanupTimer === "object" && "unref" in cleanupTimer) {
616
870
  cleanupTimer.unref();
617
871
  }
618
872
  });
619
- pendingRefreshes.set(userId, refreshPromise);
873
+ pendingRefreshes.set(grantKey, refreshPromise);
620
874
  }
621
875
  const refreshed = await refreshPromise;
622
876
  if (refreshed.success && refreshed.accessToken && refreshed.refreshToken) {
@@ -626,12 +880,13 @@ function withOAuth42Auth(options = {}) {
626
880
  refreshToken: refreshed.refreshToken,
627
881
  expiresAt: refreshed.expiresAt
628
882
  };
883
+ if (options.clientAssertionKey) delete updatedToken.clientSecret;
884
+ if (!await verifyAndAuthorize(updatedToken)) return denied();
629
885
  const newJwt = await encode({
630
886
  token: updatedToken,
631
887
  secret
632
888
  });
633
- const requestHeaders = new Headers(req.headers);
634
- requestHeaders.set("x-oauth42-refreshed-token", refreshed.accessToken);
889
+ const requestHeaders = forwardedHeaders(req, cookieName, newJwt);
635
890
  const response = NextResponse.next({
636
891
  request: {
637
892
  headers: requestHeaders
@@ -684,28 +939,11 @@ function withOAuth42Auth(options = {}) {
684
939
  chunked: newJwt.length > CHUNK_SIZE,
685
940
  existingBaseCooke: req.cookies.has(cookieName),
686
941
  existingChunks: existingChunks.length > 0 ? existingChunks : "none",
687
- newAccessTokenPrefix: refreshed.accessToken?.substring(0, 20),
688
942
  newExpiresAt: refreshed.expiresAt,
689
943
  cookieName
690
944
  });
691
945
  return response;
692
946
  } else {
693
- if (shouldPreserveSessionOnRefreshFailure({
694
- error: refreshed.error,
695
- expiresAt,
696
- now
697
- })) {
698
- console.warn(
699
- "[OAuth42 Middleware] Refresh failed with invalid_grant for a recently expired token; preserving session cookie for likely parallel refresh race",
700
- {
701
- expiresAt,
702
- now,
703
- expiredAgo: now - expiresAt,
704
- path: pathname
705
- }
706
- );
707
- return NextResponse.next();
708
- }
709
947
  console.log("[OAuth42 Middleware] Refresh failed, clearing session");
710
948
  const signInUrl = options.pages?.signIn || "/auth/signin";
711
949
  const url = new URL(signInUrl, req.url);
@@ -723,16 +961,13 @@ function withOAuth42Auth(options = {}) {
723
961
  return response;
724
962
  }
725
963
  }
726
- if (options.callbacks?.authorized) {
727
- const isAuthorized = await options.callbacks.authorized({ token, req });
728
- if (!isAuthorized) {
729
- const signInUrl = options.pages?.signIn || "/auth/signin";
730
- const url = new URL(signInUrl, req.url);
731
- url.searchParams.set("callbackUrl", pathname);
732
- return NextResponse.redirect(url);
733
- }
964
+ if (!Number.isFinite(expiresAt) || now >= expiresAt || !token.accessToken) {
965
+ const url = new URL(options.pages?.signIn || "/auth/signin", req.url);
966
+ url.searchParams.set("callbackUrl", pathname);
967
+ return NextResponse.redirect(url);
734
968
  }
735
- return NextResponse.next();
969
+ if (!await verifyAndAuthorize(token)) return denied();
970
+ return NextResponse.next({ request: { headers: forwardedHeaders(req) } });
736
971
  };
737
972
  }
738
973
  function createMiddlewareConfig(protectedPaths = ["/protected"], publicPaths = ["/auth", "/api/auth"]) {
@@ -751,15 +986,87 @@ function createMiddlewareConfig(protectedPaths = ["/protected"], publicPaths = [
751
986
  publicPaths
752
987
  };
753
988
  }
989
+
990
+ // src/server/dpop.ts
991
+ import { createHash as createHash2, createPublicKey as createPublicKey2, generateKeyPairSync as generateKeyPairSync2, randomBytes as randomBytes2, sign as sign2 } from "crypto";
992
+ var OAuth42DPoP = class _OAuth42DPoP {
993
+ constructor(key) {
994
+ this.nonces = /* @__PURE__ */ new Map();
995
+ if (key.type !== "private" || key.asymmetricKeyType !== "ec" || key.asymmetricKeyDetails?.namedCurve !== "prime256v1") {
996
+ throw new Error("DPoP requires a private P-256 key");
997
+ }
998
+ this.key = key;
999
+ const publicKey = createPublicKey2(key).export({ format: "jwk" });
1000
+ this.jwk = { crv: "P-256", kty: "EC", x: publicKey.x, y: publicKey.y };
1001
+ this.thumbprint = createHash2("sha256").update(JSON.stringify(this.jwk)).digest("base64url");
1002
+ }
1003
+ /** Persist keys in protected server storage; regeneration cannot recover a public refresh grant. */
1004
+ static generate() {
1005
+ return new _OAuth42DPoP(generateKeyPairSync2("ec", { namedCurve: "prime256v1" }).privateKey);
1006
+ }
1007
+ /** Passed directly to openid-client so its callback keeps OIDC issuer/nonce/signature validation. */
1008
+ get oidcKey() {
1009
+ return this.key;
1010
+ }
1011
+ proof(method, target, nonce, accessToken) {
1012
+ const url = new URL(target);
1013
+ if (!["http:", "https:"].includes(url.protocol) || url.protocol !== "https:" && !["localhost", "127.0.0.1", "[::1]"].includes(url.hostname)) {
1014
+ throw new Error("DPoP requires HTTPS outside loopback development");
1015
+ }
1016
+ if (url.username || url.password) throw new Error("DPoP endpoint must not contain credentials");
1017
+ url.search = "";
1018
+ url.hash = "";
1019
+ const header = { typ: "dpop+jwt", alg: "ES256", jwk: this.jwk };
1020
+ const claims = {
1021
+ htm: method.toUpperCase(),
1022
+ htu: url.href,
1023
+ iat: Math.floor(Date.now() / 1e3),
1024
+ jti: randomBytes2(32).toString("base64url"),
1025
+ ...nonce ? { nonce } : {},
1026
+ ...accessToken !== void 0 ? { ath: createHash2("sha256").update(accessToken).digest("base64url") } : {}
1027
+ };
1028
+ const input = `${Buffer.from(JSON.stringify(header)).toString("base64url")}.${Buffer.from(JSON.stringify(claims)).toString("base64url")}`;
1029
+ return `${input}.${sign2("sha256", Buffer.from(input), { key: this.key, dsaEncoding: "ieee-p1363" }).toString("base64url")}`;
1030
+ }
1031
+ assertTokenResponse(tokens) {
1032
+ if (typeof tokens.token_type !== "string" || tokens.token_type.toLowerCase() !== "dpop") {
1033
+ throw new Error("Server did not issue the requested DPoP-bound token");
1034
+ }
1035
+ }
1036
+ async fetch(target, init = {}, accessToken, prepare) {
1037
+ if (init.body instanceof ReadableStream) throw new Error("DPoP nonce retries require a replayable request body");
1038
+ const cacheKey = `${new URL(target).origin}:${accessToken !== void 0 ? "resource" : "token"}`;
1039
+ for (let attempt = 0; attempt < 2; attempt++) {
1040
+ const request = prepare ? prepare() : init;
1041
+ const headers = new Headers(request.headers);
1042
+ headers.set("DPoP", this.proof(request.method || "GET", target, this.nonces.get(cacheKey), accessToken));
1043
+ if (accessToken !== void 0) headers.set("Authorization", `DPoP ${accessToken}`);
1044
+ const response = await fetch(target, { ...request, headers, redirect: "error", cache: "no-store", signal: request.signal || AbortSignal.timeout(3e4) });
1045
+ const nonce = response.headers.get("DPoP-Nonce");
1046
+ if (nonce && /^[\x21-\x7e]{1,128}$/.test(nonce)) {
1047
+ if (this.nonces.size >= 256) this.nonces.clear();
1048
+ this.nonces.set(cacheKey, nonce);
1049
+ } else return response;
1050
+ if (attempt || ![400, 401].includes(response.status)) return response;
1051
+ const error = await response.clone().json().catch(() => null);
1052
+ if (error?.error !== "use_dpop_nonce") return response;
1053
+ }
1054
+ throw new Error("DPoP nonce negotiation failed");
1055
+ }
1056
+ };
754
1057
  export {
1058
+ OAuth42ClientAssertion,
1059
+ OAuth42DPoP,
755
1060
  OAuth42Provider,
756
1061
  createAuth,
757
1062
  createMiddlewareConfig,
758
1063
  getOAuth42Session,
759
1064
  getServerSession,
1065
+ pushAuthorizationURL,
760
1066
  refreshAccessToken,
761
1067
  withOAuth42Auth,
762
1068
  withOAuth42ServerSideProps,
763
- withOAuth42Session
1069
+ withOAuth42Session,
1070
+ withPushedAuthorization
764
1071
  };
765
1072
  //# sourceMappingURL=index.mjs.map