transactional-auth-next 0.1.0

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.
@@ -0,0 +1,369 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/server/index.ts
31
+ var server_exports = {};
32
+ __export(server_exports, {
33
+ getAccessToken: () => getAccessToken,
34
+ getSession: () => getSession,
35
+ getUser: () => getUser,
36
+ handleCallback: () => handleCallback,
37
+ handleLogin: () => handleLogin,
38
+ handleLogout: () => handleLogout,
39
+ handleSession: () => handleSession,
40
+ isAuthenticated: () => isAuthenticated
41
+ });
42
+ module.exports = __toCommonJS(server_exports);
43
+
44
+ // src/server/session.ts
45
+ var import_headers = require("next/headers");
46
+ var jose = __toESM(require("jose"));
47
+
48
+ // src/config.ts
49
+ var globalConfig = null;
50
+ function getConfig() {
51
+ if (!globalConfig) {
52
+ const domain = process.env.TRANSACTIONAL_AUTH_DOMAIN || process.env.NEXT_PUBLIC_TRANSACTIONAL_AUTH_DOMAIN;
53
+ const clientId = process.env.TRANSACTIONAL_AUTH_CLIENT_ID || process.env.NEXT_PUBLIC_TRANSACTIONAL_AUTH_CLIENT_ID;
54
+ const clientSecret = process.env.TRANSACTIONAL_AUTH_CLIENT_SECRET;
55
+ const baseUrl = process.env.TRANSACTIONAL_AUTH_BASE_URL || process.env.NEXT_PUBLIC_APP_URL;
56
+ if (domain && clientId) {
57
+ globalConfig = {
58
+ domain,
59
+ clientId,
60
+ clientSecret,
61
+ baseUrl,
62
+ scope: "openid profile email",
63
+ cookieName: "transactional_session",
64
+ cookieOptions: {
65
+ secure: process.env.NODE_ENV === "production",
66
+ sameSite: "lax",
67
+ maxAge: 7 * 24 * 60 * 60
68
+ }
69
+ };
70
+ return globalConfig;
71
+ }
72
+ throw new Error(
73
+ "Transactional Auth not initialized. Call initTransactionalAuth() or set environment variables."
74
+ );
75
+ }
76
+ return globalConfig;
77
+ }
78
+
79
+ // src/server/session.ts
80
+ async function getSession() {
81
+ const config = getConfig();
82
+ const cookieStore = await (0, import_headers.cookies)();
83
+ const sessionCookie = cookieStore.get(config.cookieName || "transactional_session");
84
+ if (!sessionCookie?.value) {
85
+ return null;
86
+ }
87
+ try {
88
+ const sessionData = JSON.parse(
89
+ Buffer.from(sessionCookie.value, "base64").toString("utf-8")
90
+ );
91
+ if (sessionData.expiresAt < Date.now() / 1e3) {
92
+ if (sessionData.refreshToken) {
93
+ const newSession = await refreshSession(sessionData.refreshToken);
94
+ if (newSession) {
95
+ return newSession;
96
+ }
97
+ }
98
+ return null;
99
+ }
100
+ return sessionData;
101
+ } catch {
102
+ return null;
103
+ }
104
+ }
105
+ async function getUser() {
106
+ const session = await getSession();
107
+ return session?.user || null;
108
+ }
109
+ async function getAccessToken() {
110
+ const session = await getSession();
111
+ return session?.accessToken || null;
112
+ }
113
+ async function isAuthenticated() {
114
+ const session = await getSession();
115
+ return session !== null;
116
+ }
117
+ async function refreshSession(refreshToken) {
118
+ const config = getConfig();
119
+ try {
120
+ const response = await fetch(`https://${config.domain}/token`, {
121
+ method: "POST",
122
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
123
+ body: new URLSearchParams({
124
+ grant_type: "refresh_token",
125
+ client_id: config.clientId,
126
+ ...config.clientSecret ? { client_secret: config.clientSecret } : {},
127
+ refresh_token: refreshToken
128
+ })
129
+ });
130
+ if (!response.ok) {
131
+ return null;
132
+ }
133
+ const tokens = await response.json();
134
+ const idToken = jose.decodeJwt(tokens.id_token);
135
+ const session = {
136
+ user: {
137
+ sub: idToken.sub,
138
+ email: idToken.email,
139
+ emailVerified: idToken.email_verified,
140
+ name: idToken.name,
141
+ givenName: idToken.given_name,
142
+ familyName: idToken.family_name,
143
+ picture: idToken.picture
144
+ },
145
+ accessToken: tokens.access_token,
146
+ refreshToken: tokens.refresh_token || refreshToken,
147
+ idToken: tokens.id_token,
148
+ expiresAt: Math.floor(Date.now() / 1e3) + tokens.expires_in
149
+ };
150
+ const cookieStore = await (0, import_headers.cookies)();
151
+ cookieStore.set(
152
+ config.cookieName || "transactional_session",
153
+ Buffer.from(JSON.stringify(session)).toString("base64"),
154
+ {
155
+ httpOnly: true,
156
+ secure: config.cookieOptions?.secure ?? process.env.NODE_ENV === "production",
157
+ sameSite: config.cookieOptions?.sameSite ?? "lax",
158
+ maxAge: config.cookieOptions?.maxAge ?? 7 * 24 * 60 * 60,
159
+ path: "/"
160
+ }
161
+ );
162
+ return session;
163
+ } catch {
164
+ return null;
165
+ }
166
+ }
167
+
168
+ // src/server/handlers.ts
169
+ var import_headers2 = require("next/headers");
170
+ var import_server = require("next/server");
171
+ var jose2 = __toESM(require("jose"));
172
+ function generateRandomString(length) {
173
+ const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
174
+ let result = "";
175
+ const randomValues = new Uint8Array(length);
176
+ crypto.getRandomValues(randomValues);
177
+ for (let i = 0; i < length; i++) {
178
+ result += chars[randomValues[i] % chars.length];
179
+ }
180
+ return result;
181
+ }
182
+ async function generatePKCE() {
183
+ const verifier = generateRandomString(64);
184
+ const encoder = new TextEncoder();
185
+ const data = encoder.encode(verifier);
186
+ const hash = await crypto.subtle.digest("SHA-256", data);
187
+ const challenge = Buffer.from(hash).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
188
+ return { verifier, challenge };
189
+ }
190
+ function handleLogin(options) {
191
+ return async (request) => {
192
+ const config = getConfig();
193
+ const { verifier, challenge } = await generatePKCE();
194
+ const state = generateRandomString(32);
195
+ const returnTo = options?.returnTo || request.nextUrl.searchParams.get("returnTo") || "/";
196
+ const cookieStore = await (0, import_headers2.cookies)();
197
+ cookieStore.set("transactional_pkce_verifier", verifier, {
198
+ httpOnly: true,
199
+ secure: process.env.NODE_ENV === "production",
200
+ sameSite: "lax",
201
+ maxAge: 300,
202
+ // 5 minutes
203
+ path: "/"
204
+ });
205
+ cookieStore.set("transactional_auth_state", JSON.stringify({ state, returnTo }), {
206
+ httpOnly: true,
207
+ secure: process.env.NODE_ENV === "production",
208
+ sameSite: "lax",
209
+ maxAge: 300,
210
+ path: "/"
211
+ });
212
+ const authUrl = new URL(`https://${config.domain}/authorize`);
213
+ authUrl.searchParams.set("client_id", config.clientId);
214
+ authUrl.searchParams.set("redirect_uri", `${config.baseUrl}/api/auth/callback`);
215
+ authUrl.searchParams.set("response_type", "code");
216
+ authUrl.searchParams.set("scope", config.scope || "openid profile email");
217
+ authUrl.searchParams.set("state", state);
218
+ authUrl.searchParams.set("code_challenge", challenge);
219
+ authUrl.searchParams.set("code_challenge_method", "S256");
220
+ if (options?.connection) {
221
+ authUrl.searchParams.set("connection", options.connection);
222
+ }
223
+ if (options?.loginHint) {
224
+ authUrl.searchParams.set("login_hint", options.loginHint);
225
+ }
226
+ if (config.audience) {
227
+ authUrl.searchParams.set("audience", config.audience);
228
+ }
229
+ return import_server.NextResponse.redirect(authUrl.toString());
230
+ };
231
+ }
232
+ function handleCallback() {
233
+ return async (request) => {
234
+ const config = getConfig();
235
+ const searchParams = request.nextUrl.searchParams;
236
+ const code = searchParams.get("code");
237
+ const state = searchParams.get("state");
238
+ const error = searchParams.get("error");
239
+ if (error) {
240
+ const errorDescription = searchParams.get("error_description") || error;
241
+ return import_server.NextResponse.redirect(
242
+ `${config.baseUrl}/auth/error?error=${encodeURIComponent(errorDescription)}`
243
+ );
244
+ }
245
+ if (!code || !state) {
246
+ return import_server.NextResponse.redirect(`${config.baseUrl}/auth/error?error=missing_code_or_state`);
247
+ }
248
+ const cookieStore = await (0, import_headers2.cookies)();
249
+ const stateCookie = cookieStore.get("transactional_auth_state");
250
+ const verifierCookie = cookieStore.get("transactional_pkce_verifier");
251
+ if (!stateCookie?.value || !verifierCookie?.value) {
252
+ return import_server.NextResponse.redirect(`${config.baseUrl}/auth/error?error=missing_state_cookie`);
253
+ }
254
+ const storedState = JSON.parse(stateCookie.value);
255
+ if (storedState.state !== state) {
256
+ return import_server.NextResponse.redirect(`${config.baseUrl}/auth/error?error=state_mismatch`);
257
+ }
258
+ const tokenResponse = await fetch(`https://${config.domain}/token`, {
259
+ method: "POST",
260
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
261
+ body: new URLSearchParams({
262
+ grant_type: "authorization_code",
263
+ client_id: config.clientId,
264
+ ...config.clientSecret ? { client_secret: config.clientSecret } : {},
265
+ code,
266
+ redirect_uri: `${config.baseUrl}/api/auth/callback`,
267
+ code_verifier: verifierCookie.value
268
+ })
269
+ });
270
+ if (!tokenResponse.ok) {
271
+ const errorData = await tokenResponse.json().catch(() => ({}));
272
+ console.error("Token exchange failed:", errorData);
273
+ return import_server.NextResponse.redirect(`${config.baseUrl}/auth/error?error=token_exchange_failed`);
274
+ }
275
+ const tokens = await tokenResponse.json();
276
+ const idToken = jose2.decodeJwt(tokens.id_token);
277
+ const session = {
278
+ user: {
279
+ sub: idToken.sub,
280
+ email: idToken.email,
281
+ emailVerified: idToken.email_verified,
282
+ name: idToken.name,
283
+ givenName: idToken.given_name,
284
+ familyName: idToken.family_name,
285
+ picture: idToken.picture
286
+ },
287
+ accessToken: tokens.access_token,
288
+ refreshToken: tokens.refresh_token,
289
+ idToken: tokens.id_token,
290
+ expiresAt: Math.floor(Date.now() / 1e3) + tokens.expires_in
291
+ };
292
+ cookieStore.set(
293
+ config.cookieName || "transactional_session",
294
+ Buffer.from(JSON.stringify(session)).toString("base64"),
295
+ {
296
+ httpOnly: true,
297
+ secure: config.cookieOptions?.secure ?? process.env.NODE_ENV === "production",
298
+ sameSite: config.cookieOptions?.sameSite ?? "lax",
299
+ maxAge: config.cookieOptions?.maxAge ?? 7 * 24 * 60 * 60,
300
+ path: "/"
301
+ }
302
+ );
303
+ cookieStore.delete("transactional_pkce_verifier");
304
+ cookieStore.delete("transactional_auth_state");
305
+ return import_server.NextResponse.redirect(`${config.baseUrl}${storedState.returnTo || "/"}`);
306
+ };
307
+ }
308
+ function handleLogout(options) {
309
+ return async (request) => {
310
+ const config = getConfig();
311
+ const cookieStore = await (0, import_headers2.cookies)();
312
+ const sessionCookie = cookieStore.get(config.cookieName || "transactional_session");
313
+ let idToken;
314
+ if (sessionCookie?.value) {
315
+ try {
316
+ const session = JSON.parse(
317
+ Buffer.from(sessionCookie.value, "base64").toString("utf-8")
318
+ );
319
+ idToken = session.idToken;
320
+ } catch {
321
+ }
322
+ }
323
+ cookieStore.delete(config.cookieName || "transactional_session");
324
+ const returnTo = options?.returnTo || request.nextUrl.searchParams.get("returnTo") || config.baseUrl || "/";
325
+ const logoutUrl = new URL(`https://${config.domain}/session/end`);
326
+ logoutUrl.searchParams.set("post_logout_redirect_uri", returnTo);
327
+ if (idToken) {
328
+ logoutUrl.searchParams.set("id_token_hint", idToken);
329
+ }
330
+ return import_server.NextResponse.redirect(logoutUrl.toString());
331
+ };
332
+ }
333
+ function handleSession() {
334
+ return async () => {
335
+ const config = getConfig();
336
+ const cookieStore = await (0, import_headers2.cookies)();
337
+ const sessionCookie = cookieStore.get(config.cookieName || "transactional_session");
338
+ if (!sessionCookie?.value) {
339
+ return import_server.NextResponse.json({ session: null });
340
+ }
341
+ try {
342
+ const session = JSON.parse(
343
+ Buffer.from(sessionCookie.value, "base64").toString("utf-8")
344
+ );
345
+ if (session.expiresAt < Date.now() / 1e3) {
346
+ return import_server.NextResponse.json({ session: null });
347
+ }
348
+ return import_server.NextResponse.json({
349
+ session: {
350
+ user: session.user,
351
+ expiresAt: session.expiresAt
352
+ }
353
+ });
354
+ } catch {
355
+ return import_server.NextResponse.json({ session: null });
356
+ }
357
+ };
358
+ }
359
+ // Annotate the CommonJS export names for ESM import in node:
360
+ 0 && (module.exports = {
361
+ getAccessToken,
362
+ getSession,
363
+ getUser,
364
+ handleCallback,
365
+ handleLogin,
366
+ handleLogout,
367
+ handleSession,
368
+ isAuthenticated
369
+ });
@@ -0,0 +1,296 @@
1
+ import {
2
+ getConfig
3
+ } from "../chunk-4S34DQOR.mjs";
4
+
5
+ // src/server/session.ts
6
+ import { cookies } from "next/headers";
7
+ import * as jose from "jose";
8
+ async function getSession() {
9
+ const config = getConfig();
10
+ const cookieStore = await cookies();
11
+ const sessionCookie = cookieStore.get(config.cookieName || "transactional_session");
12
+ if (!sessionCookie?.value) {
13
+ return null;
14
+ }
15
+ try {
16
+ const sessionData = JSON.parse(
17
+ Buffer.from(sessionCookie.value, "base64").toString("utf-8")
18
+ );
19
+ if (sessionData.expiresAt < Date.now() / 1e3) {
20
+ if (sessionData.refreshToken) {
21
+ const newSession = await refreshSession(sessionData.refreshToken);
22
+ if (newSession) {
23
+ return newSession;
24
+ }
25
+ }
26
+ return null;
27
+ }
28
+ return sessionData;
29
+ } catch {
30
+ return null;
31
+ }
32
+ }
33
+ async function getUser() {
34
+ const session = await getSession();
35
+ return session?.user || null;
36
+ }
37
+ async function getAccessToken() {
38
+ const session = await getSession();
39
+ return session?.accessToken || null;
40
+ }
41
+ async function isAuthenticated() {
42
+ const session = await getSession();
43
+ return session !== null;
44
+ }
45
+ async function refreshSession(refreshToken) {
46
+ const config = getConfig();
47
+ try {
48
+ const response = await fetch(`https://${config.domain}/token`, {
49
+ method: "POST",
50
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
51
+ body: new URLSearchParams({
52
+ grant_type: "refresh_token",
53
+ client_id: config.clientId,
54
+ ...config.clientSecret ? { client_secret: config.clientSecret } : {},
55
+ refresh_token: refreshToken
56
+ })
57
+ });
58
+ if (!response.ok) {
59
+ return null;
60
+ }
61
+ const tokens = await response.json();
62
+ const idToken = jose.decodeJwt(tokens.id_token);
63
+ const session = {
64
+ user: {
65
+ sub: idToken.sub,
66
+ email: idToken.email,
67
+ emailVerified: idToken.email_verified,
68
+ name: idToken.name,
69
+ givenName: idToken.given_name,
70
+ familyName: idToken.family_name,
71
+ picture: idToken.picture
72
+ },
73
+ accessToken: tokens.access_token,
74
+ refreshToken: tokens.refresh_token || refreshToken,
75
+ idToken: tokens.id_token,
76
+ expiresAt: Math.floor(Date.now() / 1e3) + tokens.expires_in
77
+ };
78
+ const cookieStore = await cookies();
79
+ cookieStore.set(
80
+ config.cookieName || "transactional_session",
81
+ Buffer.from(JSON.stringify(session)).toString("base64"),
82
+ {
83
+ httpOnly: true,
84
+ secure: config.cookieOptions?.secure ?? process.env.NODE_ENV === "production",
85
+ sameSite: config.cookieOptions?.sameSite ?? "lax",
86
+ maxAge: config.cookieOptions?.maxAge ?? 7 * 24 * 60 * 60,
87
+ path: "/"
88
+ }
89
+ );
90
+ return session;
91
+ } catch {
92
+ return null;
93
+ }
94
+ }
95
+
96
+ // src/server/handlers.ts
97
+ import { cookies as cookies2 } from "next/headers";
98
+ import { NextResponse } from "next/server";
99
+ import * as jose2 from "jose";
100
+ function generateRandomString(length) {
101
+ const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
102
+ let result = "";
103
+ const randomValues = new Uint8Array(length);
104
+ crypto.getRandomValues(randomValues);
105
+ for (let i = 0; i < length; i++) {
106
+ result += chars[randomValues[i] % chars.length];
107
+ }
108
+ return result;
109
+ }
110
+ async function generatePKCE() {
111
+ const verifier = generateRandomString(64);
112
+ const encoder = new TextEncoder();
113
+ const data = encoder.encode(verifier);
114
+ const hash = await crypto.subtle.digest("SHA-256", data);
115
+ const challenge = Buffer.from(hash).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
116
+ return { verifier, challenge };
117
+ }
118
+ function handleLogin(options) {
119
+ return async (request) => {
120
+ const config = getConfig();
121
+ const { verifier, challenge } = await generatePKCE();
122
+ const state = generateRandomString(32);
123
+ const returnTo = options?.returnTo || request.nextUrl.searchParams.get("returnTo") || "/";
124
+ const cookieStore = await cookies2();
125
+ cookieStore.set("transactional_pkce_verifier", verifier, {
126
+ httpOnly: true,
127
+ secure: process.env.NODE_ENV === "production",
128
+ sameSite: "lax",
129
+ maxAge: 300,
130
+ // 5 minutes
131
+ path: "/"
132
+ });
133
+ cookieStore.set("transactional_auth_state", JSON.stringify({ state, returnTo }), {
134
+ httpOnly: true,
135
+ secure: process.env.NODE_ENV === "production",
136
+ sameSite: "lax",
137
+ maxAge: 300,
138
+ path: "/"
139
+ });
140
+ const authUrl = new URL(`https://${config.domain}/authorize`);
141
+ authUrl.searchParams.set("client_id", config.clientId);
142
+ authUrl.searchParams.set("redirect_uri", `${config.baseUrl}/api/auth/callback`);
143
+ authUrl.searchParams.set("response_type", "code");
144
+ authUrl.searchParams.set("scope", config.scope || "openid profile email");
145
+ authUrl.searchParams.set("state", state);
146
+ authUrl.searchParams.set("code_challenge", challenge);
147
+ authUrl.searchParams.set("code_challenge_method", "S256");
148
+ if (options?.connection) {
149
+ authUrl.searchParams.set("connection", options.connection);
150
+ }
151
+ if (options?.loginHint) {
152
+ authUrl.searchParams.set("login_hint", options.loginHint);
153
+ }
154
+ if (config.audience) {
155
+ authUrl.searchParams.set("audience", config.audience);
156
+ }
157
+ return NextResponse.redirect(authUrl.toString());
158
+ };
159
+ }
160
+ function handleCallback() {
161
+ return async (request) => {
162
+ const config = getConfig();
163
+ const searchParams = request.nextUrl.searchParams;
164
+ const code = searchParams.get("code");
165
+ const state = searchParams.get("state");
166
+ const error = searchParams.get("error");
167
+ if (error) {
168
+ const errorDescription = searchParams.get("error_description") || error;
169
+ return NextResponse.redirect(
170
+ `${config.baseUrl}/auth/error?error=${encodeURIComponent(errorDescription)}`
171
+ );
172
+ }
173
+ if (!code || !state) {
174
+ return NextResponse.redirect(`${config.baseUrl}/auth/error?error=missing_code_or_state`);
175
+ }
176
+ const cookieStore = await cookies2();
177
+ const stateCookie = cookieStore.get("transactional_auth_state");
178
+ const verifierCookie = cookieStore.get("transactional_pkce_verifier");
179
+ if (!stateCookie?.value || !verifierCookie?.value) {
180
+ return NextResponse.redirect(`${config.baseUrl}/auth/error?error=missing_state_cookie`);
181
+ }
182
+ const storedState = JSON.parse(stateCookie.value);
183
+ if (storedState.state !== state) {
184
+ return NextResponse.redirect(`${config.baseUrl}/auth/error?error=state_mismatch`);
185
+ }
186
+ const tokenResponse = await fetch(`https://${config.domain}/token`, {
187
+ method: "POST",
188
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
189
+ body: new URLSearchParams({
190
+ grant_type: "authorization_code",
191
+ client_id: config.clientId,
192
+ ...config.clientSecret ? { client_secret: config.clientSecret } : {},
193
+ code,
194
+ redirect_uri: `${config.baseUrl}/api/auth/callback`,
195
+ code_verifier: verifierCookie.value
196
+ })
197
+ });
198
+ if (!tokenResponse.ok) {
199
+ const errorData = await tokenResponse.json().catch(() => ({}));
200
+ console.error("Token exchange failed:", errorData);
201
+ return NextResponse.redirect(`${config.baseUrl}/auth/error?error=token_exchange_failed`);
202
+ }
203
+ const tokens = await tokenResponse.json();
204
+ const idToken = jose2.decodeJwt(tokens.id_token);
205
+ const session = {
206
+ user: {
207
+ sub: idToken.sub,
208
+ email: idToken.email,
209
+ emailVerified: idToken.email_verified,
210
+ name: idToken.name,
211
+ givenName: idToken.given_name,
212
+ familyName: idToken.family_name,
213
+ picture: idToken.picture
214
+ },
215
+ accessToken: tokens.access_token,
216
+ refreshToken: tokens.refresh_token,
217
+ idToken: tokens.id_token,
218
+ expiresAt: Math.floor(Date.now() / 1e3) + tokens.expires_in
219
+ };
220
+ cookieStore.set(
221
+ config.cookieName || "transactional_session",
222
+ Buffer.from(JSON.stringify(session)).toString("base64"),
223
+ {
224
+ httpOnly: true,
225
+ secure: config.cookieOptions?.secure ?? process.env.NODE_ENV === "production",
226
+ sameSite: config.cookieOptions?.sameSite ?? "lax",
227
+ maxAge: config.cookieOptions?.maxAge ?? 7 * 24 * 60 * 60,
228
+ path: "/"
229
+ }
230
+ );
231
+ cookieStore.delete("transactional_pkce_verifier");
232
+ cookieStore.delete("transactional_auth_state");
233
+ return NextResponse.redirect(`${config.baseUrl}${storedState.returnTo || "/"}`);
234
+ };
235
+ }
236
+ function handleLogout(options) {
237
+ return async (request) => {
238
+ const config = getConfig();
239
+ const cookieStore = await cookies2();
240
+ const sessionCookie = cookieStore.get(config.cookieName || "transactional_session");
241
+ let idToken;
242
+ if (sessionCookie?.value) {
243
+ try {
244
+ const session = JSON.parse(
245
+ Buffer.from(sessionCookie.value, "base64").toString("utf-8")
246
+ );
247
+ idToken = session.idToken;
248
+ } catch {
249
+ }
250
+ }
251
+ cookieStore.delete(config.cookieName || "transactional_session");
252
+ const returnTo = options?.returnTo || request.nextUrl.searchParams.get("returnTo") || config.baseUrl || "/";
253
+ const logoutUrl = new URL(`https://${config.domain}/session/end`);
254
+ logoutUrl.searchParams.set("post_logout_redirect_uri", returnTo);
255
+ if (idToken) {
256
+ logoutUrl.searchParams.set("id_token_hint", idToken);
257
+ }
258
+ return NextResponse.redirect(logoutUrl.toString());
259
+ };
260
+ }
261
+ function handleSession() {
262
+ return async () => {
263
+ const config = getConfig();
264
+ const cookieStore = await cookies2();
265
+ const sessionCookie = cookieStore.get(config.cookieName || "transactional_session");
266
+ if (!sessionCookie?.value) {
267
+ return NextResponse.json({ session: null });
268
+ }
269
+ try {
270
+ const session = JSON.parse(
271
+ Buffer.from(sessionCookie.value, "base64").toString("utf-8")
272
+ );
273
+ if (session.expiresAt < Date.now() / 1e3) {
274
+ return NextResponse.json({ session: null });
275
+ }
276
+ return NextResponse.json({
277
+ session: {
278
+ user: session.user,
279
+ expiresAt: session.expiresAt
280
+ }
281
+ });
282
+ } catch {
283
+ return NextResponse.json({ session: null });
284
+ }
285
+ };
286
+ }
287
+ export {
288
+ getAccessToken,
289
+ getSession,
290
+ getUser,
291
+ handleCallback,
292
+ handleLogin,
293
+ handleLogout,
294
+ handleSession,
295
+ isAuthenticated
296
+ };