@bigso/auth-sdk 0.4.7 → 0.5.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,32 @@
1
+ // src/utils/jws.ts
2
+ import { jwtVerify, createRemoteJWKSet } from "jose";
3
+ async function verifySignedPayload(token, jwksUrl, expectedAudience) {
4
+ const JWKS = createRemoteJWKSet(new URL(jwksUrl));
5
+ const { payload } = await jwtVerify(token, JWKS, {
6
+ audience: expectedAudience
7
+ });
8
+ return payload;
9
+ }
10
+ async function verifyAccessToken(accessToken, jwksUrl) {
11
+ const JWKS = createRemoteJWKSet(new URL(jwksUrl));
12
+ const { payload } = await jwtVerify(accessToken, JWKS);
13
+ if (!payload.sub || !payload.jti) {
14
+ throw new Error("Invalid token structure: missing sub or jti");
15
+ }
16
+ return {
17
+ sub: payload.sub,
18
+ jti: payload.jti,
19
+ iss: payload.iss,
20
+ aud: payload.aud || "",
21
+ exp: payload.exp,
22
+ iat: payload.iat,
23
+ tenants: payload.tenants || [],
24
+ systemRole: payload.systemRole || "user",
25
+ deviceFingerprint: payload.deviceFingerprint
26
+ };
27
+ }
28
+
29
+ export {
30
+ verifySignedPayload,
31
+ verifyAccessToken
32
+ };
@@ -29,56 +29,32 @@ module.exports = __toCommonJS(express_exports);
29
29
 
30
30
  // src/express/middlewares/ssoAuth.ts
