@asgardeo/nextjs 0.3.9 → 0.3.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/middleware.js +45 -0
- package/dist/cjs/middleware.js.map +7 -0
- package/dist/cjs/server/index.js +2 -10
- package/dist/cjs/server/index.js.map +2 -2
- package/dist/esm/client/contexts/Asgardeo/AsgardeoContext.js +2 -0
- package/dist/esm/client/contexts/Asgardeo/AsgardeoContext.js.map +1 -1
- package/dist/esm/client/contexts/Asgardeo/AsgardeoProvider.js +3 -1
- package/dist/esm/client/contexts/Asgardeo/AsgardeoProvider.js.map +1 -1
- package/dist/esm/constants/sessionConstants.js +49 -0
- package/dist/esm/constants/sessionConstants.js.map +1 -0
- package/dist/esm/middleware.js +35 -0
- package/dist/esm/middleware.js.map +1 -0
- package/dist/esm/server/AsgardeoProvider.js +4 -2
- package/dist/esm/server/AsgardeoProvider.js.map +1 -1
- package/dist/esm/server/actions/clearSession.js +49 -0
- package/dist/esm/server/actions/clearSession.js.map +1 -0
- package/dist/esm/server/actions/getAccessToken.js.map +1 -1
- package/dist/esm/server/actions/getSessionId.js.map +1 -1
- package/dist/esm/server/actions/getSessionPayload.js.map +1 -1
- package/dist/esm/server/actions/handleOAuthCallbackAction.js +6 -3
- package/dist/esm/server/actions/handleOAuthCallbackAction.js.map +1 -1
- package/dist/esm/server/actions/isSignedIn.js +11 -15
- package/dist/esm/server/actions/isSignedIn.js.map +1 -1
- package/dist/esm/server/actions/refreshToken.js +86 -0
- package/dist/esm/server/actions/refreshToken.js.map +1 -0
- package/dist/esm/server/actions/signInAction.js +13 -5
- package/dist/esm/server/actions/signInAction.js.map +1 -1
- package/dist/esm/server/actions/signOutAction.js.map +1 -1
- package/dist/esm/server/actions/switchOrganization.js +8 -9
- package/dist/esm/server/actions/switchOrganization.js.map +1 -1
- package/dist/esm/server/index.js +0 -3
- package/dist/esm/server/index.js.map +1 -1
- package/dist/esm/server/middleware/asgardeoMiddleware.js +182 -85
- package/dist/esm/server/middleware/asgardeoMiddleware.js.map +1 -1
- package/dist/esm/utils/SessionManager.js +54 -7
- package/dist/esm/utils/SessionManager.js.map +1 -1
- package/dist/esm/utils/decorateConfigWithNextEnv.js +5 -1
- package/dist/esm/utils/decorateConfigWithNextEnv.js.map +1 -1
- package/dist/esm/utils/handleRefreshToken.js +82 -0
- package/dist/esm/utils/handleRefreshToken.js.map +1 -0
- package/dist/types/client/contexts/Asgardeo/AsgardeoContext.d.ts +5 -1
- package/dist/types/client/contexts/Asgardeo/AsgardeoProvider.d.ts +3 -0
- package/dist/types/constants/sessionConstants.d.ts +48 -0
- package/dist/types/middleware.d.ts +34 -0
- package/dist/types/server/AsgardeoProvider.d.ts +14 -0
- package/dist/types/server/actions/clearSession.d.ts +39 -0
- package/dist/types/server/actions/isSignedIn.d.ts +8 -3
- package/dist/types/server/actions/refreshToken.d.ts +44 -0
- package/dist/types/server/index.d.ts +0 -3
- package/dist/types/server/middleware/asgardeoMiddleware.d.ts +20 -28
- package/dist/types/utils/SessionManager.d.ts +26 -5
- package/dist/types/utils/handleRefreshToken.d.ts +46 -0
- package/package.json +9 -3
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
import { generateSessionId, EmbeddedSignInFlowStatus, isEmpty, } from '@asgardeo/node';
|
|
20
20
|
import { cookies } from 'next/headers';
|
|
21
21
|
import AsgardeoNextClient from '../../AsgardeoNextClient';
|
|
22
|
+
import logger from '../../utils/logger';
|
|
22
23
|
import SessionManager from '../../utils/SessionManager';
|
|
23
24
|
/**
|
|
24
25
|
* Server action for signing in a user.
|
|
@@ -74,13 +75,21 @@ const signInAction = async (payload, request) => {
|
|
|
74
75
|
state: response?.authData?.state,
|
|
75
76
|
}, {}, sessionId);
|
|
76
77
|
if (signInResult) {
|
|
77
|
-
const idToken = await client.getDecodedIdToken(sessionId);
|
|
78
|
+
const idToken = await client.getDecodedIdToken(sessionId, (signInResult['idToken'] || signInResult['id_token']));
|
|
78
79
|
const userIdFromToken = (idToken['sub'] || signInResult['sub'] || sessionId);
|
|
79
80
|
const { accessToken } = signInResult;
|
|
81
|
+
const refreshToken = signInResult['refreshToken'] ?? '';
|
|
80
82
|
const scopes = signInResult['scope'];
|
|
81
83
|
const organizationId = (idToken['user_org'] || idToken['organization_id']);
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
+
const rawExpiresIn = signInResult['expiresIn'] ?? signInResult['expires_in'];
|
|
85
|
+
const expiresIn = Number(rawExpiresIn);
|
|
86
|
+
if (Number.isNaN(expiresIn)) {
|
|
87
|
+
throw new Error(`[signInAction] Invalid expiresIn value received: ${rawExpiresIn}`);
|
|
88
|
+
}
|
|
89
|
+
const config = await client.getConfiguration();
|
|
90
|
+
const sessionCookieExpiryTime = SessionManager.resolveSessionCookieExpiry(config.sessionCookieExpiryTime);
|
|
91
|
+
const sessionToken = await SessionManager.createSessionToken(accessToken, userIdFromToken, sessionId, scopes, expiresIn, refreshToken, organizationId);
|
|
92
|
+
cookieStore.set(SessionManager.getSessionCookieName(), sessionToken, SessionManager.getSessionCookieOptions(sessionCookieExpiryTime));
|
|
84
93
|
cookieStore.delete(SessionManager.getTempSessionCookieName());
|
|
85
94
|
}
|
|
86
95
|
const afterSignInUrl = await (await client.getStorageManager()).getConfigDataParameter('afterSignInUrl');
|
|
@@ -89,8 +98,7 @@ const signInAction = async (payload, request) => {
|
|
|
89
98
|
return { data: response, success: true };
|
|
90
99
|
}
|
|
91
100
|
catch (error) {
|
|
92
|
-
|
|
93
|
-
console.error('[signInAction] Error during sign-in:', error);
|
|
101
|
+
logger.error(`[signInAction] Error during sign-in: ${error instanceof Error ? error.message : String(error)}`);
|
|
94
102
|
return { error: String(error), success: false };
|
|
95
103
|
}
|
|
96
104
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signInAction.js","sourceRoot":"","sources":["../../../../src/server/actions/signInAction.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,YAAY,CAAC;AAEb,OAAO,EACL,iBAAiB,EACjB,wBAAwB,EAKxB,OAAO,GACR,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"signInAction.js","sourceRoot":"","sources":["../../../../src/server/actions/signInAction.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signOutAction.js","sourceRoot":"","sources":["../../../../src/server/actions/signOutAction.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"signOutAction.js","sourceRoot":"","sources":["../../../../src/server/actions/signOutAction.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,YAAY,CAAC;AAEb,OAAO,EAAC,OAAO,EAAC,MAAM,cAAc,CAAC;AACrC,OAAO,YAAY,MAAM,gBAAgB,CAAC;AAC1C,OAAO,kBAAkB,MAAM,0BAA0B,CAAC;AAC1D,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,cAAc,MAAM,4BAA4B,CAAC;AAIxD;;;;;GAKG;AACH,MAAM,aAAa,GAAG,KAAK,IAAqF,EAAE;IAChH,MAAM,CAAC,KAAK,CAAC,qEAAqE,CAAC,CAAC;IAEpF,MAAM,mBAAmB,GAAG,KAAK,IAAmB,EAAE;QACpD,MAAM,WAAW,GAAmB,MAAM,OAAO,EAAE,CAAC;QAEpD,WAAW,CAAC,MAAM,CAAC,cAAc,CAAC,oBAAoB,EAAE,CAAC,CAAC;QAC1D,WAAW,CAAC,MAAM,CAAC,cAAc,CAAC,wBAAwB,EAAE,CAAC,CAAC;IAChE,CAAC,CAAC;IAEF,IAAI,CAAC;QACH,MAAM,MAAM,GAAuB,kBAAkB,CAAC,WAAW,EAAE,CAAC;QACpE,MAAM,SAAS,GAAuB,MAAM,YAAY,EAAE,CAAC;QAE3D,IAAI,eAAe,GAAW,GAAG,CAAC;QAElC,IAAI,SAAS,EAAE,CAAC;YACd,MAAM,CAAC,KAAK,CAAC,2FAA2F,CAAC,CAAC;YAE1G,eAAe,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;QACxD,CAAC;QAED,MAAM,mBAAmB,EAAE,CAAC;QAE5B,OAAO,EAAC,IAAI,EAAE,EAAC,eAAe,EAAC,EAAE,OAAO,EAAE,IAAI,EAAC,CAAC;IAClD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CAAC,+DAA+D,EAAE,KAAK,CAAC,CAAC;QAErF,MAAM,CAAC,KAAK,CAAC,sEAAsE,CAAC,CAAC;QAErF,MAAM,mBAAmB,EAAE,CAAC;QAE5B,IAAI,YAAqB,CAAC;QAC1B,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,YAAY,GAAG,KAAK,CAAC;QACvB,CAAC;aAAM,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;YAClC,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC;QAC/B,CAAC;aAAM,CAAC;YACN,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACvC,CAAC;QAED,OAAO;YACL,KAAK,EAAE,YAAY;YACnB,OAAO,EAAE,KAAK;SACf,CAAC;IACJ,CAAC;AACH,CAAC,CAAC;AAEF,eAAe,aAAa,CAAC"}
|
|
@@ -31,20 +31,19 @@ const switchOrganization = async (organization, sessionId) => {
|
|
|
31
31
|
const client = AsgardeoNextClient.getInstance();
|
|
32
32
|
const resolvedSessionId = sessionId ?? (await getSessionId());
|
|
33
33
|
const response = await client.switchOrganization(organization, resolvedSessionId);
|
|
34
|
-
// After switching organization, we need to refresh the page to get updated session data
|
|
35
|
-
// This is because server components don't maintain state between function calls
|
|
36
34
|
const { revalidatePath } = await import('next/cache');
|
|
37
|
-
// Revalidate the current path to refresh the component with new data
|
|
38
35
|
revalidatePath('/');
|
|
39
|
-
if (response) {
|
|
40
|
-
const
|
|
36
|
+
if (response && response.accessToken) {
|
|
37
|
+
const tokenResponse = response;
|
|
38
|
+
const idToken = await client.getDecodedIdToken(resolvedSessionId, tokenResponse.idToken);
|
|
41
39
|
const userIdFromToken = idToken['sub'];
|
|
42
|
-
const { accessToken } = response;
|
|
43
|
-
const scopes = response.scope;
|
|
44
40
|
const organizationId = (idToken['user_org'] || idToken['organization_id']);
|
|
45
|
-
const
|
|
41
|
+
const config = await client.getConfiguration();
|
|
42
|
+
const sessionCookieExpiryTime = SessionManager.resolveSessionCookieExpiry(config.sessionCookieExpiryTime);
|
|
43
|
+
const expiresIn = parseInt(tokenResponse.expiresIn, 10);
|
|
44
|
+
const sessionToken = await SessionManager.createSessionToken(tokenResponse.accessToken, userIdFromToken, resolvedSessionId, tokenResponse.scope, expiresIn, tokenResponse.refreshToken ?? '', organizationId);
|
|
46
45
|
logger.debug('[switchOrganization] Session token created successfully.');
|
|
47
|
-
cookieStore.set(SessionManager.getSessionCookieName(), sessionToken, SessionManager.getSessionCookieOptions());
|
|
46
|
+
cookieStore.set(SessionManager.getSessionCookieName(), sessionToken, SessionManager.getSessionCookieOptions(sessionCookieExpiryTime));
|
|
48
47
|
}
|
|
49
48
|
return response;
|
|
50
49
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"switchOrganization.js","sourceRoot":"","sources":["../../../../src/server/actions/switchOrganization.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,YAAY,CAAC;AAEb,OAAO,EAAe,gBAAgB,EAAyB,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"switchOrganization.js","sourceRoot":"","sources":["../../../../src/server/actions/switchOrganization.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,YAAY,CAAC;AAEb,OAAO,EAAe,gBAAgB,EAAyB,MAAM,gBAAgB,CAAC;AACtF,OAAO,EAAC,OAAO,EAAC,MAAM,cAAc,CAAC;AACrC,OAAO,YAAY,MAAM,gBAAgB,CAAC;AAC1C,OAAO,kBAAkB,MAAM,0BAA0B,CAAC;AAE1D,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,cAAc,MAAM,4BAA4B,CAAC;AAIxD;;GAEG;AACH,MAAM,kBAAkB,GAAG,KAAK,EAC9B,YAA0B,EAC1B,SAA6B,EACM,EAAE;IACrC,IAAI,CAAC;QACH,MAAM,WAAW,GAAmB,MAAM,OAAO,EAAE,CAAC;QACpD,MAAM,MAAM,GAAuB,kBAAkB,CAAC,WAAW,EAAE,CAAC;QACpE,MAAM,iBAAiB,GAAW,SAAS,IAAK,CAAC,MAAM,YAAY,EAAE,CAAY,CAAC;QAClF,MAAM,QAAQ,GAA6B,MAAM,MAAM,CAAC,kBAAkB,CAAC,YAAY,EAAE,iBAAiB,CAAC,CAAC;QAE5G,MAAM,EAAC,cAAc,EAAC,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;QACpD,cAAc,CAAC,GAAG,CAAC,CAAC;QAEpB,IAAI,QAAQ,IAAK,QAA0B,CAAC,WAAW,EAAE,CAAC;YACxD,MAAM,aAAa,GAAkB,QAAyB,CAAC;YAC/D,MAAM,OAAO,GAAY,MAAM,MAAM,CAAC,iBAAiB,CAAC,iBAAiB,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;YAClG,MAAM,eAAe,GAAW,OAAO,CAAC,KAAK,CAAW,CAAC;YACzD,MAAM,cAAc,GAAuB,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAEhF,CAAC;YACd,MAAM,MAAM,GAAuB,MAAM,MAAM,CAAC,gBAAgB,EAAE,CAAC;YACnE,MAAM,uBAAuB,GAAW,cAAc,CAAC,0BAA0B,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC;YAClH,MAAM,SAAS,GAAW,QAAQ,CAAC,aAAa,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;YAEhE,MAAM,YAAY,GAAW,MAAM,cAAc,CAAC,kBAAkB,CAClE,aAAa,CAAC,WAAW,EACzB,eAAe,EACf,iBAAiB,EACjB,aAAa,CAAC,KAAK,EACnB,SAAS,EACT,aAAa,CAAC,YAAY,IAAI,EAAE,EAChC,cAAc,CACf,CAAC;YAEF,MAAM,CAAC,KAAK,CAAC,0DAA0D,CAAC,CAAC;YAEzE,WAAW,CAAC,GAAG,CACb,cAAc,CAAC,oBAAoB,EAAE,EACrC,YAAY,EACZ,cAAc,CAAC,uBAAuB,CAAC,uBAAuB,CAAC,CAChE,CAAC;QACJ,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,gBAAgB,CACxB,uCAAuC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,EAC/G,0CAA0C,EAC1C,QAAQ,EACR,KAAK,YAAY,gBAAgB,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CACjE,CAAC;IACJ,CAAC;AACH,CAAC,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
|
package/dist/esm/server/index.js
CHANGED
|
@@ -19,7 +19,4 @@
|
|
|
19
19
|
export { default as asgardeo } from './asgardeo';
|
|
20
20
|
export { default as AsgardeoProvider } from './AsgardeoProvider.js';
|
|
21
21
|
export * from './AsgardeoProvider.js';
|
|
22
|
-
export { default as asgardeoMiddleware } from './middleware/asgardeoMiddleware';
|
|
23
|
-
export * from './middleware/asgardeoMiddleware';
|
|
24
|
-
export { default as createRouteMatcher } from './middleware/createRouteMatcher';
|
|
25
22
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/server/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,mCAAmC;AAEnC,OAAO,EAAC,OAAO,IAAI,QAAQ,EAAC,MAAM,YAAY,CAAC;AAE/C,OAAO,EAAC,OAAO,IAAI,gBAAgB,EAAC,MAAM,uBAAuB,CAAC;AAClE,cAAc,uBAAuB,CAAC
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/server/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,mCAAmC;AAEnC,OAAO,EAAC,OAAO,IAAI,QAAQ,EAAC,MAAM,YAAY,CAAC;AAE/C,OAAO,EAAC,OAAO,IAAI,gBAAgB,EAAC,MAAM,uBAAuB,CAAC;AAClE,cAAc,uBAAuB,CAAC"}
|
|
@@ -16,44 +16,79 @@
|
|
|
16
16
|
* under the License.
|
|
17
17
|
*/
|
|
18
18
|
import { NextResponse } from 'next/server';
|
|
19
|
+
import { REFRESH_BUFFER_SECONDS } from '../../constants/sessionConstants';
|
|
20
|
+
import decorateConfigWithNextEnv from '../../utils/decorateConfigWithNextEnv';
|
|
21
|
+
import handleRefreshToken from '../../utils/handleRefreshToken';
|
|
19
22
|
import SessionManager from '../../utils/SessionManager';
|
|
20
|
-
import {
|
|
23
|
+
import { getSessionFromRequest, getSessionIdFromRequest } from '../../utils/sessionUtils';
|
|
21
24
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* @param request - The Next.js request object
|
|
25
|
-
* @returns True if a valid session exists, false otherwise
|
|
25
|
+
* Removes a named cookie from a raw Cookie header string.
|
|
26
26
|
*/
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
27
|
+
const removeCookieFromHeader = (cookieHeader, name) => cookieHeader
|
|
28
|
+
.split(';')
|
|
29
|
+
.map((p) => p.trim())
|
|
30
|
+
.filter((p) => {
|
|
31
|
+
const eqIdx = p.indexOf('=');
|
|
32
|
+
const partName = eqIdx === -1 ? p : p.slice(0, eqIdx).trim();
|
|
33
|
+
return partName !== name;
|
|
34
|
+
})
|
|
35
|
+
.join('; ');
|
|
35
36
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* @param request - The Next.js request object
|
|
40
|
-
* @returns The session ID if it exists, undefined otherwise
|
|
37
|
+
* Replaces the value of a named cookie inside a raw Cookie header string.
|
|
38
|
+
* If the cookie does not already appear in the header it is appended.
|
|
41
39
|
*/
|
|
42
|
-
const
|
|
40
|
+
const replaceCookieInHeader = (cookieHeader, name, value) => {
|
|
41
|
+
const parts = cookieHeader
|
|
42
|
+
.split(';')
|
|
43
|
+
.map((p) => p.trim())
|
|
44
|
+
.filter(Boolean);
|
|
45
|
+
let found = false;
|
|
46
|
+
const updated = parts.map((part) => {
|
|
47
|
+
const eqIdx = part.indexOf('=');
|
|
48
|
+
const partName = eqIdx === -1 ? part : part.slice(0, eqIdx).trim();
|
|
49
|
+
if (partName === name) {
|
|
50
|
+
found = true;
|
|
51
|
+
return `${name}=${value}`;
|
|
52
|
+
}
|
|
53
|
+
return part;
|
|
54
|
+
});
|
|
55
|
+
if (!found) {
|
|
56
|
+
updated.push(`${name}=${value}`);
|
|
57
|
+
}
|
|
58
|
+
return updated.join('; ');
|
|
59
|
+
};
|
|
43
60
|
/**
|
|
44
61
|
* Asgardeo middleware that integrates authentication into your Next.js application.
|
|
45
62
|
* Similar to Clerk's clerkMiddleware pattern.
|
|
46
63
|
*
|
|
64
|
+
* Proactively refreshes the access token when it is within REFRESH_BUFFER_SECONDS of
|
|
65
|
+
* expiry so that Server Components always receive a fresh session. The refresh also
|
|
66
|
+
* recovers expired tokens as long as a refresh token is present.
|
|
67
|
+
*
|
|
68
|
+
* The updated session cookie is written to:
|
|
69
|
+
* - The response → browser stores the new cookie for subsequent requests.
|
|
70
|
+
* - The forwarded request headers → the same-request Server Component render sees
|
|
71
|
+
* the fresh token immediately without waiting for the next navigation.
|
|
72
|
+
*
|
|
73
|
+
* Token refresh requires baseUrl, clientId, and clientSecret. These are resolved from
|
|
74
|
+
* the options argument first, then from the standard Asgardeo environment variables
|
|
75
|
+
* (NEXT_PUBLIC_ASGARDEO_BASE_URL, NEXT_PUBLIC_ASGARDEO_CLIENT_ID,
|
|
76
|
+
* ASGARDEO_CLIENT_SECRET). If none are available the refresh step is skipped silently.
|
|
77
|
+
*
|
|
47
78
|
* @param handler - Optional handler function to customize middleware behavior
|
|
48
79
|
* @param options - Configuration options for the middleware
|
|
49
80
|
* @returns Next.js middleware function
|
|
50
81
|
*
|
|
51
82
|
* @example
|
|
52
83
|
* ```typescript
|
|
53
|
-
* // middleware.ts - Basic usage
|
|
84
|
+
* // middleware.ts - Basic usage (config read from env vars automatically)
|
|
54
85
|
* import { asgardeoMiddleware } from '@asgardeo/nextjs';
|
|
55
86
|
*
|
|
56
87
|
* export default asgardeoMiddleware();
|
|
88
|
+
*
|
|
89
|
+
* export const config = {
|
|
90
|
+
* matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
|
|
91
|
+
* };
|
|
57
92
|
* ```
|
|
58
93
|
*
|
|
59
94
|
* @example
|
|
@@ -69,82 +104,111 @@ const getSessionIdFromRequestMiddleware = async (request) => getSessionIdFromReq
|
|
|
69
104
|
* }
|
|
70
105
|
* });
|
|
71
106
|
* ```
|
|
72
|
-
*
|
|
73
|
-
* @example
|
|
74
|
-
* ```typescript
|
|
75
|
-
* // Advanced usage with custom logic
|
|
76
|
-
* import { asgardeoMiddleware, createRouteMatcher } from '@asgardeo/nextjs';
|
|
77
|
-
*
|
|
78
|
-
* const isProtectedRoute = createRouteMatcher(['/dashboard(.*)']);
|
|
79
|
-
* const isAuthRoute = createRouteMatcher(['/sign-in', '/sign-up']);
|
|
80
|
-
*
|
|
81
|
-
* export default asgardeoMiddleware(async (asgardeo, req) => {
|
|
82
|
-
* // Skip protection for auth routes
|
|
83
|
-
* if (isAuthRoute(req)) return;
|
|
84
|
-
*
|
|
85
|
-
* // Protect specified routes
|
|
86
|
-
* if (isProtectedRoute(req)) {
|
|
87
|
-
* await asgardeo.protectRoute({ redirect: '/sign-in' });
|
|
88
|
-
* }
|
|
89
|
-
*
|
|
90
|
-
* // Check authentication status
|
|
91
|
-
* if (asgardeo.isSignedIn()) {
|
|
92
|
-
* console.log('User is authenticated with session:', asgardeo.getSessionId());
|
|
93
|
-
* }
|
|
94
|
-
* }, {
|
|
95
|
-
* defaultRedirect: '/sign-in'
|
|
96
|
-
* });
|
|
97
|
-
* ```
|
|
98
107
|
*/
|
|
99
108
|
const asgardeoMiddleware = (handler, options) => async (request) => {
|
|
100
109
|
const resolvedOptions = typeof options === 'function' ? options(request) : options || {};
|
|
110
|
+
// Resolve full config from passed options + environment variable fallbacks.
|
|
111
|
+
const resolvedConfig = decorateConfigWithNextEnv(resolvedOptions);
|
|
112
|
+
// ── OAuth callback detection ──────────────────────────────────────────────
|
|
101
113
|
const url = new URL(request.url);
|
|
102
114
|
const hasCallbackParams = url.searchParams.has('code') && url.searchParams.has('state');
|
|
103
115
|
let isValidOAuthCallback = false;
|
|
104
|
-
if (hasCallbackParams) {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
await SessionManager.verifyTempSession(tempSessionToken);
|
|
114
|
-
isValidOAuthCallback = true;
|
|
115
|
-
}
|
|
116
|
-
catch {
|
|
117
|
-
// Invalid temp session - this is not a legitimate OAuth callback
|
|
118
|
-
isValidOAuthCallback = false;
|
|
119
|
-
}
|
|
116
|
+
if (hasCallbackParams && !url.searchParams.has('error')) {
|
|
117
|
+
const tempSessionToken = request.cookies.get(SessionManager.getTempSessionCookieName())?.value;
|
|
118
|
+
if (tempSessionToken) {
|
|
119
|
+
try {
|
|
120
|
+
await SessionManager.verifyTempSession(tempSessionToken);
|
|
121
|
+
isValidOAuthCallback = true;
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
isValidOAuthCallback = false;
|
|
120
125
|
}
|
|
121
126
|
}
|
|
122
127
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const
|
|
126
|
-
|
|
128
|
+
// ── Session resolution ────────────────────────────────────────────────────
|
|
129
|
+
// Step 1: Attempt to get a fully verified (signature + expiry) session.
|
|
130
|
+
const verifiedSession = await getSessionFromRequest(request);
|
|
131
|
+
// Step 2: If no verified session exists, verify the raw cookie's signature
|
|
132
|
+
// without enforcing expiry. This allows the middleware to recover from an
|
|
133
|
+
// expired access token as long as the JWT is authentic and a refresh token
|
|
134
|
+
// is present. Skipping the signature check here would let a tampered cookie
|
|
135
|
+
// drive identity-confusion attacks since handleRefreshToken reuses `sub`,
|
|
136
|
+
// `sessionId`, and `organizationId` from the input payload when minting the
|
|
137
|
+
// new session JWT.
|
|
138
|
+
let expiredSession;
|
|
139
|
+
if (!verifiedSession) {
|
|
140
|
+
const rawToken = request.cookies.get(SessionManager.getSessionCookieName())?.value;
|
|
141
|
+
if (rawToken) {
|
|
127
142
|
try {
|
|
128
|
-
|
|
143
|
+
const decoded = await SessionManager.verifySessionTokenForRefresh(rawToken);
|
|
144
|
+
if (decoded.refreshToken) {
|
|
145
|
+
expiredSession = decoded;
|
|
146
|
+
}
|
|
129
147
|
}
|
|
130
148
|
catch {
|
|
131
|
-
|
|
149
|
+
// Forged, tampered, wrong type, or malformed — ignore.
|
|
132
150
|
}
|
|
133
|
-
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// ── Token refresh ─────────────────────────────────────────────────────────
|
|
154
|
+
const now = Math.floor(Date.now() / 1000);
|
|
155
|
+
const candidateSession = verifiedSession ?? expiredSession;
|
|
156
|
+
// Config is required to call the token endpoint.
|
|
157
|
+
const hasRefreshConfig = !!(resolvedConfig.baseUrl &&
|
|
158
|
+
resolvedConfig.clientId &&
|
|
159
|
+
resolvedConfig.clientSecret);
|
|
160
|
+
// Refresh when:
|
|
161
|
+
// a) Token is verified but within the proactive buffer window, OR
|
|
162
|
+
// b) Token has already expired but a refresh token is available.
|
|
163
|
+
const needsRefresh = !isValidOAuthCallback &&
|
|
164
|
+
hasRefreshConfig &&
|
|
165
|
+
!!candidateSession?.refreshToken &&
|
|
166
|
+
((!!verifiedSession && verifiedSession.exp <= now + REFRESH_BUFFER_SECONDS) || !!expiredSession);
|
|
167
|
+
let activeSession = verifiedSession;
|
|
168
|
+
let refreshCookieUpdate;
|
|
169
|
+
if (needsRefresh && candidateSession) {
|
|
170
|
+
try {
|
|
171
|
+
const { newSessionToken, sessionCookieExpiryTime } = await handleRefreshToken(candidateSession, {
|
|
172
|
+
baseUrl: resolvedConfig.baseUrl,
|
|
173
|
+
clientId: resolvedConfig.clientId,
|
|
174
|
+
clientSecret: resolvedConfig.clientSecret,
|
|
175
|
+
sessionCookieExpiryTime: resolvedConfig.sessionCookieExpiryTime,
|
|
176
|
+
});
|
|
177
|
+
// Verify the newly minted token so activeSession reflects fresh claims.
|
|
178
|
+
activeSession = await SessionManager.verifySessionToken(newSessionToken);
|
|
179
|
+
refreshCookieUpdate = { expiry: sessionCookieExpiryTime, token: newSessionToken };
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
// Refresh failed — clear the irrecoverable session.
|
|
183
|
+
activeSession = undefined;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
// ── Session cleanup detection ─────────────────────────────────────────────
|
|
187
|
+
// Mark stale cookies for deletion when the session is irrecoverable. Skipped
|
|
188
|
+
// during OAuth callbacks where a session cookie may not exist yet.
|
|
189
|
+
const rawSessionCookie = request.cookies.get(SessionManager.getSessionCookieName())?.value;
|
|
190
|
+
let shouldClearCookie = false;
|
|
191
|
+
if (!isValidOAuthCallback && rawSessionCookie && !activeSession && !refreshCookieUpdate) {
|
|
192
|
+
// A cookie was present but all resolution paths (verify, decode, refresh)
|
|
193
|
+
// failed — the session is dead and cannot be recovered.
|
|
194
|
+
shouldClearCookie = true;
|
|
195
|
+
}
|
|
196
|
+
const sessionId = activeSession?.sessionId ?? (await getSessionIdFromRequest(request));
|
|
197
|
+
const isAuthenticated = !!activeSession;
|
|
198
|
+
// ── Middleware context ────────────────────────────────────────────────────
|
|
199
|
+
const asgardeo = {
|
|
200
|
+
getSession: async () => activeSession,
|
|
134
201
|
getSessionId: () => sessionId,
|
|
135
202
|
isSignedIn: () => isAuthenticated,
|
|
136
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
137
203
|
protectRoute: async (routeOptions) => {
|
|
138
|
-
// Skip
|
|
139
|
-
//
|
|
204
|
+
// Skip during a valid OAuth callback to avoid redirecting before the
|
|
205
|
+
// callback action has had a chance to complete.
|
|
140
206
|
if (isValidOAuthCallback) {
|
|
141
207
|
return undefined;
|
|
142
208
|
}
|
|
143
209
|
if (!isAuthenticated) {
|
|
144
210
|
const referer = request.headers.get('referer');
|
|
145
|
-
// TODO: Make this configurable or call the signIn() from here.
|
|
146
211
|
let fallbackRedirect = '/';
|
|
147
|
-
// If referer exists and is from the same origin, use it as fallback
|
|
148
212
|
if (referer) {
|
|
149
213
|
try {
|
|
150
214
|
const refererUrl = new URL(referer);
|
|
@@ -153,25 +217,58 @@ const asgardeoMiddleware = (handler, options) => async (request) => {
|
|
|
153
217
|
fallbackRedirect = refererUrl.pathname + refererUrl.search;
|
|
154
218
|
}
|
|
155
219
|
}
|
|
156
|
-
catch
|
|
157
|
-
// Invalid referer
|
|
220
|
+
catch {
|
|
221
|
+
// Invalid referer — ignore.
|
|
158
222
|
}
|
|
159
223
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const signInUrl = new URL(redirectUrl, request.url);
|
|
163
|
-
return NextResponse.redirect(signInUrl);
|
|
224
|
+
const redirectUrl = routeOptions?.redirect ?? resolvedConfig.signInUrl ?? fallbackRedirect;
|
|
225
|
+
return NextResponse.redirect(new URL(redirectUrl, request.url));
|
|
164
226
|
}
|
|
165
227
|
return undefined;
|
|
166
228
|
},
|
|
167
229
|
};
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
230
|
+
// ── Handler ───────────────────────────────────────────────────────────────
|
|
231
|
+
const handlerResponse = handler ? await handler(asgardeo, request) : undefined;
|
|
232
|
+
// ── Build final response ──────────────────────────────────────────────────
|
|
233
|
+
if (shouldClearCookie) {
|
|
234
|
+
const cookieName = SessionManager.getSessionCookieName();
|
|
235
|
+
if (handlerResponse) {
|
|
236
|
+
// Handler returned a response (e.g. a redirect from protectRoute).
|
|
237
|
+
// Attach the deletion so the browser discards the stale cookie.
|
|
238
|
+
handlerResponse.cookies.delete(cookieName);
|
|
239
|
+
return handlerResponse;
|
|
172
240
|
}
|
|
241
|
+
// Pass-through: strip the dead cookie from the forwarded request headers
|
|
242
|
+
// so the same-request Server Component render sees no session at all.
|
|
243
|
+
const requestHeaders = new Headers(request.headers);
|
|
244
|
+
requestHeaders.set('cookie', removeCookieFromHeader(request.headers.get('cookie') ?? '', cookieName));
|
|
245
|
+
const cleanResponse = NextResponse.next({ request: { headers: requestHeaders } });
|
|
246
|
+
cleanResponse.cookies.delete(cookieName);
|
|
247
|
+
return cleanResponse;
|
|
248
|
+
}
|
|
249
|
+
if (!refreshCookieUpdate) {
|
|
250
|
+
return handlerResponse ?? NextResponse.next();
|
|
251
|
+
}
|
|
252
|
+
// A token refresh occurred — the new session cookie must be applied to:
|
|
253
|
+
// 1. The HTTP response so the browser stores the updated cookie.
|
|
254
|
+
// 2. The forwarded request headers so the same-request Server Component
|
|
255
|
+
// render reads the fresh session token instead of the expired one.
|
|
256
|
+
const cookieName = SessionManager.getSessionCookieName();
|
|
257
|
+
const cookieOptions = SessionManager.getSessionCookieOptions(refreshCookieUpdate.expiry);
|
|
258
|
+
if (handlerResponse) {
|
|
259
|
+
// Handler returned a response (e.g. a redirect from protectRoute).
|
|
260
|
+
// Attach the refresh cookie so the browser receives it even on redirects.
|
|
261
|
+
handlerResponse.cookies.set(cookieName, refreshCookieUpdate.token, cookieOptions);
|
|
262
|
+
return handlerResponse;
|
|
173
263
|
}
|
|
174
|
-
|
|
264
|
+
// Default pass-through: update both the response cookie and the request
|
|
265
|
+
// Cookie header so the downstream Server Component render is not stale.
|
|
266
|
+
const requestHeaders = new Headers(request.headers);
|
|
267
|
+
const updatedCookieHeader = replaceCookieInHeader(request.headers.get('cookie') ?? '', cookieName, refreshCookieUpdate.token);
|
|
268
|
+
requestHeaders.set('cookie', updatedCookieHeader);
|
|
269
|
+
const response = NextResponse.next({ request: { headers: requestHeaders } });
|
|
270
|
+
response.cookies.set(cookieName, refreshCookieUpdate.token, cookieOptions);
|
|
271
|
+
return response;
|
|
175
272
|
};
|
|
176
273
|
export default asgardeoMiddleware;
|
|
177
274
|
//# sourceMappingURL=asgardeoMiddleware.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"asgardeoMiddleware.js","sourceRoot":"","sources":["../../../../src/server/middleware/asgardeoMiddleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAc,YAAY,EAAC,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"asgardeoMiddleware.js","sourceRoot":"","sources":["../../../../src/server/middleware/asgardeoMiddleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAc,YAAY,EAAC,MAAM,aAAa,CAAC;AACtD,OAAO,EAAC,sBAAsB,EAAC,MAAM,kCAAkC,CAAC;AAExE,OAAO,yBAAyB,MAAM,uCAAuC,CAAC;AAC9E,OAAO,kBAAkB,MAAM,gCAAgC,CAAC;AAChE,OAAO,cAAqC,MAAM,4BAA4B,CAAC;AAC/E,OAAO,EAAC,qBAAqB,EAAE,uBAAuB,EAAC,MAAM,0BAA0B,CAAC;AA4BxF;;GAEG;AACH,MAAM,sBAAsB,GAAG,CAAC,YAAoB,EAAE,IAAY,EAAU,EAAE,CAC5E,YAAY;KACT,KAAK,CAAC,GAAG,CAAC;KACV,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;KAC5B,MAAM,CAAC,CAAC,CAAS,EAAE,EAAE;IACpB,MAAM,KAAK,GAAW,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACrC,MAAM,QAAQ,GAAW,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;IACrE,OAAO,QAAQ,KAAK,IAAI,CAAC;AAC3B,CAAC,CAAC;KACD,IAAI,CAAC,IAAI,CAAC,CAAC;AAEhB;;;GAGG;AACH,MAAM,qBAAqB,GAAG,CAAC,YAAoB,EAAE,IAAY,EAAE,KAAa,EAAU,EAAE;IAC1F,MAAM,KAAK,GAAa,YAAY;SACjC,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SAC5B,MAAM,CAAC,OAAO,CAAC,CAAC;IAEnB,IAAI,KAAK,GAAY,KAAK,CAAC;IAC3B,MAAM,OAAO,GAAa,KAAK,CAAC,GAAG,CAAC,CAAC,IAAY,EAAE,EAAE;QACnD,MAAM,KAAK,GAAW,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACxC,MAAM,QAAQ,GAAW,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;QAC3E,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACtB,KAAK,GAAG,IAAI,CAAC;YACb,OAAO,GAAG,IAAI,IAAI,KAAK,EAAE,CAAC;QAC5B,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,IAAI,KAAK,EAAE,CAAC,CAAC;IACnC,CAAC;IAED,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,MAAM,kBAAkB,GACtB,CACE,OAAmC,EACnC,OAAuF,EACpC,EAAE,CACvD,KAAK,EAAE,OAAoB,EAAyB,EAAE;IACpD,MAAM,eAAe,GAA8B,OAAO,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC;IAEpH,4EAA4E;IAC5E,MAAM,cAAc,GAAuB,yBAAyB,CAAC,eAAqC,CAAC,CAAC;IAE5G,6EAA6E;IAC7E,MAAM,GAAG,GAAQ,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACtC,MAAM,iBAAiB,GAAY,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAEjG,IAAI,oBAAoB,GAAY,KAAK,CAAC;IAC1C,IAAI,iBAAiB,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;QACxD,MAAM,gBAAgB,GAAuB,OAAO,CAAC,OAAO,CAAC,GAAG,CAC9D,cAAc,CAAC,wBAAwB,EAAE,CAC1C,EAAE,KAAK,CAAC;QACT,IAAI,gBAAgB,EAAE,CAAC;YACrB,IAAI,CAAC;gBACH,MAAM,cAAc,CAAC,iBAAiB,CAAC,gBAAgB,CAAC,CAAC;gBACzD,oBAAoB,GAAG,IAAI,CAAC;YAC9B,CAAC;YAAC,MAAM,CAAC;gBACP,oBAAoB,GAAG,KAAK,CAAC;YAC/B,CAAC;QACH,CAAC;IACH,CAAC;IAED,6EAA6E;IAC7E,wEAAwE;IACxE,MAAM,eAAe,GAAoC,MAAM,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAE9F,2EAA2E;IAC3E,0EAA0E;IAC1E,2EAA2E;IAC3E,4EAA4E;IAC5E,0EAA0E;IAC1E,4EAA4E;IAC5E,mBAAmB;IACnB,IAAI,cAA+C,CAAC;IACpD,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,MAAM,QAAQ,GAAuB,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,oBAAoB,EAAE,CAAC,EAAE,KAAK,CAAC;QACvG,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,CAAC;gBACH,MAAM,OAAO,GAAwB,MAAM,cAAc,CAAC,4BAA4B,CAAC,QAAQ,CAAC,CAAC;gBACjG,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;oBACzB,cAAc,GAAG,OAAO,CAAC;gBAC3B,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,uDAAuD;YACzD,CAAC;QACH,CAAC;IACH,CAAC;IAED,6EAA6E;IAC7E,MAAM,GAAG,GAAW,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IAClD,MAAM,gBAAgB,GAAoC,eAAe,IAAI,cAAc,CAAC;IAE5F,iDAAiD;IACjD,MAAM,gBAAgB,GAAY,CAAC,CAAC,CAClC,cAAc,CAAC,OAAO;QACtB,cAAc,CAAC,QAAQ;QACvB,cAAc,CAAC,YAAY,CAC5B,CAAC;IAEF,gBAAgB;IAChB,oEAAoE;IACpE,mEAAmE;IACnE,MAAM,YAAY,GAChB,CAAC,oBAAoB;QACrB,gBAAgB;QAChB,CAAC,CAAC,gBAAgB,EAAE,YAAY;QAChC,CAAC,CAAC,CAAC,CAAC,eAAe,IAAI,eAAe,CAAC,GAAG,IAAI,GAAG,GAAG,sBAAsB,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,CAAC;IAEnG,IAAI,aAAa,GAAoC,eAAe,CAAC;IACrE,IAAI,mBAAgE,CAAC;IAErE,IAAI,YAAY,IAAI,gBAAgB,EAAE,CAAC;QACrC,IAAI,CAAC;YACH,MAAM,EAAC,eAAe,EAAE,uBAAuB,EAAC,GAAG,MAAM,kBAAkB,CAAC,gBAAgB,EAAE;gBAC5F,OAAO,EAAE,cAAc,CAAC,OAAQ;gBAChC,QAAQ,EAAE,cAAc,CAAC,QAAS;gBAClC,YAAY,EAAE,cAAc,CAAC,YAAa;gBAC1C,uBAAuB,EAAE,cAAc,CAAC,uBAAuB;aAChE,CAAC,CAAC;YACH,wEAAwE;YACxE,aAAa,GAAG,MAAM,cAAc,CAAC,kBAAkB,CAAC,eAAe,CAAC,CAAC;YACzE,mBAAmB,GAAG,EAAC,MAAM,EAAE,uBAAuB,EAAE,KAAK,EAAE,eAAe,EAAC,CAAC;QAClF,CAAC;QAAC,MAAM,CAAC;YACP,oDAAoD;YACpD,aAAa,GAAG,SAAS,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,6EAA6E;IAC7E,6EAA6E;IAC7E,mEAAmE;IACnE,MAAM,gBAAgB,GAAuB,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,oBAAoB,EAAE,CAAC,EAAE,KAAK,CAAC;IAE/G,IAAI,iBAAiB,GAAY,KAAK,CAAC;IAEvC,IAAI,CAAC,oBAAoB,IAAI,gBAAgB,IAAI,CAAC,aAAa,IAAI,CAAC,mBAAmB,EAAE,CAAC;QACxF,0EAA0E;QAC1E,wDAAwD;QACxD,iBAAiB,GAAG,IAAI,CAAC;IAC3B,CAAC;IAED,MAAM,SAAS,GAAuB,aAAa,EAAE,SAAS,IAAI,CAAC,MAAM,uBAAuB,CAAC,OAAO,CAAC,CAAC,CAAC;IAC3G,MAAM,eAAe,GAAY,CAAC,CAAC,aAAa,CAAC;IAEjD,6EAA6E;IAC7E,MAAM,QAAQ,GAA8B;QAC1C,UAAU,EAAE,KAAK,IAA8C,EAAE,CAAC,aAAa;QAC/E,YAAY,EAAE,GAAuB,EAAE,CAAC,SAAS;QACjD,UAAU,EAAE,GAAY,EAAE,CAAC,eAAe;QAC1C,YAAY,EAAE,KAAK,EAAE,YAAkC,EAAgC,EAAE;YACvF,qEAAqE;YACrE,gDAAgD;YAChD,IAAI,oBAAoB,EAAE,CAAC;gBACzB,OAAO,SAAS,CAAC;YACnB,CAAC;YAED,IAAI,CAAC,eAAe,EAAE,CAAC;gBACrB,MAAM,OAAO,GAAkB,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;gBAC9D,IAAI,gBAAgB,GAAW,GAAG,CAAC;gBAEnC,IAAI,OAAO,EAAE,CAAC;oBACZ,IAAI,CAAC;wBACH,MAAM,UAAU,GAAQ,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;wBACzC,MAAM,UAAU,GAAQ,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;wBAC7C,IAAI,UAAU,CAAC,MAAM,KAAK,UAAU,CAAC,MAAM,EAAE,CAAC;4BAC5C,gBAAgB,GAAG,UAAU,CAAC,QAAQ,GAAG,UAAU,CAAC,MAAM,CAAC;wBAC7D,CAAC;oBACH,CAAC;oBAAC,MAAM,CAAC;wBACP,4BAA4B;oBAC9B,CAAC;gBACH,CAAC;gBAED,MAAM,WAAW,GACf,YAAY,EAAE,QAAQ,IAAK,cAAc,CAAC,SAAoB,IAAI,gBAAgB,CAAC;gBAErF,OAAO,YAAY,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;YAClE,CAAC;YAED,OAAO,SAAS,CAAC;QACnB,CAAC;KACF,CAAC;IAEF,6EAA6E;IAC7E,MAAM,eAAe,GAAwB,OAAO,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAEpG,6EAA6E;IAC7E,IAAI,iBAAiB,EAAE,CAAC;QACtB,MAAM,UAAU,GAAW,cAAc,CAAC,oBAAoB,EAAE,CAAC;QAEjE,IAAI,eAAe,EAAE,CAAC;YACpB,mEAAmE;YACnE,gEAAgE;YAChE,eAAe,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YAC3C,OAAO,eAAe,CAAC;QACzB,CAAC;QAED,yEAAyE;QACzE,sEAAsE;QACtE,MAAM,cAAc,GAAY,IAAI,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC7D,cAAc,CAAC,GAAG,CAAC,QAAQ,EAAE,sBAAsB,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,UAAU,CAAC,CAAC,CAAC;QACtG,MAAM,aAAa,GAAiB,YAAY,CAAC,IAAI,CAAC,EAAC,OAAO,EAAE,EAAC,OAAO,EAAE,cAAc,EAAC,EAAC,CAAC,CAAC;QAC5F,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACzC,OAAO,aAAa,CAAC;IACvB,CAAC;IAED,IAAI,CAAC,mBAAmB,EAAE,CAAC;QACzB,OAAO,eAAe,IAAI,YAAY,CAAC,IAAI,EAAE,CAAC;IAChD,CAAC;IAED,wEAAwE;IACxE,mEAAmE;IACnE,0EAA0E;IAC1E,wEAAwE;IACxE,MAAM,UAAU,GAAW,cAAc,CAAC,oBAAoB,EAAE,CAAC;IACjE,MAAM,aAAa,GACjB,cAAc,CAAC,uBAAuB,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAErE,IAAI,eAAe,EAAE,CAAC;QACpB,mEAAmE;QACnE,0EAA0E;QAC1E,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,mBAAmB,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;QAClF,OAAO,eAAe,CAAC;IACzB,CAAC;IAED,wEAAwE;IACxE,wEAAwE;IACxE,MAAM,cAAc,GAAY,IAAI,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7D,MAAM,mBAAmB,GAAW,qBAAqB,CACvD,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,EACnC,UAAU,EACV,mBAAmB,CAAC,KAAK,CAC1B,CAAC;IACF,cAAc,CAAC,GAAG,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC;IAElD,MAAM,QAAQ,GAAiB,YAAY,CAAC,IAAI,CAAC,EAAC,OAAO,EAAE,EAAC,OAAO,EAAE,cAAc,EAAC,EAAC,CAAC,CAAC;IACvF,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,mBAAmB,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;IAC3E,OAAO,QAAQ,CAAC;AAClB,CAAC,CAAC;AAEJ,eAAe,kBAAkB,CAAC"}
|
|
@@ -16,12 +16,12 @@
|
|
|
16
16
|
* under the License.
|
|
17
17
|
*/
|
|
18
18
|
import { AsgardeoRuntimeError, CookieConfig } from '@asgardeo/node';
|
|
19
|
-
import { SignJWT, jwtVerify } from 'jose';
|
|
19
|
+
import { SignJWT, jwtVerify, compactVerify } from 'jose';
|
|
20
|
+
import { DEFAULT_SESSION_COOKIE_EXPIRY_TIME } from '../constants/sessionConstants';
|
|
20
21
|
/**
|
|
21
22
|
* Session management utility class for JWT-based session cookies
|
|
22
23
|
*/
|
|
23
24
|
class SessionManager {
|
|
24
|
-
static DEFAULT_EXPIRY_SECONDS = 3600;
|
|
25
25
|
/**
|
|
26
26
|
* Get the signing secret from environment variable
|
|
27
27
|
* Throws error in production if not set
|
|
@@ -55,13 +55,32 @@ class SessionManager {
|
|
|
55
55
|
return jwt;
|
|
56
56
|
}
|
|
57
57
|
/**
|
|
58
|
-
*
|
|
58
|
+
* Resolve the session cookie expiry time in seconds.
|
|
59
|
+
*
|
|
60
|
+
* Resolution order (first defined value wins):
|
|
61
|
+
* 1. `configuredExpiry` — value from `AsgardeoNodeConfig.sessionCookieExpiryTime`
|
|
62
|
+
* 2. `ASGARDEO_SESSION_COOKIE_EXPIRY_TIME` environment variable
|
|
63
|
+
* 3. `DEFAULT_SESSION_COOKIE_EXPIRY_TIME` (24 hours)
|
|
59
64
|
*/
|
|
60
|
-
static
|
|
65
|
+
static resolveSessionCookieExpiry(configuredExpiry) {
|
|
66
|
+
if (configuredExpiry != null && configuredExpiry > 0) {
|
|
67
|
+
return configuredExpiry;
|
|
68
|
+
}
|
|
69
|
+
const envValue = process.env['ASGARDEO_SESSION_COOKIE_EXPIRY_TIME'];
|
|
70
|
+
if (envValue) {
|
|
71
|
+
const parsed = parseInt(envValue, 10);
|
|
72
|
+
if (!Number.isNaN(parsed) && parsed > 0) {
|
|
73
|
+
return parsed;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return DEFAULT_SESSION_COOKIE_EXPIRY_TIME;
|
|
77
|
+
}
|
|
78
|
+
static async createSessionToken(accessToken, userId, sessionId, scopes, accessTokenTtlSeconds, refreshToken, organizationId) {
|
|
61
79
|
const secret = this.getSecret();
|
|
62
80
|
const jwt = await new SignJWT({
|
|
63
81
|
accessToken,
|
|
64
82
|
organizationId,
|
|
83
|
+
refreshToken,
|
|
65
84
|
scopes,
|
|
66
85
|
sessionId,
|
|
67
86
|
type: 'session',
|
|
@@ -69,7 +88,7 @@ class SessionManager {
|
|
|
69
88
|
.setProtectedHeader({ alg: 'HS256' })
|
|
70
89
|
.setSubject(userId)
|
|
71
90
|
.setIssuedAt()
|
|
72
|
-
.setExpirationTime(Date.now() / 1000 +
|
|
91
|
+
.setExpirationTime(Math.floor(Date.now() / 1000) + accessTokenTtlSeconds)
|
|
73
92
|
.sign(secret);
|
|
74
93
|
return jwt;
|
|
75
94
|
}
|
|
@@ -80,12 +99,40 @@ class SessionManager {
|
|
|
80
99
|
try {
|
|
81
100
|
const secret = this.getSecret();
|
|
82
101
|
const { payload } = await jwtVerify(token, secret);
|
|
102
|
+
if (payload['type'] !== 'session') {
|
|
103
|
+
throw new Error('Invalid token type');
|
|
104
|
+
}
|
|
83
105
|
return payload;
|
|
84
106
|
}
|
|
85
107
|
catch (error) {
|
|
86
108
|
throw new AsgardeoRuntimeError(`Invalid session token: ${error instanceof Error ? error.message : 'Unknown error'}`, 'invalid-session-token', 'nextjs', 'Session token verification failed');
|
|
87
109
|
}
|
|
88
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Verify a session token for refresh. Validates the HMAC signature and the
|
|
113
|
+
* `type === 'session'` discriminant but intentionally skips the `exp` check
|
|
114
|
+
* so an expired access token can still be exchanged for a new one.
|
|
115
|
+
*
|
|
116
|
+
* Session lifetime is still bounded — the cookie's `maxAge` is set from
|
|
117
|
+
* `sessionCookieExpiryTime`, so the browser drops an over-age session regardless
|
|
118
|
+
* of the access-token exp embedded in the JWT.
|
|
119
|
+
*
|
|
120
|
+
* Never use the returned payload for authorization.
|
|
121
|
+
*/
|
|
122
|
+
static async verifySessionTokenForRefresh(token) {
|
|
123
|
+
try {
|
|
124
|
+
const secret = this.getSecret();
|
|
125
|
+
const { payload: rawPayload } = await compactVerify(token, secret);
|
|
126
|
+
const payload = JSON.parse(new TextDecoder().decode(rawPayload));
|
|
127
|
+
if (payload.type !== 'session') {
|
|
128
|
+
throw new Error('Invalid token type');
|
|
129
|
+
}
|
|
130
|
+
return payload;
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
throw new AsgardeoRuntimeError(`Invalid session token: ${error instanceof Error ? error.message : 'Unknown error'}`, 'invalid-session-token-for-refresh', 'nextjs', 'Session token signature or type check failed during refresh');
|
|
134
|
+
}
|
|
135
|
+
}
|
|
89
136
|
/**
|
|
90
137
|
* Verify and decode a temporary session token
|
|
91
138
|
*/
|
|
@@ -105,10 +152,10 @@ class SessionManager {
|
|
|
105
152
|
/**
|
|
106
153
|
* Get session cookie options
|
|
107
154
|
*/
|
|
108
|
-
static getSessionCookieOptions() {
|
|
155
|
+
static getSessionCookieOptions(maxAge) {
|
|
109
156
|
return {
|
|
110
157
|
httpOnly: true,
|
|
111
|
-
maxAge
|
|
158
|
+
maxAge,
|
|
112
159
|
path: '/',
|
|
113
160
|
sameSite: 'lax',
|
|
114
161
|
secure: process.env['NODE_ENV'] === 'production',
|