@oauth42/next 0.4.8 → 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/README.md +164 -2
- package/dist/build-manifest.json +59 -0
- package/dist/client/index.d.mts +15 -6
- package/dist/client/index.d.ts +15 -6
- package/dist/client/index.js +23 -2
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +23 -2
- package/dist/client/index.mjs.map +1 -1
- package/dist/index-BuZTiUds.d.mts +144 -0
- package/dist/index-BuZTiUds.d.ts +144 -0
- package/dist/index.d.mts +20 -2
- package/dist/index.d.ts +20 -2
- package/dist/index.js +617 -104
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +613 -103
- package/dist/index.mjs.map +1 -1
- package/dist/middleware/index.d.mts +3 -39
- package/dist/middleware/index.d.ts +3 -39
- package/dist/middleware/index.js +283 -34
- package/dist/middleware/index.js.map +1 -1
- package/dist/middleware/index.mjs +284 -34
- package/dist/middleware/index.mjs.map +1 -1
- package/dist/{auth-BThRQMzh.d.ts → par-Cyu6M1e6.d.mts} +36 -11
- package/dist/{auth-BThRQMzh.d.mts → par-DQNt8j-s.d.ts} +36 -11
- package/dist/server/index.d.mts +7 -3
- package/dist/server/index.d.ts +7 -3
- package/dist/server/index.js +657 -123
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +650 -122
- package/dist/server/index.mjs.map +1 -1
- package/package.json +14 -5
- package/src/types/index.ts +2 -0
- package/src/types/next-auth.d.ts +2 -0
package/dist/server/index.js
CHANGED
|
@@ -5,6 +5,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __typeError = (msg) => {
|
|
9
|
+
throw TypeError(msg);
|
|
10
|
+
};
|
|
8
11
|
var __export = (target, all) => {
|
|
9
12
|
for (var name in all)
|
|
10
13
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -26,25 +29,215 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
26
29
|
mod
|
|
27
30
|
));
|
|
28
31
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
32
|
+
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
33
|
+
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
34
|
+
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);
|
|
35
|
+
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
29
36
|
|
|
30
37
|
// src/server/index.ts
|
|
31
38
|
var server_exports = {};
|
|
32
39
|
__export(server_exports, {
|
|
33
40
|
NextAuth: () => import_next_auth3.default,
|
|
41
|
+
OAuth42ClientAssertion: () => OAuth42ClientAssertion,
|
|
42
|
+
OAuth42DPoP: () => OAuth42DPoP,
|
|
34
43
|
OAuth42Provider: () => OAuth42Provider,
|
|
35
44
|
createAuth: () => createAuth,
|
|
45
|
+
createBackendSession: () => createBackendSession,
|
|
36
46
|
createHandlers: () => createHandlers,
|
|
37
47
|
createHostedAuthCallback: () => createHostedAuthCallback,
|
|
38
48
|
createMiddlewareConfig: () => createMiddlewareConfig,
|
|
39
49
|
getOAuth42Session: () => getOAuth42Session,
|
|
40
50
|
getServerSession: () => getServerSession,
|
|
51
|
+
pushAuthorizationURL: () => pushAuthorizationURL,
|
|
41
52
|
refreshAccessToken: () => refreshAccessToken,
|
|
53
|
+
resolveBackendSession: () => resolveBackendSession,
|
|
54
|
+
revokeBackendSession: () => revokeBackendSession,
|
|
42
55
|
withOAuth42Auth: () => withOAuth42Auth,
|
|
43
56
|
withOAuth42ServerSideProps: () => withOAuth42ServerSideProps,
|
|
44
|
-
withOAuth42Session: () => withOAuth42Session
|
|
57
|
+
withOAuth42Session: () => withOAuth42Session,
|
|
58
|
+
withPushedAuthorization: () => withPushedAuthorization
|
|
45
59
|
});
|
|
46
60
|
module.exports = __toCommonJS(server_exports);
|
|
47
61
|
|
|
62
|
+
// src/server/client-assertion.ts
|
|
63
|
+
var import_node_crypto = require("crypto");
|
|
64
|
+
var _key, _public;
|
|
65
|
+
var _OAuth42ClientAssertion = class _OAuth42ClientAssertion {
|
|
66
|
+
constructor(kid, key) {
|
|
67
|
+
__privateAdd(this, _key);
|
|
68
|
+
__privateAdd(this, _public);
|
|
69
|
+
if (typeof kid !== "string" || !/^[\x21-\x7e]{1,128}$/.test(kid)) throw new Error("Invalid client signing key ID");
|
|
70
|
+
if (key.type !== "private" || key.asymmetricKeyType !== "ec" || key.asymmetricKeyDetails?.namedCurve !== "prime256v1") {
|
|
71
|
+
throw new Error("Client assertions require a private P-256 key");
|
|
72
|
+
}
|
|
73
|
+
__privateSet(this, _key, key);
|
|
74
|
+
this.kid = kid;
|
|
75
|
+
const publicKey = (0, import_node_crypto.createPublicKey)(key).export({ format: "jwk" });
|
|
76
|
+
__privateSet(this, _public, { crv: "P-256", kty: "EC", x: publicKey.x, y: publicKey.y });
|
|
77
|
+
this.thumbprint = (0, import_node_crypto.createHash)("sha256").update(JSON.stringify(__privateGet(this, _public))).digest("base64url");
|
|
78
|
+
}
|
|
79
|
+
static generate(kid) {
|
|
80
|
+
return new _OAuth42ClientAssertion(kid, (0, import_node_crypto.generateKeyPairSync)("ec", { namedCurve: "prime256v1" }).privateKey);
|
|
81
|
+
}
|
|
82
|
+
get publicJwk() {
|
|
83
|
+
return { ...__privateGet(this, _public), kid: this.kid };
|
|
84
|
+
}
|
|
85
|
+
/** Private keys for the server-only NextAuth/openid-client configuration, never registration. */
|
|
86
|
+
get oidcJwks() {
|
|
87
|
+
return { keys: [{
|
|
88
|
+
kty: "EC",
|
|
89
|
+
crv: "P-256",
|
|
90
|
+
x: __privateGet(this, _public).x,
|
|
91
|
+
y: __privateGet(this, _public).y,
|
|
92
|
+
d: __privateGet(this, _key).export({ format: "jwk" }).d,
|
|
93
|
+
kid: this.kid
|
|
94
|
+
}] };
|
|
95
|
+
}
|
|
96
|
+
static validateEndpoint(target) {
|
|
97
|
+
const url = new URL(target);
|
|
98
|
+
if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || url.hash || url.protocol !== "https:" && !["localhost", "127.0.0.1", "[::1]"].includes(url.hostname)) {
|
|
99
|
+
throw new Error("Client authentication requires HTTPS outside loopback development");
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
assertion(clientId, audience) {
|
|
103
|
+
_OAuth42ClientAssertion.validateEndpoint(audience);
|
|
104
|
+
if (!clientId || Buffer.byteLength(clientId) > 256) throw new Error("Invalid client identity");
|
|
105
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
106
|
+
const header = { alg: "ES256", typ: "JWT", kid: this.kid };
|
|
107
|
+
const payload = { iss: clientId, sub: clientId, aud: audience, iat: now, exp: now + 60, jti: (0, import_node_crypto.randomBytes)(32).toString("base64url") };
|
|
108
|
+
const data = `${Buffer.from(JSON.stringify(header)).toString("base64url")}.${Buffer.from(JSON.stringify(payload)).toString("base64url")}`;
|
|
109
|
+
return `${data}.${(0, import_node_crypto.sign)("sha256", Buffer.from(data), { key: __privateGet(this, _key), dsaEncoding: "ieee-p1363" }).toString("base64url")}`;
|
|
110
|
+
}
|
|
111
|
+
async fetch(clientId, target, init, dpop) {
|
|
112
|
+
if (init.method?.toUpperCase() !== "POST" || !(typeof init.body === "string" || init.body instanceof URLSearchParams)) {
|
|
113
|
+
throw new Error("Client assertions require a form POST");
|
|
114
|
+
}
|
|
115
|
+
const prepare = () => {
|
|
116
|
+
const body = new URLSearchParams(init.body);
|
|
117
|
+
body.delete("client_secret");
|
|
118
|
+
body.set("client_id", clientId);
|
|
119
|
+
body.set("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer");
|
|
120
|
+
body.set("client_assertion", this.assertion(clientId, target));
|
|
121
|
+
const headers = new Headers(init.headers);
|
|
122
|
+
headers.delete("Authorization");
|
|
123
|
+
headers.delete("Content-Length");
|
|
124
|
+
headers.delete("Transfer-Encoding");
|
|
125
|
+
headers.set("Content-Type", "application/x-www-form-urlencoded");
|
|
126
|
+
return { ...init, body, headers, redirect: "error", cache: "no-store", signal: init.signal || AbortSignal.timeout(3e4) };
|
|
127
|
+
};
|
|
128
|
+
return dpop ? dpop.fetch(target, init, void 0, prepare) : fetch(target, prepare());
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
_key = new WeakMap();
|
|
132
|
+
_public = new WeakMap();
|
|
133
|
+
var OAuth42ClientAssertion = _OAuth42ClientAssertion;
|
|
134
|
+
|
|
135
|
+
// src/server/par.ts
|
|
136
|
+
async function pushAuthorizationURL(value, config) {
|
|
137
|
+
const base = config.issuer.replace(/\/$/, "");
|
|
138
|
+
let url;
|
|
139
|
+
try {
|
|
140
|
+
url = new URL(value);
|
|
141
|
+
} catch {
|
|
142
|
+
return value;
|
|
143
|
+
}
|
|
144
|
+
if (`${url.origin}${url.pathname}` !== `${base}/oauth2/authorize`) return value;
|
|
145
|
+
if (url.username || url.password || url.hash) throw new Error("Invalid authorization endpoint");
|
|
146
|
+
const body = new URLSearchParams(url.search);
|
|
147
|
+
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))) {
|
|
148
|
+
throw new Error("Invalid PAR authorization transaction");
|
|
149
|
+
}
|
|
150
|
+
const target = `${base}/oauth2/par`;
|
|
151
|
+
OAuth42ClientAssertion.validateEndpoint(target);
|
|
152
|
+
if (!config.clientAssertionKey) body.set("client_secret", config.clientSecret || "");
|
|
153
|
+
const init = {
|
|
154
|
+
method: "POST",
|
|
155
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
156
|
+
body,
|
|
157
|
+
redirect: "error",
|
|
158
|
+
cache: "no-store",
|
|
159
|
+
signal: AbortSignal.timeout(3e4)
|
|
160
|
+
};
|
|
161
|
+
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);
|
|
162
|
+
if (response.status !== 201) throw new Error("Pushed authorization failed");
|
|
163
|
+
const pushed = await response.json();
|
|
164
|
+
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");
|
|
165
|
+
url.search = new URLSearchParams({ client_id: config.clientId, request_uri: pushed.request_uri }).toString();
|
|
166
|
+
return url.href;
|
|
167
|
+
}
|
|
168
|
+
function withPushedAuthorization(handler, config) {
|
|
169
|
+
return async (request, context) => {
|
|
170
|
+
if (!(request instanceof Request)) throw new Error("PAR handlers require Next.js App Router Request/Response");
|
|
171
|
+
const response = await handler(request, context);
|
|
172
|
+
const location = response.headers.get("Location");
|
|
173
|
+
if (location) {
|
|
174
|
+
const next = await pushAuthorizationURL(location, config);
|
|
175
|
+
if (next === location) return response;
|
|
176
|
+
const headers = new Headers(response.headers);
|
|
177
|
+
headers.set("Location", next);
|
|
178
|
+
headers.delete("content-length");
|
|
179
|
+
return new Response(response.body, { status: response.status, headers });
|
|
180
|
+
}
|
|
181
|
+
if (response.headers.get("content-type")?.includes("application/json")) {
|
|
182
|
+
const body = await response.clone().json();
|
|
183
|
+
if (typeof body?.url === "string") {
|
|
184
|
+
const next = await pushAuthorizationURL(body.url, config);
|
|
185
|
+
if (next !== body.url) {
|
|
186
|
+
const headers = new Headers(response.headers);
|
|
187
|
+
headers.delete("content-length");
|
|
188
|
+
return new Response(JSON.stringify({ ...body, url: next }), { status: response.status, headers });
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return response;
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// src/server/verified-userinfo.ts
|
|
197
|
+
async function verifiedUserInfo(issuer, accessToken, subject, dpop) {
|
|
198
|
+
if (typeof accessToken !== "string" || !accessToken || typeof subject !== "string" || !subject) return null;
|
|
199
|
+
try {
|
|
200
|
+
const target = `${issuer.replace(/\/$/, "")}/oauth2/userinfo`;
|
|
201
|
+
const fetcher = dpop ? (url, init) => dpop.fetch(url, init, accessToken) : fetch;
|
|
202
|
+
const response = await fetcher(target, {
|
|
203
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
204
|
+
redirect: "error",
|
|
205
|
+
cache: "no-store",
|
|
206
|
+
signal: AbortSignal.timeout(3e4)
|
|
207
|
+
});
|
|
208
|
+
if (!response.ok) return null;
|
|
209
|
+
const user = await response.json();
|
|
210
|
+
return parseVerifiedUserInfo(user, subject);
|
|
211
|
+
} catch {
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
function parseVerifiedUserInfo(value, subject) {
|
|
216
|
+
if (typeof subject !== "string" || !subject || !value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
217
|
+
const user = value;
|
|
218
|
+
if (user.sub !== subject) return null;
|
|
219
|
+
if (user.groups !== void 0 && (!Array.isArray(user.groups) || user.groups.some((group) => typeof group !== "string"))) return null;
|
|
220
|
+
if (user.email !== void 0 && typeof user.email !== "string") return null;
|
|
221
|
+
if (user.email_verified !== void 0 && typeof user.email_verified !== "boolean") return null;
|
|
222
|
+
const profile = { sub: user.sub };
|
|
223
|
+
for (const field of ["email", "name", "preferred_username", "username", "given_name", "family_name", "company_id", "company_name"]) {
|
|
224
|
+
if (user[field] != null) {
|
|
225
|
+
if (typeof user[field] !== "string") return null;
|
|
226
|
+
profile[field] = user[field];
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
if (user.email_verified !== void 0) profile.email_verified = user.email_verified;
|
|
230
|
+
if (user.groups !== void 0) profile.groups = user.groups;
|
|
231
|
+
return profile;
|
|
232
|
+
}
|
|
233
|
+
async function verifiedBackendSessionUserInfo(issuer, session, subject, dpop) {
|
|
234
|
+
if (typeof session.access_token !== "string" || !session.access_token) return null;
|
|
235
|
+
if (Object.prototype.hasOwnProperty.call(session, "user_info")) {
|
|
236
|
+
return parseVerifiedUserInfo(session.user_info, subject);
|
|
237
|
+
}
|
|
238
|
+
return verifiedUserInfo(issuer, session.access_token, subject, dpop);
|
|
239
|
+
}
|
|
240
|
+
|
|
48
241
|
// src/server/auth.ts
|
|
49
242
|
var import_next_auth2 = __toESM(require("next-auth"));
|
|
50
243
|
|
|
@@ -52,13 +245,20 @@ var import_next_auth2 = __toESM(require("next-auth"));
|
|
|
52
245
|
function OAuth42Provider(options) {
|
|
53
246
|
const issuer = options.issuer || process.env.OAUTH42_ISSUER || "https://api.oauth42.com";
|
|
54
247
|
const baseUrl = issuer.replace(/\/$/, "");
|
|
248
|
+
const { dpop, clientAssertionKey, ...providerOptions } = options;
|
|
249
|
+
if (clientAssertionKey) {
|
|
250
|
+
OAuth42ClientAssertion.validateEndpoint(`${baseUrl}/oauth2/token`);
|
|
251
|
+
providerOptions.clientSecret = void 0;
|
|
252
|
+
}
|
|
253
|
+
if (options.pkceEnabled === false) throw new Error("OAuth42 requires S256 PKCE");
|
|
55
254
|
return {
|
|
56
255
|
id: "oauth42",
|
|
57
256
|
name: "OAuth42",
|
|
58
257
|
type: "oauth",
|
|
59
258
|
version: "2.0",
|
|
60
|
-
//
|
|
61
|
-
|
|
259
|
+
// Pin O42 metadata: NextAuth v4 discovery does not compare the returned issuer.
|
|
260
|
+
// Constructing its OIDC client from these trusted endpoints preserves issuer binding.
|
|
261
|
+
jwks_endpoint: `${baseUrl}/.well-known/jwks.json`,
|
|
62
262
|
// Use ID token for user profile (standard OIDC pattern)
|
|
63
263
|
// This avoids an extra HTTP call to /userinfo on every auth flow
|
|
64
264
|
// Apps can call /userinfo separately when they need extended claims (groups, company_id, etc.)
|
|
@@ -71,17 +271,26 @@ function OAuth42Provider(options) {
|
|
|
71
271
|
response_type: "code"
|
|
72
272
|
}
|
|
73
273
|
},
|
|
74
|
-
token:
|
|
274
|
+
token: dpop ? {
|
|
275
|
+
url: `${baseUrl}/oauth2/token`,
|
|
276
|
+
async request({ client, provider, params, checks }) {
|
|
277
|
+
const tokens = await client.callback(provider.callbackUrl, params, checks, { DPoP: dpop.oidcKey });
|
|
278
|
+
dpop.assertTokenResponse(tokens);
|
|
279
|
+
return { tokens };
|
|
280
|
+
}
|
|
281
|
+
} : `${baseUrl}/oauth2/token`,
|
|
75
282
|
userinfo: `${baseUrl}/oauth2/userinfo`,
|
|
283
|
+
...clientAssertionKey ? { jwks: clientAssertionKey.oidcJwks } : {},
|
|
76
284
|
client: {
|
|
77
285
|
id: options.clientId,
|
|
78
|
-
secret: options.clientSecret,
|
|
79
|
-
token_endpoint_auth_method: "client_secret_post",
|
|
286
|
+
secret: clientAssertionKey ? void 0 : options.clientSecret,
|
|
287
|
+
token_endpoint_auth_method: clientAssertionKey ? "private_key_jwt" : "client_secret_post",
|
|
288
|
+
...clientAssertionKey ? { token_endpoint_auth_signing_alg: "ES256" } : {},
|
|
80
289
|
id_token_signed_response_alg: "RS256"
|
|
81
290
|
// OAuth42 uses RS256 for ID tokens
|
|
82
291
|
},
|
|
83
|
-
issuer
|
|
84
|
-
checks:
|
|
292
|
+
issuer,
|
|
293
|
+
checks: ["pkce", "state", "nonce"],
|
|
85
294
|
profile(profile, tokens) {
|
|
86
295
|
return {
|
|
87
296
|
id: profile.sub || profile.id || profile.email,
|
|
@@ -97,42 +306,18 @@ function OAuth42Provider(options) {
|
|
|
97
306
|
bg: "#1e40af",
|
|
98
307
|
text: "#ffffff"
|
|
99
308
|
},
|
|
100
|
-
|
|
309
|
+
// NextAuth v4 types require a string even when private_key_jwt sends no secret.
|
|
310
|
+
options: { ...providerOptions, clientSecret: providerOptions.clientSecret || "" }
|
|
101
311
|
};
|
|
102
312
|
}
|
|
103
313
|
|
|
104
314
|
// src/server/session.ts
|
|
105
315
|
var import_next_auth = require("next-auth");
|
|
106
|
-
var import_headers = require("next/headers");
|
|
107
|
-
var REFRESHED_TOKEN_HEADER = "x-oauth42-refreshed-token";
|
|
108
316
|
async function getOAuth42Session(...args) {
|
|
109
317
|
const session = await (0, import_next_auth.getServerSession)(...args);
|
|
110
|
-
if (!session) {
|
|
318
|
+
if (!session || session.error) {
|
|
111
319
|
return null;
|
|
112
320
|
}
|
|
113
|
-
let refreshedToken = null;
|
|
114
|
-
try {
|
|
115
|
-
if (args.length === 1) {
|
|
116
|
-
const headersList = await (0, import_headers.headers)();
|
|
117
|
-
refreshedToken = headersList.get(REFRESHED_TOKEN_HEADER);
|
|
118
|
-
} else if (args.length === 3) {
|
|
119
|
-
const req = args[0];
|
|
120
|
-
refreshedToken = req.headers[REFRESHED_TOKEN_HEADER] || null;
|
|
121
|
-
}
|
|
122
|
-
} catch {
|
|
123
|
-
}
|
|
124
|
-
if (refreshedToken) {
|
|
125
|
-
const sessionToken = session.accessToken;
|
|
126
|
-
console.log("[OAuth42 Session] Using refreshed token from middleware:", {
|
|
127
|
-
refreshedPrefix: refreshedToken.substring(0, 20),
|
|
128
|
-
sessionPrefix: sessionToken?.substring(0, 20),
|
|
129
|
-
tokensMatch: refreshedToken === sessionToken
|
|
130
|
-
});
|
|
131
|
-
return {
|
|
132
|
-
...session,
|
|
133
|
-
accessToken: refreshedToken
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
321
|
return session;
|
|
137
322
|
}
|
|
138
323
|
function withOAuth42Session(handler, authOptions) {
|
|
@@ -163,13 +348,66 @@ function withOAuth42ServerSideProps(getServerSideProps, authOptions) {
|
|
|
163
348
|
};
|
|
164
349
|
}
|
|
165
350
|
|
|
351
|
+
// src/server/backend-session.ts
|
|
352
|
+
function isBackendSessionMode(mode) {
|
|
353
|
+
return mode === "backend";
|
|
354
|
+
}
|
|
355
|
+
function sessionEndpoint(issuer) {
|
|
356
|
+
return `${issuer.replace(/\/$/, "")}/api/sdk/sessions`;
|
|
357
|
+
}
|
|
358
|
+
async function sessionFetch(params, target, body, credential, method = "POST") {
|
|
359
|
+
OAuth42ClientAssertion.validateEndpoint(target);
|
|
360
|
+
const prepare = () => ({
|
|
361
|
+
method,
|
|
362
|
+
redirect: "error",
|
|
363
|
+
cache: "no-store",
|
|
364
|
+
signal: AbortSignal.timeout(3e4),
|
|
365
|
+
headers: { "Content-Type": "application/json" },
|
|
366
|
+
body: JSON.stringify({
|
|
367
|
+
...body,
|
|
368
|
+
client_id: params.clientId,
|
|
369
|
+
...params.clientAssertionKey ? {
|
|
370
|
+
client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
|
|
371
|
+
client_assertion: params.clientAssertionKey.assertion(params.clientId, target)
|
|
372
|
+
} : { client_secret: params.clientSecret }
|
|
373
|
+
})
|
|
374
|
+
});
|
|
375
|
+
return params.dpop ? params.dpop.fetch(target, {}, credential, prepare) : fetch(target, prepare());
|
|
376
|
+
}
|
|
377
|
+
async function sessionResponse(response, params) {
|
|
378
|
+
if (!response.ok) throw new Error(`OAuth42 backend session request failed: ${response.status}`);
|
|
379
|
+
const session = await response.json();
|
|
380
|
+
if (params.dpop) params.dpop.assertTokenResponse(session);
|
|
381
|
+
else if (session.token_type?.toLowerCase() === "dpop") throw new Error("Backend session requires its retained proof key");
|
|
382
|
+
return session;
|
|
383
|
+
}
|
|
384
|
+
async function createBackendSession(params) {
|
|
385
|
+
return sessionResponse(await sessionFetch(params, sessionEndpoint(params.issuer), {
|
|
386
|
+
access_token: params.accessToken,
|
|
387
|
+
refresh_token: params.refreshToken,
|
|
388
|
+
id_token: params.idToken,
|
|
389
|
+
user: params.user
|
|
390
|
+
}, params.accessToken), params);
|
|
391
|
+
}
|
|
392
|
+
async function resolveBackendSession(params) {
|
|
393
|
+
const response = await sessionFetch(params, `${sessionEndpoint(params.issuer)}/resolve`, { session_id: params.sessionId }, params.sessionId);
|
|
394
|
+
if ([401, 403, 404].includes(response.status)) return null;
|
|
395
|
+
return sessionResponse(response, params);
|
|
396
|
+
}
|
|
397
|
+
async function revokeBackendSession(params) {
|
|
398
|
+
const response = await sessionFetch(params, sessionEndpoint(params.issuer), { session_id: params.sessionId }, params.sessionId, "DELETE");
|
|
399
|
+
if (!response.ok) throw new Error(`OAuth42 backend session revoke failed: ${response.status}`);
|
|
400
|
+
}
|
|
401
|
+
|
|
166
402
|
// src/server/auth.ts
|
|
167
403
|
var NextAuth = import_next_auth2.default.default || import_next_auth2.default;
|
|
168
|
-
var
|
|
404
|
+
var activeRefreshes = /* @__PURE__ */ new Map();
|
|
169
405
|
function createAuth(options = {}) {
|
|
170
406
|
const clientId = options.clientId || process.env.OAUTH42_CLIENT_ID;
|
|
171
|
-
const clientSecret = options.clientSecret || process.env.OAUTH42_CLIENT_SECRET;
|
|
172
|
-
|
|
407
|
+
const clientSecret = options.clientAssertionKey ? "" : options.clientSecret || process.env.OAUTH42_CLIENT_SECRET;
|
|
408
|
+
const issuer = options.issuer || process.env.NEXT_PUBLIC_OAUTH_ISSUER || process.env.OAUTH42_ISSUER || "https://localhost:8443";
|
|
409
|
+
const useBackendSessions = isBackendSessionMode(options.sessionStore);
|
|
410
|
+
if (!clientId || !clientSecret && !options.clientAssertionKey) {
|
|
173
411
|
throw new Error(
|
|
174
412
|
"OAuth42 client credentials are required. Set OAUTH42_CLIENT_ID and OAUTH42_CLIENT_SECRET environment variables or pass them in the options."
|
|
175
413
|
);
|
|
@@ -178,24 +416,57 @@ function createAuth(options = {}) {
|
|
|
178
416
|
providers: [
|
|
179
417
|
OAuth42Provider({
|
|
180
418
|
clientId,
|
|
181
|
-
clientSecret,
|
|
182
|
-
issuer
|
|
419
|
+
clientSecret: clientSecret || "",
|
|
420
|
+
issuer,
|
|
183
421
|
scopes: options.scopes,
|
|
184
|
-
pkceEnabled: options.pkceEnabled
|
|
422
|
+
pkceEnabled: options.pkceEnabled,
|
|
423
|
+
dpop: options.dpop,
|
|
424
|
+
clientAssertionKey: options.clientAssertionKey
|
|
185
425
|
})
|
|
186
426
|
],
|
|
187
427
|
callbacks: {
|
|
188
428
|
async jwt({ token, account, profile }) {
|
|
429
|
+
if (options.clientAssertionKey) delete token.clientSecret;
|
|
189
430
|
console.log("[OAuth42 SDK] JWT callback called", { hasAccount: !!account, hasProfile: !!profile });
|
|
190
431
|
if (account) {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
432
|
+
if (useBackendSessions) {
|
|
433
|
+
if (!account.access_token || !account.refresh_token) {
|
|
434
|
+
throw new Error("OAuth42 backend sessions require access and refresh tokens");
|
|
435
|
+
}
|
|
436
|
+
const oauth42Profile = profile;
|
|
437
|
+
const backendSession = await createBackendSession({
|
|
438
|
+
dpop: options.dpop,
|
|
439
|
+
clientAssertionKey: options.clientAssertionKey,
|
|
440
|
+
issuer,
|
|
441
|
+
clientId,
|
|
442
|
+
clientSecret: clientSecret || "",
|
|
443
|
+
accessToken: account.access_token,
|
|
444
|
+
refreshToken: account.refresh_token,
|
|
445
|
+
expiresAt: account.expires_at,
|
|
446
|
+
idToken: account.id_token,
|
|
447
|
+
user: {
|
|
448
|
+
sub: oauth42Profile?.sub,
|
|
449
|
+
email: oauth42Profile?.email,
|
|
450
|
+
username: oauth42Profile?.username,
|
|
451
|
+
name: token.name,
|
|
452
|
+
emailVerified: oauth42Profile?.email_verified,
|
|
453
|
+
groups: oauth42Profile?.groups
|
|
454
|
+
}
|
|
455
|
+
});
|
|
456
|
+
token.oauth42SessionId = backendSession.session_id;
|
|
457
|
+
token.expiresAt = backendSession.expires_at;
|
|
458
|
+
token.issuer = issuer;
|
|
459
|
+
} else {
|
|
460
|
+
console.log("[OAuth42 SDK] Initial sign in - storing tokens in JWT");
|
|
461
|
+
token.accessToken = account.access_token;
|
|
462
|
+
token.refreshToken = account.refresh_token;
|
|
463
|
+
token.expiresAt = account.expires_at;
|
|
464
|
+
token.idToken = account.id_token;
|
|
465
|
+
token.clientId = clientId;
|
|
466
|
+
if (options.clientAssertionKey) delete token.clientSecret;
|
|
467
|
+
else token.clientSecret = clientSecret;
|
|
468
|
+
token.issuer = issuer;
|
|
469
|
+
}
|
|
199
470
|
}
|
|
200
471
|
if (profile) {
|
|
201
472
|
const oauth42Profile = profile;
|
|
@@ -212,12 +483,68 @@ function createAuth(options = {}) {
|
|
|
212
483
|
},
|
|
213
484
|
async session({ session, token }) {
|
|
214
485
|
console.log("[OAuth42 SDK] Session callback called", { hasToken: !!token, hasSession: !!session });
|
|
215
|
-
|
|
216
|
-
|
|
486
|
+
const revokedSession = () => {
|
|
487
|
+
session.error = "SessionRevoked";
|
|
488
|
+
delete session.accessToken;
|
|
489
|
+
delete session.idToken;
|
|
490
|
+
delete session.userInfo;
|
|
491
|
+
delete session.user;
|
|
492
|
+
return session;
|
|
493
|
+
};
|
|
494
|
+
if (token.error) return revokedSession();
|
|
495
|
+
let verified;
|
|
496
|
+
if (useBackendSessions) {
|
|
497
|
+
const sessionId = token.oauth42SessionId;
|
|
498
|
+
if (!sessionId) {
|
|
499
|
+
return revokedSession();
|
|
500
|
+
}
|
|
501
|
+
const backendSession = await resolveBackendSession({
|
|
502
|
+
dpop: options.dpop,
|
|
503
|
+
clientAssertionKey: options.clientAssertionKey,
|
|
504
|
+
issuer,
|
|
505
|
+
clientId,
|
|
506
|
+
clientSecret: clientSecret || "",
|
|
507
|
+
sessionId
|
|
508
|
+
});
|
|
509
|
+
if (!backendSession) {
|
|
510
|
+
return revokedSession();
|
|
511
|
+
}
|
|
512
|
+
session.accessToken = backendSession.access_token;
|
|
513
|
+
session.idToken = backendSession.id_token;
|
|
514
|
+
token.expiresAt = backendSession.expires_at;
|
|
515
|
+
verified = await verifiedBackendSessionUserInfo(issuer, backendSession, token.sub, options.dpop);
|
|
516
|
+
if (backendSession.user && session.user) {
|
|
517
|
+
session.user.email = backendSession.user.email;
|
|
518
|
+
session.user.name = backendSession.user.name;
|
|
519
|
+
session.user.username = backendSession.user.username;
|
|
520
|
+
session.user.emailVerified = backendSession.user.emailVerified;
|
|
521
|
+
session.user.groups = backendSession.user.groups;
|
|
522
|
+
}
|
|
523
|
+
} else {
|
|
524
|
+
session.accessToken = token.accessToken;
|
|
525
|
+
session.idToken = token.idToken;
|
|
526
|
+
verified = await verifiedUserInfo(issuer, session.accessToken, token.sub, options.dpop);
|
|
527
|
+
}
|
|
528
|
+
if (!verified) {
|
|
529
|
+
return revokedSession();
|
|
530
|
+
}
|
|
531
|
+
session.userInfo = verified;
|
|
532
|
+
token.email = verified.email;
|
|
533
|
+
token.name = verified.name;
|
|
534
|
+
token.username = verified.preferred_username ?? verified.username;
|
|
535
|
+
token.emailVerified = verified.email_verified;
|
|
536
|
+
token.groups = verified.groups ?? [];
|
|
537
|
+
if (session.user) {
|
|
538
|
+
session.user.email = verified.email;
|
|
539
|
+
session.user.name = verified.name;
|
|
540
|
+
session.user.username = verified.preferred_username ?? verified.username;
|
|
541
|
+
session.user.emailVerified = verified.email_verified;
|
|
542
|
+
session.user.groups = verified.groups ?? [];
|
|
543
|
+
}
|
|
217
544
|
if (token.error) {
|
|
218
545
|
session.error = token.error;
|
|
219
546
|
}
|
|
220
|
-
if (session.user) {
|
|
547
|
+
if (!useBackendSessions && session.user) {
|
|
221
548
|
session.user.email = token.email;
|
|
222
549
|
session.user.name = token.name;
|
|
223
550
|
session.user.username = token.username;
|
|
@@ -241,6 +568,28 @@ function createAuth(options = {}) {
|
|
|
241
568
|
strategy: "jwt",
|
|
242
569
|
...options.session
|
|
243
570
|
},
|
|
571
|
+
events: {
|
|
572
|
+
async signOut(message) {
|
|
573
|
+
if (useBackendSessions) {
|
|
574
|
+
const sessionId = message.token?.oauth42SessionId;
|
|
575
|
+
if (sessionId) {
|
|
576
|
+
await revokeBackendSession({
|
|
577
|
+
dpop: options.dpop,
|
|
578
|
+
clientAssertionKey: options.clientAssertionKey,
|
|
579
|
+
issuer,
|
|
580
|
+
clientId,
|
|
581
|
+
clientSecret: clientSecret || "",
|
|
582
|
+
sessionId
|
|
583
|
+
}).catch((error) => {
|
|
584
|
+
console.warn("[OAuth42 SDK] Failed to revoke backend session on signOut:", error);
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
if (options.events?.signOut) {
|
|
589
|
+
await options.events.signOut(message);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
},
|
|
244
593
|
debug: options.debug || process.env.NODE_ENV === "development",
|
|
245
594
|
secret: process.env.NEXTAUTH_SECRET,
|
|
246
595
|
// Configure unique cookie names per app to prevent session conflicts on localhost
|
|
@@ -316,7 +665,8 @@ function createAuth(options = {}) {
|
|
|
316
665
|
}
|
|
317
666
|
}
|
|
318
667
|
};
|
|
319
|
-
const
|
|
668
|
+
const nextAuthHandler = NextAuth(authOptions);
|
|
669
|
+
const handler = options.pushedAuthorization ? withPushedAuthorization(nextAuthHandler, { issuer, clientId, clientSecret, dpop: options.dpop, clientAssertionKey: options.clientAssertionKey }) : nextAuthHandler;
|
|
320
670
|
return {
|
|
321
671
|
auth: authOptions,
|
|
322
672
|
handlers: { GET: handler, POST: handler }
|
|
@@ -327,24 +677,40 @@ function createHandlers(authOptions) {
|
|
|
327
677
|
return { GET: handler, POST: handler };
|
|
328
678
|
}
|
|
329
679
|
var getServerSession = getOAuth42Session;
|
|
330
|
-
async function refreshAccessToken(token, clientId, clientSecret, issuer) {
|
|
331
|
-
if (
|
|
332
|
-
|
|
333
|
-
|
|
680
|
+
async function refreshAccessToken(token, clientId, clientSecret, issuer, dpop, clientAssertionKey) {
|
|
681
|
+
if (typeof token.refreshToken !== "string" || !token.refreshToken) {
|
|
682
|
+
return { ...token, error: "RefreshAccessTokenError" };
|
|
683
|
+
}
|
|
684
|
+
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]);
|
|
685
|
+
let refresh = activeRefreshes.get(key);
|
|
686
|
+
if (!refresh) {
|
|
687
|
+
refresh = doRefresh(token, clientId, clientSecret, issuer, dpop, clientAssertionKey);
|
|
688
|
+
activeRefreshes.set(key, refresh);
|
|
334
689
|
}
|
|
335
|
-
activeRefresh = doRefresh(token, clientId, clientSecret, issuer);
|
|
336
690
|
try {
|
|
337
|
-
|
|
691
|
+
const result = await refresh;
|
|
692
|
+
const updated = {
|
|
693
|
+
...token,
|
|
694
|
+
accessToken: result.accessToken,
|
|
695
|
+
refreshToken: result.refreshToken,
|
|
696
|
+
expiresAt: result.expiresAt,
|
|
697
|
+
error: result.error
|
|
698
|
+
};
|
|
699
|
+
if (clientAssertionKey) delete updated.clientSecret;
|
|
700
|
+
return updated;
|
|
338
701
|
} finally {
|
|
339
|
-
|
|
702
|
+
if (activeRefreshes.get(key) === refresh) activeRefreshes.delete(key);
|
|
340
703
|
}
|
|
341
704
|
}
|
|
342
|
-
async function doRefresh(token, clientId, clientSecret, issuer) {
|
|
705
|
+
async function doRefresh(token, clientId, clientSecret, issuer, dpop, clientAssertionKey) {
|
|
343
706
|
try {
|
|
344
707
|
const baseUrl = issuer || process.env.OAUTH42_ISSUER || "https://api.oauth42.com";
|
|
345
708
|
const tokenUrl = `${baseUrl}/oauth2/token`;
|
|
346
709
|
const fetchOptions = {
|
|
347
710
|
method: "POST",
|
|
711
|
+
redirect: "error",
|
|
712
|
+
cache: "no-store",
|
|
713
|
+
signal: AbortSignal.timeout(3e4),
|
|
348
714
|
headers: {
|
|
349
715
|
"Content-Type": "application/x-www-form-urlencoded"
|
|
350
716
|
},
|
|
@@ -355,17 +721,12 @@ async function doRefresh(token, clientId, clientSecret, issuer) {
|
|
|
355
721
|
client_secret: clientSecret
|
|
356
722
|
})
|
|
357
723
|
};
|
|
358
|
-
|
|
359
|
-
const https2 = await import("https");
|
|
360
|
-
fetchOptions.agent = new https2.Agent({
|
|
361
|
-
rejectUnauthorized: false
|
|
362
|
-
});
|
|
363
|
-
}
|
|
364
|
-
const response = await fetch(tokenUrl, fetchOptions);
|
|
724
|
+
const response = clientAssertionKey ? await clientAssertionKey.fetch(clientId, tokenUrl, fetchOptions, dpop) : dpop ? await dpop.fetch(tokenUrl, fetchOptions) : await fetch(tokenUrl, fetchOptions);
|
|
365
725
|
const refreshedTokens = await response.json();
|
|
366
726
|
if (!response.ok) {
|
|
367
727
|
throw refreshedTokens;
|
|
368
728
|
}
|
|
729
|
+
dpop?.assertTokenResponse(refreshedTokens);
|
|
369
730
|
console.log("[OAuth42] Token refreshed successfully");
|
|
370
731
|
return {
|
|
371
732
|
...token,
|
|
@@ -377,7 +738,7 @@ async function doRefresh(token, clientId, clientSecret, issuer) {
|
|
|
377
738
|
error: void 0
|
|
378
739
|
};
|
|
379
740
|
} catch (error) {
|
|
380
|
-
console.error("[OAuth42] Failed to refresh access token
|
|
741
|
+
console.error("[OAuth42] Failed to refresh access token");
|
|
381
742
|
return {
|
|
382
743
|
...token,
|
|
383
744
|
error: "RefreshAccessTokenError"
|
|
@@ -394,12 +755,37 @@ var import_jwt = require("next-auth/jwt");
|
|
|
394
755
|
var ALLOWED_COOKIE_SIZE = 4096;
|
|
395
756
|
var ESTIMATED_EMPTY_COOKIE_SIZE = 163;
|
|
396
757
|
var CHUNK_SIZE = ALLOWED_COOKIE_SIZE - ESTIMATED_EMPTY_COOKIE_SIZE;
|
|
758
|
+
var DEFAULT_SESSION_MAX_AGE_SECONDS = 365 * 24 * 60 * 60;
|
|
397
759
|
var pendingRefreshes = /* @__PURE__ */ new Map();
|
|
398
|
-
|
|
760
|
+
function forwardedHeaders(req, cookieName, jwt) {
|
|
761
|
+
const headers = new Headers(req.headers);
|
|
762
|
+
headers.delete("x-oauth42-refreshed-token");
|
|
763
|
+
if (cookieName && jwt) {
|
|
764
|
+
const cookies = req.cookies.getAll().filter((cookie) => cookie.name !== cookieName && !cookie.name.startsWith(`${cookieName}.`));
|
|
765
|
+
if (jwt.length <= CHUNK_SIZE) cookies.push({ name: cookieName, value: jwt });
|
|
766
|
+
else for (let i = 0; i * CHUNK_SIZE < jwt.length; i++) {
|
|
767
|
+
cookies.push({ name: `${cookieName}.${i}`, value: jwt.slice(i * CHUNK_SIZE, (i + 1) * CHUNK_SIZE) });
|
|
768
|
+
}
|
|
769
|
+
headers.set("cookie", cookies.map((cookie) => `${encodeURIComponent(cookie.name)}=${encodeURIComponent(cookie.value)}`).join("; "));
|
|
770
|
+
}
|
|
771
|
+
return headers;
|
|
772
|
+
}
|
|
773
|
+
function resolveRefreshConfig(options, token) {
|
|
774
|
+
return {
|
|
775
|
+
clientId: options.clientId || token.clientId || process.env.OAUTH42_CLIENT_ID,
|
|
776
|
+
clientSecret: options.clientSecret || token.clientSecret || process.env.OAUTH42_CLIENT_SECRET,
|
|
777
|
+
issuer: options.issuer || token.issuer || process.env.OAUTH42_ISSUER || "https://localhost:8443"
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
async function refreshTokens(refreshToken, clientId, clientSecret, issuer, dpop, clientAssertionKey) {
|
|
399
781
|
try {
|
|
400
782
|
const tokenUrl = `${issuer}/oauth2/token`;
|
|
401
|
-
const
|
|
783
|
+
const fetcher = clientAssertionKey ? (url, init) => clientAssertionKey.fetch(clientId, url, init, dpop) : dpop ? dpop.fetch.bind(dpop) : fetch;
|
|
784
|
+
const response = await fetcher(tokenUrl, {
|
|
402
785
|
method: "POST",
|
|
786
|
+
redirect: "error",
|
|
787
|
+
cache: "no-store",
|
|
788
|
+
signal: AbortSignal.timeout(3e4),
|
|
403
789
|
headers: {
|
|
404
790
|
"Content-Type": "application/x-www-form-urlencoded"
|
|
405
791
|
},
|
|
@@ -412,9 +798,10 @@ async function refreshTokens(refreshToken, clientId, clientSecret, issuer) {
|
|
|
412
798
|
});
|
|
413
799
|
const data = await response.json();
|
|
414
800
|
if (!response.ok) {
|
|
415
|
-
console.error("[OAuth42 Middleware] Token refresh failed:",
|
|
801
|
+
console.error("[OAuth42 Middleware] Token refresh failed:", response.status);
|
|
416
802
|
return { success: false, error: data.error || "refresh_failed" };
|
|
417
803
|
}
|
|
804
|
+
dpop?.assertTokenResponse(data);
|
|
418
805
|
console.log("[OAuth42 Middleware] Token refreshed successfully");
|
|
419
806
|
return {
|
|
420
807
|
success: true,
|
|
@@ -423,17 +810,14 @@ async function refreshTokens(refreshToken, clientId, clientSecret, issuer) {
|
|
|
423
810
|
expiresAt: Math.floor(Date.now() / 1e3) + (data.expires_in || 3600)
|
|
424
811
|
};
|
|
425
812
|
} catch (error) {
|
|
426
|
-
console.error("[OAuth42 Middleware] Token refresh
|
|
813
|
+
console.error("[OAuth42 Middleware] Token refresh request failed");
|
|
427
814
|
return { success: false, error: "refresh_error" };
|
|
428
815
|
}
|
|
429
816
|
}
|
|
430
817
|
function withOAuth42Auth(options = {}) {
|
|
431
818
|
const secret = process.env.NEXTAUTH_SECRET;
|
|
432
|
-
const clientId = process.env.OAUTH42_CLIENT_ID;
|
|
433
|
-
const clientSecret = process.env.OAUTH42_CLIENT_SECRET;
|
|
434
|
-
const issuer = process.env.OAUTH42_ISSUER || "https://localhost:8443";
|
|
435
819
|
if (!secret) {
|
|
436
|
-
|
|
820
|
+
throw new Error("NEXTAUTH_SECRET is required for OAuth42 middleware");
|
|
437
821
|
}
|
|
438
822
|
return async function middleware(req) {
|
|
439
823
|
const cookieName = options.cookiePrefix ? `${options.cookiePrefix}.session-token` : "next-auth.session-token";
|
|
@@ -444,18 +828,76 @@ function withOAuth42Auth(options = {}) {
|
|
|
444
828
|
});
|
|
445
829
|
const pathname = req.nextUrl.pathname;
|
|
446
830
|
if (options.publicPaths?.some((path) => pathname.startsWith(path))) {
|
|
447
|
-
return import_server.NextResponse.next();
|
|
831
|
+
return import_server.NextResponse.next({ request: { headers: forwardedHeaders(req) } });
|
|
448
832
|
}
|
|
449
833
|
const needsProtection = options.protectedPaths ? options.protectedPaths.some((path) => pathname.startsWith(path)) : true;
|
|
450
834
|
if (!needsProtection) {
|
|
451
|
-
return import_server.NextResponse.next();
|
|
835
|
+
return import_server.NextResponse.next({ request: { headers: forwardedHeaders(req) } });
|
|
452
836
|
}
|
|
453
|
-
if (!token) {
|
|
837
|
+
if (!token || token.error) {
|
|
454
838
|
const signInUrl = options.pages?.signIn || "/auth/signin";
|
|
455
839
|
const url = new URL(signInUrl, req.url);
|
|
456
840
|
url.searchParams.set("callbackUrl", pathname);
|
|
457
841
|
return import_server.NextResponse.redirect(url);
|
|
458
842
|
}
|
|
843
|
+
const refreshConfig = resolveRefreshConfig(options, token);
|
|
844
|
+
const denied = () => {
|
|
845
|
+
const url = new URL(options.pages?.signIn || "/auth/signin", req.url);
|
|
846
|
+
url.searchParams.set("callbackUrl", pathname);
|
|
847
|
+
const response = import_server.NextResponse.redirect(url);
|
|
848
|
+
for (const cookie of req.cookies.getAll()) {
|
|
849
|
+
if (cookie.name === cookieName || cookie.name.startsWith(`${cookieName}.`)) response.cookies.delete(cookie.name);
|
|
850
|
+
}
|
|
851
|
+
return response;
|
|
852
|
+
};
|
|
853
|
+
const verifyAndAuthorize = async (current, resolved) => {
|
|
854
|
+
const user = resolved === void 0 ? await verifiedUserInfo(refreshConfig.issuer, current.accessToken, token.sub, options.dpop) : resolved;
|
|
855
|
+
if (!user) return false;
|
|
856
|
+
current.groups = user.groups ?? [];
|
|
857
|
+
current.email = user.email;
|
|
858
|
+
current.name = user.name;
|
|
859
|
+
current.username = user.preferred_username ?? user.username;
|
|
860
|
+
current.emailVerified = user.email_verified;
|
|
861
|
+
return options.callbacks?.authorized ? options.callbacks.authorized({ token: current, req }) : true;
|
|
862
|
+
};
|
|
863
|
+
if (isBackendSessionMode(options.sessionStore)) {
|
|
864
|
+
const sessionId = token.oauth42SessionId;
|
|
865
|
+
if (!sessionId || !refreshConfig.clientId || !refreshConfig.clientSecret && !options.clientAssertionKey) {
|
|
866
|
+
const signInUrl = options.pages?.signIn || "/auth/signin";
|
|
867
|
+
const url = new URL(signInUrl, req.url);
|
|
868
|
+
url.searchParams.set("callbackUrl", pathname);
|
|
869
|
+
const response = import_server.NextResponse.redirect(url);
|
|
870
|
+
response.cookies.delete(cookieName);
|
|
871
|
+
return response;
|
|
872
|
+
}
|
|
873
|
+
const backendSession = await resolveBackendSession({
|
|
874
|
+
dpop: options.dpop,
|
|
875
|
+
clientAssertionKey: options.clientAssertionKey,
|
|
876
|
+
issuer: refreshConfig.issuer,
|
|
877
|
+
clientId: refreshConfig.clientId,
|
|
878
|
+
clientSecret: refreshConfig.clientSecret || "",
|
|
879
|
+
sessionId
|
|
880
|
+
}).catch((error) => {
|
|
881
|
+
console.error("[OAuth42 Middleware] Backend session resolve failed:", error);
|
|
882
|
+
return null;
|
|
883
|
+
});
|
|
884
|
+
if (!backendSession) {
|
|
885
|
+
const signInUrl = options.pages?.signIn || "/auth/signin";
|
|
886
|
+
const url = new URL(signInUrl, req.url);
|
|
887
|
+
url.searchParams.set("callbackUrl", pathname);
|
|
888
|
+
const response = import_server.NextResponse.redirect(url);
|
|
889
|
+
response.cookies.delete(cookieName);
|
|
890
|
+
return response;
|
|
891
|
+
}
|
|
892
|
+
const verified = await verifiedBackendSessionUserInfo(refreshConfig.issuer, backendSession, token.sub, options.dpop);
|
|
893
|
+
if (!await verifyAndAuthorize({ ...token, accessToken: backendSession.access_token }, verified)) return denied();
|
|
894
|
+
const requestHeaders = forwardedHeaders(req);
|
|
895
|
+
return import_server.NextResponse.next({
|
|
896
|
+
request: {
|
|
897
|
+
headers: requestHeaders
|
|
898
|
+
}
|
|
899
|
+
});
|
|
900
|
+
}
|
|
459
901
|
const expiresAt = token.expiresAt;
|
|
460
902
|
const now = Math.floor(Date.now() / 1e3);
|
|
461
903
|
const bufferSeconds = 15;
|
|
@@ -465,26 +907,30 @@ function withOAuth42Auth(options = {}) {
|
|
|
465
907
|
expiresAt,
|
|
466
908
|
now,
|
|
467
909
|
expiredAgo: expiresAt ? now - expiresAt : "n/a",
|
|
468
|
-
accessTokenPrefix: token.accessToken?.substring(0, 20),
|
|
469
910
|
path: pathname
|
|
470
911
|
});
|
|
471
912
|
}
|
|
472
|
-
if (needsRefresh && token.refreshToken && clientId && clientSecret) {
|
|
473
|
-
const
|
|
474
|
-
let refreshPromise = pendingRefreshes.get(
|
|
913
|
+
if (needsRefresh && token.refreshToken && refreshConfig.clientId && (refreshConfig.clientSecret || options.clientAssertionKey)) {
|
|
914
|
+
const grantKey = JSON.stringify([refreshConfig.issuer, refreshConfig.clientId, refreshConfig.clientSecret, token.sub, token.refreshToken, options.dpop?.thumbprint, options.clientAssertionKey?.thumbprint, options.clientAssertionKey?.kid]);
|
|
915
|
+
let refreshPromise = pendingRefreshes.get(grantKey);
|
|
475
916
|
if (refreshPromise) {
|
|
476
917
|
console.log("[OAuth42 Middleware] Waiting for in-flight refresh...");
|
|
477
918
|
} else {
|
|
478
919
|
console.log("[OAuth42 Middleware] Access token expired, refreshing...");
|
|
479
920
|
refreshPromise = refreshTokens(
|
|
480
921
|
token.refreshToken,
|
|
481
|
-
clientId,
|
|
482
|
-
clientSecret,
|
|
483
|
-
issuer
|
|
922
|
+
refreshConfig.clientId,
|
|
923
|
+
refreshConfig.clientSecret || "",
|
|
924
|
+
refreshConfig.issuer,
|
|
925
|
+
options.dpop,
|
|
926
|
+
options.clientAssertionKey
|
|
484
927
|
).finally(() => {
|
|
485
|
-
setTimeout(() => pendingRefreshes.delete(
|
|
928
|
+
const cleanupTimer = setTimeout(() => pendingRefreshes.delete(grantKey), 3e4);
|
|
929
|
+
if (typeof cleanupTimer === "object" && "unref" in cleanupTimer) {
|
|
930
|
+
cleanupTimer.unref();
|
|
931
|
+
}
|
|
486
932
|
});
|
|
487
|
-
pendingRefreshes.set(
|
|
933
|
+
pendingRefreshes.set(grantKey, refreshPromise);
|
|
488
934
|
}
|
|
489
935
|
const refreshed = await refreshPromise;
|
|
490
936
|
if (refreshed.success && refreshed.accessToken && refreshed.refreshToken) {
|
|
@@ -494,12 +940,13 @@ function withOAuth42Auth(options = {}) {
|
|
|
494
940
|
refreshToken: refreshed.refreshToken,
|
|
495
941
|
expiresAt: refreshed.expiresAt
|
|
496
942
|
};
|
|
943
|
+
if (options.clientAssertionKey) delete updatedToken.clientSecret;
|
|
944
|
+
if (!await verifyAndAuthorize(updatedToken)) return denied();
|
|
497
945
|
const newJwt = await (0, import_jwt.encode)({
|
|
498
946
|
token: updatedToken,
|
|
499
947
|
secret
|
|
500
948
|
});
|
|
501
|
-
const requestHeaders =
|
|
502
|
-
requestHeaders.set("x-oauth42-refreshed-token", refreshed.accessToken);
|
|
949
|
+
const requestHeaders = forwardedHeaders(req, cookieName, newJwt);
|
|
503
950
|
const response = import_server.NextResponse.next({
|
|
504
951
|
request: {
|
|
505
952
|
headers: requestHeaders
|
|
@@ -509,7 +956,8 @@ function withOAuth42Auth(options = {}) {
|
|
|
509
956
|
httpOnly: true,
|
|
510
957
|
sameSite: "lax",
|
|
511
958
|
path: "/",
|
|
512
|
-
secure: process.env.NODE_ENV === "production"
|
|
959
|
+
secure: process.env.NODE_ENV === "production",
|
|
960
|
+
maxAge: options.session?.maxAge ?? DEFAULT_SESSION_MAX_AGE_SECONDS
|
|
513
961
|
};
|
|
514
962
|
if (newJwt.length <= CHUNK_SIZE) {
|
|
515
963
|
response.cookies.set(cookieName, newJwt, cookieOptions);
|
|
@@ -551,13 +999,12 @@ function withOAuth42Auth(options = {}) {
|
|
|
551
999
|
chunked: newJwt.length > CHUNK_SIZE,
|
|
552
1000
|
existingBaseCooke: req.cookies.has(cookieName),
|
|
553
1001
|
existingChunks: existingChunks.length > 0 ? existingChunks : "none",
|
|
554
|
-
newAccessTokenPrefix: refreshed.accessToken?.substring(0, 20),
|
|
555
1002
|
newExpiresAt: refreshed.expiresAt,
|
|
556
1003
|
cookieName
|
|
557
1004
|
});
|
|
558
1005
|
return response;
|
|
559
1006
|
} else {
|
|
560
|
-
console.log("[OAuth42 Middleware] Refresh failed
|
|
1007
|
+
console.log("[OAuth42 Middleware] Refresh failed, clearing session");
|
|
561
1008
|
const signInUrl = options.pages?.signIn || "/auth/signin";
|
|
562
1009
|
const url = new URL(signInUrl, req.url);
|
|
563
1010
|
url.searchParams.set("callbackUrl", pathname);
|
|
@@ -574,16 +1021,13 @@ function withOAuth42Auth(options = {}) {
|
|
|
574
1021
|
return response;
|
|
575
1022
|
}
|
|
576
1023
|
}
|
|
577
|
-
if (
|
|
578
|
-
const
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
const url = new URL(signInUrl, req.url);
|
|
582
|
-
url.searchParams.set("callbackUrl", pathname);
|
|
583
|
-
return import_server.NextResponse.redirect(url);
|
|
584
|
-
}
|
|
1024
|
+
if (!Number.isFinite(expiresAt) || now >= expiresAt || !token.accessToken) {
|
|
1025
|
+
const url = new URL(options.pages?.signIn || "/auth/signin", req.url);
|
|
1026
|
+
url.searchParams.set("callbackUrl", pathname);
|
|
1027
|
+
return import_server.NextResponse.redirect(url);
|
|
585
1028
|
}
|
|
586
|
-
return
|
|
1029
|
+
if (!await verifyAndAuthorize(token)) return denied();
|
|
1030
|
+
return import_server.NextResponse.next({ request: { headers: forwardedHeaders(req) } });
|
|
587
1031
|
};
|
|
588
1032
|
}
|
|
589
1033
|
function createMiddlewareConfig(protectedPaths = ["/protected"], publicPaths = ["/auth", "/api/auth"]) {
|
|
@@ -605,7 +1049,7 @@ function createMiddlewareConfig(protectedPaths = ["/protected"], publicPaths = [
|
|
|
605
1049
|
|
|
606
1050
|
// src/server/hosted-auth-callback.ts
|
|
607
1051
|
var import_server2 = require("next/server");
|
|
608
|
-
var
|
|
1052
|
+
var import_crypto = require("crypto");
|
|
609
1053
|
var import_jwt2 = require("next-auth/jwt");
|
|
610
1054
|
function createHostedAuthCallback(options = {}) {
|
|
611
1055
|
return async function GET(request) {
|
|
@@ -618,7 +1062,7 @@ function createHostedAuthCallback(options = {}) {
|
|
|
618
1062
|
redirectUrl = "/dashboard",
|
|
619
1063
|
errorUrl = "/"
|
|
620
1064
|
} = options;
|
|
621
|
-
if (!clientId || !clientSecret) {
|
|
1065
|
+
if (!clientId || !clientSecret && !options.clientAssertionKey) {
|
|
622
1066
|
console.error("OAuth42 client credentials are required");
|
|
623
1067
|
return import_server2.NextResponse.redirect(new URL(`${errorUrl}?error=missing_credentials`, request.url));
|
|
624
1068
|
}
|
|
@@ -628,6 +1072,14 @@ function createHostedAuthCallback(options = {}) {
|
|
|
628
1072
|
}
|
|
629
1073
|
const searchParams = request.nextUrl.searchParams;
|
|
630
1074
|
const code = searchParams.get("code");
|
|
1075
|
+
const cookiePrefix = process.env.NODE_ENV === "production" ? "__Host-" : request.nextUrl.protocol === "https:" ? "__Host-" : "";
|
|
1076
|
+
const expectedState = request.cookies.get(`${cookiePrefix}oauth42-state`)?.value;
|
|
1077
|
+
const returnedState = searchParams.get("state");
|
|
1078
|
+
const verifier = request.cookies.get(`${cookiePrefix}oauth42-pkce`)?.value;
|
|
1079
|
+
const validState = expectedState && returnedState && expectedState.length >= 32 && Buffer.byteLength(expectedState) === Buffer.byteLength(returnedState) && (0, import_crypto.timingSafeEqual)(Buffer.from(expectedState), Buffer.from(returnedState));
|
|
1080
|
+
if (!validState || !verifier || !/^[A-Za-z0-9._~-]{43,128}$/.test(verifier)) {
|
|
1081
|
+
return import_server2.NextResponse.redirect(new URL(`${errorUrl}?error=invalid_state`, request.url));
|
|
1082
|
+
}
|
|
631
1083
|
if (!code) {
|
|
632
1084
|
return import_server2.NextResponse.redirect(new URL(`${errorUrl}?error=missing_code`, request.url));
|
|
633
1085
|
}
|
|
@@ -635,39 +1087,38 @@ function createHostedAuthCallback(options = {}) {
|
|
|
635
1087
|
const tokenEndpoint = `${issuer}/oauth2/token`;
|
|
636
1088
|
const body = new URLSearchParams({
|
|
637
1089
|
grant_type: "authorization_code",
|
|
1090
|
+
code_verifier: verifier,
|
|
638
1091
|
code,
|
|
639
1092
|
redirect_uri: `${baseUrl}/api/oauth/callback`,
|
|
640
1093
|
client_id: clientId,
|
|
641
|
-
client_secret: clientSecret
|
|
1094
|
+
client_secret: clientSecret || ""
|
|
642
1095
|
});
|
|
643
1096
|
const fetchOptions = {
|
|
644
1097
|
method: "POST",
|
|
1098
|
+
redirect: "error",
|
|
1099
|
+
cache: "no-store",
|
|
1100
|
+
signal: AbortSignal.timeout(3e4),
|
|
645
1101
|
headers: {
|
|
646
1102
|
"Content-Type": "application/x-www-form-urlencoded"
|
|
647
1103
|
},
|
|
648
1104
|
body: body.toString()
|
|
649
1105
|
};
|
|
650
|
-
|
|
651
|
-
const agent = new import_https.default.Agent({
|
|
652
|
-
rejectUnauthorized: false
|
|
653
|
-
});
|
|
654
|
-
fetchOptions.agent = agent;
|
|
655
|
-
}
|
|
656
|
-
const tokenResponse = await fetch(tokenEndpoint, fetchOptions);
|
|
1106
|
+
const tokenResponse = options.clientAssertionKey ? await options.clientAssertionKey.fetch(clientId, tokenEndpoint, fetchOptions, options.dpop) : options.dpop ? await options.dpop.fetch(tokenEndpoint, fetchOptions) : await fetch(tokenEndpoint, fetchOptions);
|
|
657
1107
|
const tokens = await tokenResponse.json();
|
|
658
1108
|
if (!tokenResponse.ok) {
|
|
659
|
-
console.error("Token exchange failed:",
|
|
1109
|
+
console.error("Token exchange failed:", tokenResponse.status);
|
|
660
1110
|
return import_server2.NextResponse.redirect(new URL(`${errorUrl}?error=token_exchange_failed`, request.url));
|
|
661
1111
|
}
|
|
1112
|
+
options.dpop?.assertTokenResponse(tokens);
|
|
662
1113
|
const userInfoEndpoint = `${issuer}/oauth2/userinfo`;
|
|
663
|
-
const
|
|
664
|
-
headers: {
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
1114
|
+
const infoOptions = {
|
|
1115
|
+
headers: { Authorization: `Bearer ${tokens.access_token}` },
|
|
1116
|
+
redirect: "error",
|
|
1117
|
+
cache: "no-store",
|
|
1118
|
+
signal: AbortSignal.timeout(3e4)
|
|
1119
|
+
};
|
|
1120
|
+
const userInfoResponse = options.dpop ? await options.dpop.fetch(userInfoEndpoint, infoOptions, tokens.access_token) : await fetch(userInfoEndpoint, infoOptions);
|
|
1121
|
+
if (!userInfoResponse.ok) throw new Error("User info request failed");
|
|
671
1122
|
const userInfo = await userInfoResponse.json();
|
|
672
1123
|
const sessionToken = await (0, import_jwt2.encode)({
|
|
673
1124
|
token: {
|
|
@@ -692,6 +1143,14 @@ function createHostedAuthCallback(options = {}) {
|
|
|
692
1143
|
// 30 days
|
|
693
1144
|
path: "/"
|
|
694
1145
|
});
|
|
1146
|
+
for (const name of ["oauth42-state", "oauth42-pkce"]) {
|
|
1147
|
+
redirectResponse.cookies.set(`${cookiePrefix}${name}`, "", {
|
|
1148
|
+
path: "/",
|
|
1149
|
+
secure: cookiePrefix === "__Host-",
|
|
1150
|
+
sameSite: "lax",
|
|
1151
|
+
maxAge: 0
|
|
1152
|
+
});
|
|
1153
|
+
}
|
|
695
1154
|
return redirectResponse;
|
|
696
1155
|
} catch (error) {
|
|
697
1156
|
console.error("OAuth callback error:", error);
|
|
@@ -699,19 +1158,94 @@ function createHostedAuthCallback(options = {}) {
|
|
|
699
1158
|
}
|
|
700
1159
|
};
|
|
701
1160
|
}
|
|
1161
|
+
|
|
1162
|
+
// src/server/dpop.ts
|
|
1163
|
+
var import_node_crypto2 = require("crypto");
|
|
1164
|
+
var OAuth42DPoP = class _OAuth42DPoP {
|
|
1165
|
+
constructor(key) {
|
|
1166
|
+
this.nonces = /* @__PURE__ */ new Map();
|
|
1167
|
+
if (key.type !== "private" || key.asymmetricKeyType !== "ec" || key.asymmetricKeyDetails?.namedCurve !== "prime256v1") {
|
|
1168
|
+
throw new Error("DPoP requires a private P-256 key");
|
|
1169
|
+
}
|
|
1170
|
+
this.key = key;
|
|
1171
|
+
const publicKey = (0, import_node_crypto2.createPublicKey)(key).export({ format: "jwk" });
|
|
1172
|
+
this.jwk = { crv: "P-256", kty: "EC", x: publicKey.x, y: publicKey.y };
|
|
1173
|
+
this.thumbprint = (0, import_node_crypto2.createHash)("sha256").update(JSON.stringify(this.jwk)).digest("base64url");
|
|
1174
|
+
}
|
|
1175
|
+
/** Persist keys in protected server storage; regeneration cannot recover a public refresh grant. */
|
|
1176
|
+
static generate() {
|
|
1177
|
+
return new _OAuth42DPoP((0, import_node_crypto2.generateKeyPairSync)("ec", { namedCurve: "prime256v1" }).privateKey);
|
|
1178
|
+
}
|
|
1179
|
+
/** Passed directly to openid-client so its callback keeps OIDC issuer/nonce/signature validation. */
|
|
1180
|
+
get oidcKey() {
|
|
1181
|
+
return this.key;
|
|
1182
|
+
}
|
|
1183
|
+
proof(method, target, nonce, accessToken) {
|
|
1184
|
+
const url = new URL(target);
|
|
1185
|
+
if (!["http:", "https:"].includes(url.protocol) || url.protocol !== "https:" && !["localhost", "127.0.0.1", "[::1]"].includes(url.hostname)) {
|
|
1186
|
+
throw new Error("DPoP requires HTTPS outside loopback development");
|
|
1187
|
+
}
|
|
1188
|
+
if (url.username || url.password) throw new Error("DPoP endpoint must not contain credentials");
|
|
1189
|
+
url.search = "";
|
|
1190
|
+
url.hash = "";
|
|
1191
|
+
const header = { typ: "dpop+jwt", alg: "ES256", jwk: this.jwk };
|
|
1192
|
+
const claims = {
|
|
1193
|
+
htm: method.toUpperCase(),
|
|
1194
|
+
htu: url.href,
|
|
1195
|
+
iat: Math.floor(Date.now() / 1e3),
|
|
1196
|
+
jti: (0, import_node_crypto2.randomBytes)(32).toString("base64url"),
|
|
1197
|
+
...nonce ? { nonce } : {},
|
|
1198
|
+
...accessToken !== void 0 ? { ath: (0, import_node_crypto2.createHash)("sha256").update(accessToken).digest("base64url") } : {}
|
|
1199
|
+
};
|
|
1200
|
+
const input = `${Buffer.from(JSON.stringify(header)).toString("base64url")}.${Buffer.from(JSON.stringify(claims)).toString("base64url")}`;
|
|
1201
|
+
return `${input}.${(0, import_node_crypto2.sign)("sha256", Buffer.from(input), { key: this.key, dsaEncoding: "ieee-p1363" }).toString("base64url")}`;
|
|
1202
|
+
}
|
|
1203
|
+
assertTokenResponse(tokens) {
|
|
1204
|
+
if (typeof tokens.token_type !== "string" || tokens.token_type.toLowerCase() !== "dpop") {
|
|
1205
|
+
throw new Error("Server did not issue the requested DPoP-bound token");
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
async fetch(target, init = {}, accessToken, prepare) {
|
|
1209
|
+
if (init.body instanceof ReadableStream) throw new Error("DPoP nonce retries require a replayable request body");
|
|
1210
|
+
const cacheKey = `${new URL(target).origin}:${accessToken !== void 0 ? "resource" : "token"}`;
|
|
1211
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
1212
|
+
const request = prepare ? prepare() : init;
|
|
1213
|
+
const headers = new Headers(request.headers);
|
|
1214
|
+
headers.set("DPoP", this.proof(request.method || "GET", target, this.nonces.get(cacheKey), accessToken));
|
|
1215
|
+
if (accessToken !== void 0) headers.set("Authorization", `DPoP ${accessToken}`);
|
|
1216
|
+
const response = await fetch(target, { ...request, headers, redirect: "error", cache: "no-store", signal: request.signal || AbortSignal.timeout(3e4) });
|
|
1217
|
+
const nonce = response.headers.get("DPoP-Nonce");
|
|
1218
|
+
if (nonce && /^[\x21-\x7e]{1,128}$/.test(nonce)) {
|
|
1219
|
+
if (this.nonces.size >= 256) this.nonces.clear();
|
|
1220
|
+
this.nonces.set(cacheKey, nonce);
|
|
1221
|
+
} else return response;
|
|
1222
|
+
if (attempt || ![400, 401].includes(response.status)) return response;
|
|
1223
|
+
const error = await response.clone().json().catch(() => null);
|
|
1224
|
+
if (error?.error !== "use_dpop_nonce") return response;
|
|
1225
|
+
}
|
|
1226
|
+
throw new Error("DPoP nonce negotiation failed");
|
|
1227
|
+
}
|
|
1228
|
+
};
|
|
702
1229
|
// Annotate the CommonJS export names for ESM import in node:
|
|
703
1230
|
0 && (module.exports = {
|
|
704
1231
|
NextAuth,
|
|
1232
|
+
OAuth42ClientAssertion,
|
|
1233
|
+
OAuth42DPoP,
|
|
705
1234
|
OAuth42Provider,
|
|
706
1235
|
createAuth,
|
|
1236
|
+
createBackendSession,
|
|
707
1237
|
createHandlers,
|
|
708
1238
|
createHostedAuthCallback,
|
|
709
1239
|
createMiddlewareConfig,
|
|
710
1240
|
getOAuth42Session,
|
|
711
1241
|
getServerSession,
|
|
1242
|
+
pushAuthorizationURL,
|
|
712
1243
|
refreshAccessToken,
|
|
1244
|
+
resolveBackendSession,
|
|
1245
|
+
revokeBackendSession,
|
|
713
1246
|
withOAuth42Auth,
|
|
714
1247
|
withOAuth42ServerSideProps,
|
|
715
|
-
withOAuth42Session
|
|
1248
|
+
withOAuth42Session,
|
|
1249
|
+
withPushedAuthorization
|
|
716
1250
|
});
|
|
717
1251
|
//# sourceMappingURL=index.js.map
|