31
31
  function ssoAuthMiddleware(options) {
32
- const cookieName = options.cookieName || "sso_session";
33
- const isProduction = options.isProduction ?? process.env.NODE_ENV === "production";
34
32
  return async (req, res, next) => {
35
33
  try {
36
- let sessionToken = req.cookies?.[cookieName];
37
- let session = null;
38
- if (sessionToken) {
39
- session = await options.ssoClient.validateSessionToken(sessionToken);
40
- }
41
- if (!session) {
42
- const refreshToken = req.cookies?.[`${cookieName}_refresh`];
43
- if (refreshToken) {
44
- const newSessionData = await options.ssoClient.refreshAppSession(refreshToken);
45
- if (newSessionData) {
46
- const sessionMaxAge = new Date(newSessionData.expiresAt).getTime() - Date.now();
47
- const refreshMaxAge = newSessionData.refreshExpiresAt ? new Date(newSessionData.refreshExpiresAt).getTime() - Date.now() : 7 * 24 * 60 * 60 * 1e3;
48
- const sessionCookieOptions = {
49
- httpOnly: true,
50
- secure: isProduction,
51
- sameSite: "lax",
52
- path: "/",
53
- maxAge: sessionMaxAge > 0 ? sessionMaxAge : 0,
54
- ...isProduction && options.cookieDomain ? { domain: options.cookieDomain } : {}
55
- };
56
- const refreshCookieOptions = {
57
- ...sessionCookieOptions,
58
- maxAge: refreshMaxAge > 0 ? refreshMaxAge : 0
59
- };
60
- res.cookie(cookieName, newSessionData.sessionToken, sessionCookieOptions);
61
- res.cookie(`${cookieName}_refresh`, newSessionData.refreshToken, refreshCookieOptions);
62
- session = await options.ssoClient.validateSessionToken(newSessionData.sessionToken);
63
- }
64
- }
65
- if (!session) {
66
- res.clearCookie(cookieName);
67
- res.clearCookie(`${cookieName}_refresh`);
68
- res.status(401).json({ error: "Session expired or invalid" });
69
- return;
70
- }
34
+ const authHeader = req.headers.authorization;
35
+ if (!authHeader || !authHeader.startsWith("Bearer ")) {
36
+ res.status(401).json({ error: "Missing access token" });
37
+ return;
71
38
  }
72
- if (options.onSessionValidated) {
73
- await options.onSessionValidated(session, req);
39
+ const accessToken = authHeader.substring(7);
40
+ const payload = await options.ssoClient.validateAccessToken(accessToken);
41
+ if (!payload) {
42
+ res.status(401).json({ error: "Invalid or expired access token" });
43
+ return;
74
44
  }
75
- req.user = session.user;
76
- req.tenant = session.tenant;
77
- req.ssoSession = session;
45
+ const primaryTenant = payload.tenants?.[0];
46
+ req.user = {
47
+ userId: payload.sub,
48
+ email: "",
49
+ firstName: "",
50
+ lastName: ""
51
+ };
52
+ req.tenant = primaryTenant || void 0;
53
+ req.tokenPayload = payload;
78
54
  next();
79
55
  } catch (error) {
80
- console.error("\u274C [BigsoAuthSDK] Authentication Middleware Error:", error instanceof Error ? error.message : error);
81
- res.status(500).json({ error: "Internal authentication error" });
56
+ console.error("[BigsoAuthSDK] Authentication Middleware Error:", error instanceof Error ? error.message : error);
57
+ res.status(401).json({ error: "Authentication failed" });
82
58
  }
83
59
  };
84
60
  }
@@ -122,59 +98,26 @@ function ssoSyncGuardMiddleware(options) {
122
98
  var import_express = require("express");
123
99
  function createSsoAuthRouter(options) {
124
100
  const router = (0, import_express.Router)();
125
- const cookieName = options.cookieName || "sso_session";
126
- const isProduction = options.isProduction ?? process.env.NODE_ENV === "production";
127
- const getCookieOptions = (customOptions = {}) => {
128
- const base = {
129
- httpOnly: true,
130
- secure: isProduction,
131
- sameSite: "lax",
132
- path: "/",
133
- ...customOptions
134
- };
135
- if (isProduction && options.cookieDomain) {
136
- base.domain = options.cookieDomain;
137
- }
138
- return base;
139
- };
140
101
  router.post("/exchange", async (req, res) => {
141
102
  try {
142
- const { code } = req.body;
143
- if (!code) {
144
- res.status(400).json({ error: "Authorization code is required" });
145
- return;
146
- }
147
- const ssoResponse = await options.ssoClient.exchangeCodeForToken(code);
148
- if (!ssoResponse.success) {
149
- res.status(401).json({ error: "Invalid authorization code" });
103
+ const { code, codeVerifier } = req.body;
104
+ if (!code || !codeVerifier) {
105
+ res.status(400).json({ error: "code and codeVerifier are required" });
150
106
  return;
151
107
  }
152
- const sessionMaxAge = new Date(ssoResponse.expiresAt).getTime() - Date.now();
153
- const refreshMaxAge = ssoResponse.refreshExpiresAt ? new Date(ssoResponse.refreshExpiresAt).getTime() - Date.now() : 7 * 24 * 60 * 60 * 1e3;
154
- const sessionCookieOptions = getCookieOptions({
155
- maxAge: sessionMaxAge > 0 ? sessionMaxAge : 0
156
- });
157
- const refreshCookieOptions = getCookieOptions({
158
- maxAge: refreshMaxAge > 0 ? refreshMaxAge : 0
159
- });
160
- res.cookie(cookieName, ssoResponse.sessionToken, sessionCookieOptions);
161
- if (ssoResponse.refreshToken) {
162
- res.cookie(`${cookieName}_refresh`, ssoResponse.refreshToken, refreshCookieOptions);
163
- }
108
+ const ssoResponse = await options.ssoClient.exchangeCode(code, codeVerifier);
164
109
  if (options.onLoginSuccess) {
165
110
  await options.onLoginSuccess(ssoResponse);
166
111
  }
167
112
  res.json({
168
113
  success: true,
114
+ tokens: ssoResponse.tokens,
169
115
  user: ssoResponse.user,
170
- tenant: ssoResponse.tenant,
171
- expiresAt: ssoResponse.expiresAt
116
+ tenant: ssoResponse.tenant
172
117
  });
173
118
  } catch (error) {
174
- console.error("\u274C [BigsoAuthSDK] Error exchanging code:", error.message);
175
- res.status(500).json({
176
- error: error.message || "Failed to exchange authorization code"
177
- });
119
+ console.error("[BigsoAuthSDK] Error exchanging code:", error.message);
120
+ res.status(401).json({ error: error.message || "Failed to exchange authorization code" });
178
121
  }
179
122
  });
180
123
  router.post("/exchange-v2", async (req, res) => {
@@ -189,40 +132,27 @@ function createSsoAuthRouter(options) {
189
132
  res.status(400).json({ error: "No authorization code found in payload" });
190
133
  return;
191
134
  }
192
- const ssoResponse = await options.ssoClient.exchangeCodeForToken(verified.code);
193
- if (!ssoResponse.success) {
194
- res.status(401).json({ error: "Invalid authorization code" });
135
+ const codeVerifier = verified.code_verifier;
136
+ if (!codeVerifier) {
137
+ res.status(400).json({ error: "code_verifier is required for PKCE exchange" });
195
138
  return;
196
139
  }
197
- const sessionMaxAge = new Date(ssoResponse.expiresAt).getTime() - Date.now();
198
- const refreshMaxAge = ssoResponse.refreshExpiresAt ? new Date(ssoResponse.refreshExpiresAt).getTime() - Date.now() : 7 * 24 * 60 * 60 * 1e3;
199
- const sessionCookieOptions = getCookieOptions({
200
- maxAge: sessionMaxAge > 0 ? sessionMaxAge : 0
201
- });
202
- const refreshCookieOptions = getCookieOptions({
203
- maxAge: refreshMaxAge > 0 ? refreshMaxAge : 0
204
- });
205
- res.cookie(cookieName, ssoResponse.sessionToken, sessionCookieOptions);
206
- if (ssoResponse.refreshToken) {
207
- res.cookie(`${cookieName}_refresh`, ssoResponse.refreshToken, refreshCookieOptions);
208
- }
140
+ const ssoResponse = await options.ssoClient.exchangeCode(verified.code, codeVerifier);
209
141
  if (options.onLoginSuccess) {
210
142
  await options.onLoginSuccess(ssoResponse);
211
143
  }
212
144
  res.json({
213
145
  success: true,
146
+ tokens: ssoResponse.tokens,
214
147
  user: ssoResponse.user,
215
- tenant: ssoResponse.tenant,
216
- expiresAt: ssoResponse.expiresAt
148
+ tenant: ssoResponse.tenant
217
149
  });
218
150
  } catch (error) {
219
- console.error("\u274C [BigsoAuthSDK] Error exchanging v2 payload:", error.message);
220
- res.status(401).json({
221
- error: error.message || "Failed to verify signed payload"
222
- });
151
+ console.error("[BigsoAuthSDK] Error exchanging v2 payload:", error.message);
152
+ res.status(401).json({ error: error.message || "Failed to verify signed payload" });
223
153
  }
224
154
  });
225
- router.get("/session", ssoAuthMiddleware(options), (req, res) => {
155
+ router.get("/session", ssoAuthMiddleware({ ssoClient: options.ssoClient }), (req, res) => {
226
156
  res.set("Cache-Control", "no-store, no-cache, must-revalidate, private");
227
157
  res.set("Pragma", "no-cache");
228
158
  res.set("Expires", "0");
@@ -230,25 +160,34 @@ function createSsoAuthRouter(options) {
230
160
  success: true,
231
161
  user: req.user,
232
162
  tenant: req.tenant,
233
- expiresAt: req.ssoSession?.expiresAt
163
+ tokenPayload: req.tokenPayload
234
164
  });
235
165
  });
236
- router.post("/logout", async (req, res) => {
237
- const sessionToken = req.cookies?.[cookieName];
238
- if (sessionToken) {
239
- try {
240
- await options.ssoClient.revokeSession(sessionToken);
241
- } catch (error) {
242
- console.warn("\u26A0\uFE0F [BigsoAuthSDK] Failed to revoke session in SSO Backend. Clearing local anyway.", error.message);
243
- }
166
+ router.post("/refresh", async (req, res) => {
167
+ try {
168
+ const ssoResponse = await options.ssoClient.refreshTokens();
169
+ res.json({
170
+ success: true,
171
+ tokens: ssoResponse.tokens
172
+ });
173
+ } catch (error) {
174
+ console.error("[BigsoAuthSDK] Error refreshing tokens:", error.message);
175
+ res.status(401).json({ error: error.message || "Failed to refresh tokens" });
244
176
  }
245
- const cookieOptions = getCookieOptions({ maxAge: 0 });
246
- res.clearCookie(cookieName, cookieOptions);
247
- res.clearCookie(`${cookieName}_refresh`, cookieOptions);
248
- if (options.onLogout && sessionToken) {
249
- await options.onLogout(sessionToken);
177
+ });
178
+ router.post("/logout", ssoAuthMiddleware({ ssoClient: options.ssoClient }), async (req, res) => {
179
+ try {
180
+ const accessToken = req.headers.authorization?.substring(7) || "";
181
+ const { revokeAll = false } = req.body || {};
182
+ await options.ssoClient.logout(accessToken, revokeAll);
183
+ if (options.onLogout) {
184
+ await options.onLogout(accessToken);
185
+ }
186
+ res.json({ success: true, message: "Logged out" });
187
+ } catch (error) {
188
+ console.warn("[BigsoAuthSDK] Failed to logout in SSO Backend.", error.message);
189
+ res.json({ success: true, message: "Logged out (backend revocation failed)" });
250
190
  }
251
- res.json({ success: true, message: "Logged out" });
252
191
  });
253
192
  return router;
254
193
  }
@@ -1,20 +1,26 @@
1
1
  import { Request, Response, NextFunction, Router } from 'express';
2
2
  import { BigsoSsoClient } from '../node/index.cjs';
3
- import { S as SsoSessionData } from '../types-CoXgtTry.cjs';
3
+ import { S as SsoTokenPayload, V as V2ExchangeResponse } from '../types-BQzACpj3.cjs';
4
4
 
5
5
  interface SsoAuthMiddlewareOptions {
6
6
  ssoClient: BigsoSsoClient;
7
- cookieName?: string;
8
- cookieDomain?: string;
9
- isProduction?: boolean;
10
- onSessionValidated?: (session: SsoSessionData, req: Request) => Promise<void> | void;
11
7
  }
12
8
  declare global {
13
9
  namespace Express {
14
10
  interface Request {
15
- user?: SsoSessionData['user'];
16
- tenant?: SsoSessionData['tenant'];
17
- ssoSession?: SsoSessionData;
11
+ user?: {
12
+ userId: string;
13
+ email: string;
14
+ firstName: string;
15
+ lastName: string;
16
+ };
17
+ tenant?: {
18
+ tenantId: string;
19
+ name: string;
20
+ slug: string;
21
+ role: string;
22
+ };
23
+ tokenPayload?: SsoTokenPayload;
18
24
  }
19
25
  }
20
26
  }
@@ -29,11 +35,8 @@ declare function ssoSyncGuardMiddleware(options: SsoSyncGuardOptions): (req: Req
29
35
  interface CreateSsoAuthRouterOptions {
30
36
  ssoClient: BigsoSsoClient;
31
37
  frontendUrl: string;
32
- cookieName?: string;
33
- cookieDomain?: string;
34
- isProduction?: boolean;
35
- onLoginSuccess?: (session: SsoSessionData) => void | Promise<void>;
36
- onLogout?: (sessionToken: string) => void | Promise<void>;
38
+ onLoginSuccess?: (session: V2ExchangeResponse) => void | Promise<void>;
39
+ onLogout?: (accessToken: string) => void | Promise<void>;
37
40
  }
38
41
  declare function createSsoAuthRouter(options: CreateSsoAuthRouterOptions): Router;
39
42
 
@@ -1,20 +1,26 @@
1
1
  import { Request, Response, NextFunction, Router } from 'express';
2
2
  import { BigsoSsoClient } from '../node/index.js';
3
- import { S as SsoSessionData } from '../types-CoXgtTry.js';
3
+ import { S as SsoTokenPayload, V as V2ExchangeResponse } from '../types-BQzACpj3.js';
4
4
 
5
5
  interface SsoAuthMiddlewareOptions {
6
6
  ssoClient: BigsoSsoClient;
7
- cookieName?: string;
8
- cookieDomain?: string;
9
- isProduction?: boolean;
10
- onSessionValidated?: (session: SsoSessionData, req: Request) => Promise<void> | void;
11
7
  }
12
8
  declare global {
13
9
  namespace Express {
14
10
  interface Request {
15
- user?: SsoSessionData['user'];
16
- tenant?: SsoSessionData['tenant'];
17
- ssoSession?: SsoSessionData;
11
+ user?: {
12
+ userId: string;
13
+ email: string;
14
+ firstName: string;
15
+ lastName: string;
16
+ };
17
+ tenant?: {
18
+ tenantId: string;
19
+ name: string;
20
+ slug: string;
21
+ role: string;
22
+ };
23
+ tokenPayload?: SsoTokenPayload;
18
24
  }
19
25
  }
20
26
  }
@@ -29,11 +35,8 @@ declare function ssoSyncGuardMiddleware(options: SsoSyncGuardOptions): (req: Req
29
35
  interface CreateSsoAuthRouterOptions {
30
36
  ssoClient: BigsoSsoClient;
31
37
  frontendUrl: string;
32
- cookieName?: string;
33
- cookieDomain?: string;
34
- isProduction?: boolean;
35
- onLoginSuccess?: (session: SsoSessionData) => void | Promise<void>;
36
- onLogout?: (sessionToken: string) => void | Promise<void>;
38
+ onLoginSuccess?: (session: V2ExchangeResponse) => void | Promise<void>;
39
+ onLogout?: (accessToken: string) => void | Promise<void>;
37
40
  }
38
41
  declare function createSsoAuthRouter(options: CreateSsoAuthRouterOptions): Router;
39
42
 
@@ -1,55 +1,31 @@
1
1
  // src/express/middlewares/ssoAuth.ts
2
2
  function ssoAuthMiddleware(options) {
3
- const cookieName = options.cookieName || "sso_session";
4
- const isProduction = options.isProduction ?? process.env.NODE_ENV === "production";
5
3
  return async (req, res, next) => {
6
4
  try {
7
- let sessionToken = req.cookies?.[cookieName];
8
- let session = null;
9
- if (sessionToken) {
10
- session = await options.ssoClient.validateSessionToken(sessionToken);
11
- }
12
- if (!session) {
13
- const refreshToken = req.cookies?.[`${cookieName}_refresh`];
14
- if (refreshToken) {
15
- const newSessionData = await options.ssoClient.refreshAppSession(refreshToken);
16
- if (newSessionData) {
17
- const sessionMaxAge = new Date(newSessionData.expiresAt).getTime() - Date.now();
18
- const refreshMaxAge = newSessionData.refreshExpiresAt ? new Date(newSessionData.refreshExpiresAt).getTime() - Date.now() : 7 * 24 * 60 * 60 * 1e3;
19
- const sessionCookieOptions = {
20
- httpOnly: true,
21
- secure: isProduction,
22
- sameSite: "lax",
23
- path: "/",
24
- maxAge: sessionMaxAge > 0 ? sessionMaxAge : 0,
25
- ...isProduction && options.cookieDomain ? { domain: options.cookieDomain } : {}
26
- };
27
- const refreshCookieOptions = {
28
- ...sessionCookieOptions,
29
- maxAge: refreshMaxAge > 0 ? refreshMaxAge : 0
30
- };
31
- res.cookie(cookieName, newSessionData.sessionToken, sessionCookieOptions);
32
- res.cookie(`${cookieName}_refresh`, newSessionData.refreshToken, refreshCookieOptions);
33
- session = await options.ssoClient.validateSessionToken(newSessionData.sessionToken);
34
- }
35
- }
36
- if (!session) {
37
- res.clearCookie(cookieName);
38
- res.clearCookie(`${cookieName}_refresh`);
39
- res.status(401).json({ error: "Session expired or invalid" });
40
- return;
41
- }
5
+ const authHeader = req.headers.authorization;
6
+ if (!authHeader || !authHeader.startsWith("Bearer ")) {
7
+ res.status(401).json({ error: "Missing access token" });
8
+ return;
42
9
  }
43
- if (options.onSessionValidated) {
44
- await options.onSessionValidated(session, req);
10
+ const accessToken = authHeader.substring(7);
11
+ const payload = await options.ssoClient.validateAccessToken(accessToken);
12
+ if (!payload) {
13
+ res.status(401).json({ error: "Invalid or expired access token" });
14
+ return;
45
15
  }
46
- req.user = session.user;
47
- req.tenant = session.tenant;
48
- req.ssoSession = session;
16
+ const primaryTenant = payload.tenants?.[0];
17
+ req.user = {
18
+ userId: payload.sub,
19
+ email: "",
20
+ firstName: "",
21
+ lastName: ""
22
+ };
23
+ req.tenant = primaryTenant || void 0;
24
+ req.tokenPayload = payload;
49
25
  next();
50
26
  } catch (error) {
51
- console.error("\u274C [BigsoAuthSDK] Authentication Middleware Error:", error instanceof Error ? error.message : error);
52
- res.status(500).json({ error: "Internal authentication error" });
27
+ console.error("[BigsoAuthSDK] Authentication Middleware Error:", error instanceof Error ? error.message : error);
28
+ res.status(401).json({ error: "Authentication failed" });
53
29
  }
54
30
  };
55
31
  }
@@ -93,59 +69,26 @@ function ssoSyncGuardMiddleware(options) {
93
69
  import { Router } from "express";
94
70
  function createSsoAuthRouter(options) {
95
71
  const router = Router();
96
- const cookieName = options.cookieName || "sso_session";
97
- const isProduction = options.isProduction ?? process.env.NODE_ENV === "production";
98
- const getCookieOptions = (customOptions = {}) => {
99
- const base = {
100
- httpOnly: true,
101
- secure: isProduction,
102
- sameSite: "lax",
103
- path: "/",
104
- ...customOptions
105
- };
106
- if (isProduction && options.cookieDomain) {
107
- base.domain = options.cookieDomain;
108
- }
109
- return base;
110
- };
111
72
  router.post("/exchange", async (req, res) => {
112
73
  try {
113
- const { code } = req.body;
114
- if (!code) {
115
- res.status(400).json({ error: "Authorization code is required" });
116
- return;
117
- }
118
- const ssoResponse = await options.ssoClient.exchangeCodeForToken(code);
119
- if (!ssoResponse.success) {
120
- res.status(401).json({ error: "Invalid authorization code" });
74
+ const { code, codeVerifier } = req.body;
75
+ if (!code || !codeVerifier) {
76
+ res.status(400).json({ error: "code and codeVerifier are required" });
121
77
  return;
122
78
  }
123
- const sessionMaxAge = new Date(ssoResponse.expiresAt).getTime() - Date.now();
124
- const refreshMaxAge = ssoResponse.refreshExpiresAt ? new Date(ssoResponse.refreshExpiresAt).getTime() - Date.now() : 7 * 24 * 60 * 60 * 1e3;
125
- const sessionCookieOptions = getCookieOptions({
126
- maxAge: sessionMaxAge > 0 ? sessionMaxAge : 0
127
- });
128
- const refreshCookieOptions = getCookieOptions({
129
- maxAge: refreshMaxAge > 0 ? refreshMaxAge : 0
130
- });
131
- res.cookie(cookieName, ssoResponse.sessionToken, sessionCookieOptions);
132
- if (ssoResponse.refreshToken) {
133
- res.cookie(`${cookieName}_refresh`, ssoResponse.refreshToken, refreshCookieOptions);
134
- }
79
+ const ssoResponse = await options.ssoClient.exchangeCode(code, codeVerifier);
135
80
  if (options.onLoginSuccess) {
136
81
  await options.onLoginSuccess(ssoResponse);
137
82
  }
138
83
  res.json({
139
84
  success: true,
85
+ tokens: ssoResponse.tokens,
140
86
  user: ssoResponse.user,
141
- tenant: ssoResponse.tenant,
142
- expiresAt: ssoResponse.expiresAt
87
+ tenant: ssoResponse.tenant
143
88
  });
144
89
  } catch (error) {
145
- console.error("\u274C [BigsoAuthSDK] Error exchanging code:", error.message);
146
- res.status(500).json({
147
- error: error.message || "Failed to exchange authorization code"
148
- });
90
+ console.error("[BigsoAuthSDK] Error exchanging code:", error.message);
91
+ res.status(401).json({ error: error.message || "Failed to exchange authorization code" });
149
92
  }
150
93
  });
151
94
  router.post("/exchange-v2", async (req, res) => {
@@ -160,40 +103,27 @@ function createSsoAuthRouter(options) {
160
103
  res.status(400).json({ error: "No authorization code found in payload" });
161
104
  return;
162
105
  }
163
- const ssoResponse = await options.ssoClient.exchangeCodeForToken(verified.code);
164
- if (!ssoResponse.success) {
165
- res.status(401).json({ error: "Invalid authorization code" });
106
+ const codeVerifier = verified.code_verifier;
107
+ if (!codeVerifier) {
108
+ res.status(400).json({ error: "code_verifier is required for PKCE exchange" });
166
109
  return;
167
110
  }
168
- const sessionMaxAge = new Date(ssoResponse.expiresAt).getTime() - Date.now();
169
- const refreshMaxAge = ssoResponse.refreshExpiresAt ? new Date(ssoResponse.refreshExpiresAt).getTime() - Date.now() : 7 * 24 * 60 * 60 * 1e3;
170
- const sessionCookieOptions = getCookieOptions({
171
- maxAge: sessionMaxAge > 0 ? sessionMaxAge : 0
172
- });
173
- const refreshCookieOptions = getCookieOptions({
174
- maxAge: refreshMaxAge > 0 ? refreshMaxAge : 0
175
- });
176
- res.cookie(cookieName, ssoResponse.sessionToken, sessionCookieOptions);
177
- if (ssoResponse.refreshToken) {
178
- res.cookie(`${cookieName}_refresh`, ssoResponse.refreshToken, refreshCookieOptions);
179
- }
111
+ const ssoResponse = await options.ssoClient.exchangeCode(verified.code, codeVerifier);
180
112
  if (options.onLoginSuccess) {
181
113
  await options.onLoginSuccess(ssoResponse);
182
114
  }
183
115
  res.json({
184
116
  success: true,
117
+ tokens: ssoResponse.tokens,
185
118
  user: ssoResponse.user,
186
- tenant: ssoResponse.tenant,
187
- expiresAt: ssoResponse.expiresAt
119
+ tenant: ssoResponse.tenant
188
120
  });
189
121
  } catch (error) {
190
- console.error("\u274C [BigsoAuthSDK] Error exchanging v2 payload:", error.message);
191
- res.status(401).json({
192
- error: error.message || "Failed to verify signed payload"
193
- });
122
+ console.error("[BigsoAuthSDK] Error exchanging v2 payload:", error.message);
123
+ res.status(401).json({ error: error.message || "Failed to verify signed payload" });
194
124
  }
195
125
  });
196
- router.get("/session", ssoAuthMiddleware(options), (req, res) => {
126
+ router.get("/session", ssoAuthMiddleware({ ssoClient: options.ssoClient }), (req, res) => {
197
127
  res.set("Cache-Control", "no-store, no-cache, must-revalidate, private");
198
128
  res.set("Pragma", "no-cache");
199
129
  res.set("Expires", "0");
@@ -201,25 +131,34 @@ function createSsoAuthRouter(options) {
201
131
  success: true,
202
132
  user: req.user,
203
133
  tenant: req.tenant,
204
- expiresAt: req.ssoSession?.expiresAt
134
+ tokenPayload: req.tokenPayload
205
135
  });
206
136
  });
207
- router.post("/logout", async (req, res) => {
208
- const sessionToken = req.cookies?.[cookieName];
209
- if (sessionToken) {
210
- try {
211
- await options.ssoClient.revokeSession(sessionToken);
212
- } catch (error) {
213
- console.warn("\u26A0\uFE0F [BigsoAuthSDK] Failed to revoke session in SSO Backend. Clearing local anyway.", error.message);
214
- }
137
+ router.post("/refresh", async (req, res) => {
138
+ try {
139
+ const ssoResponse = await options.ssoClient.refreshTokens();
140
+ res.json({
141
+ success: true,
142
+ tokens: ssoResponse.tokens
143
+ });
144
+ } catch (error) {
145
+ console.error("[BigsoAuthSDK] Error refreshing tokens:", error.message);
146
+ res.status(401).json({ error: error.message || "Failed to refresh tokens" });
215
147
  }
216
- const cookieOptions = getCookieOptions({ maxAge: 0 });
217
- res.clearCookie(cookieName, cookieOptions);
218
- res.clearCookie(`${cookieName}_refresh`, cookieOptions);
219
- if (options.onLogout && sessionToken) {
220
- await options.onLogout(sessionToken);
148
+ });
149
+ router.post("/logout", ssoAuthMiddleware({ ssoClient: options.ssoClient }), async (req, res) => {
150
+ try {
151
+ const accessToken = req.headers.authorization?.substring(7) || "";
152
+ const { revokeAll = false } = req.body || {};
153
+ await options.ssoClient.logout(accessToken, revokeAll);
154
+ if (options.onLogout) {
155
+ await options.onLogout(accessToken);
156
+ }
157
+ res.json({ success: true, message: "Logged out" });
158
+ } catch (error) {
159
+ console.warn("[BigsoAuthSDK] Failed to logout in SSO Backend.", error.message);
160
+ res.json({ success: true, message: "Logged out (backend revocation failed)" });
221
161
  }
222
- res.json({ success: true, message: "Logged out" });
223
162
  });
224
163
  return router;
225
164
  }