cloudflare-next-intl 0.6.7 → 0.6.8
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.
|
@@ -10,13 +10,14 @@ const baseFa = {
|
|
|
10
10
|
redirectAuthPath: '/login',
|
|
11
11
|
homePath: '/',
|
|
12
12
|
isAuthPath: (path) => path === '/login',
|
|
13
|
+
verifyEmailPath: '/verify-email',
|
|
13
14
|
};
|
|
14
15
|
vi.mock('@intl-config', () => ({
|
|
15
16
|
default: { locales: ['en'], defaultLocale: 'en', firebaseAuth: baseFa },
|
|
16
17
|
}));
|
|
17
|
-
function makeJwt(exp) {
|
|
18
|
+
function makeJwt(exp, claims = {}) {
|
|
18
19
|
const header = Buffer.from(JSON.stringify({ alg: 'none' })).toString('base64url');
|
|
19
|
-
const payload = Buffer.from(JSON.stringify({ exp })).toString('base64url');
|
|
20
|
+
const payload = Buffer.from(JSON.stringify({ exp, ...claims })).toString('base64url');
|
|
20
21
|
return `${header}.${payload}.sig`;
|
|
21
22
|
}
|
|
22
23
|
describe('updateSession', () => {
|
|
@@ -34,4 +35,11 @@ describe('updateSession', () => {
|
|
|
34
35
|
});
|
|
35
36
|
await updateSession(req, NextResponse.next(), 'en');
|
|
36
37
|
});
|
|
38
|
+
bench('valid session, unverified email: decodeJwtPayload + verifyEmailPath redirect', async () => {
|
|
39
|
+
const { default: updateSession } = await import('./update_session');
|
|
40
|
+
const req = makeTestRequest('https://example.com/en/dashboard', {
|
|
41
|
+
cookies: { __fa_session__: makeJwt(Date.now() / 1000 + 3600, { email_verified: false }) },
|
|
42
|
+
});
|
|
43
|
+
await updateSession(req, NextResponse.next(), 'en');
|
|
44
|
+
});
|
|
37
45
|
});
|
|
@@ -9,16 +9,19 @@ const DEFAULT_REFRESH_MAX_AGE = 60 * 60 * 24 * 365;
|
|
|
9
9
|
// time can hand a client a token that dies moments after this check, forcing
|
|
10
10
|
// an extra round-trip on the very next request.
|
|
11
11
|
const CLOCK_SKEW_MARGIN_MS = 60 * 1000;
|
|
12
|
-
function
|
|
12
|
+
function decodeJwtPayload(token) {
|
|
13
13
|
try {
|
|
14
14
|
const payload = token.split('.')[1];
|
|
15
|
-
|
|
16
|
-
return !exp || exp * 1000 - CLOCK_SKEW_MARGIN_MS <= Date.now();
|
|
15
|
+
return JSON.parse(atob(payload.replace(/[-_]/g, (c) => c === '-' ? '+' : '/')));
|
|
17
16
|
}
|
|
18
17
|
catch {
|
|
19
|
-
return
|
|
18
|
+
return null;
|
|
20
19
|
}
|
|
21
20
|
}
|
|
21
|
+
function isJwtExpired(token) {
|
|
22
|
+
const decoded = decodeJwtPayload(token);
|
|
23
|
+
return !decoded?.exp || decoded.exp * 1000 - CLOCK_SKEW_MARGIN_MS <= Date.now();
|
|
24
|
+
}
|
|
22
25
|
// Refreshing an ID token is a real network round-trip to Google on the
|
|
23
26
|
// Edge middleware's critical path, on every request where the session
|
|
24
27
|
// cookie has expired — which, for any session older than the ~1hr ID-token
|
|
@@ -170,7 +173,7 @@ export default async function updateSession(request, baseResponse, locale) {
|
|
|
170
173
|
if (rawPath.startsWith('/_next') || /\.[a-zA-Z0-9]+$/.test(lastSegment)) {
|
|
171
174
|
return baseResponse;
|
|
172
175
|
}
|
|
173
|
-
const localePrefix = locale === config.
|
|
176
|
+
const localePrefix = locale === config.defaultLocale ? '' : requestPrefix;
|
|
174
177
|
const localeUrl = (target) => new URL(`${localePrefix}${target === '/' ? '' : target}` || '/', request.url);
|
|
175
178
|
const isWhiteListed = fa.whiteListPaths?.includes(path) ?? false;
|
|
176
179
|
if (isWhiteListed)
|
|
@@ -210,6 +213,7 @@ export default async function updateSession(request, baseResponse, locale) {
|
|
|
210
213
|
}
|
|
211
214
|
const hasSession = !!token;
|
|
212
215
|
let response;
|
|
216
|
+
const isVerifyEmailPage = !!fa.verifyEmailPath && path === fa.verifyEmailPath;
|
|
213
217
|
if (refreshWasTransientFailure) {
|
|
214
218
|
// Couldn't confirm the session either way — pass through without
|
|
215
219
|
// forcing a redirect in either direction. The next request (or the
|
|
@@ -223,6 +227,9 @@ export default async function updateSession(request, baseResponse, locale) {
|
|
|
223
227
|
else if (isAuthPage) {
|
|
224
228
|
response = buildRedirect(baseResponse, localeUrl(fa.homePath));
|
|
225
229
|
}
|
|
230
|
+
else if (fa.verifyEmailPath && !isVerifyEmailPage && token && decodeJwtPayload(token)?.email_verified === false) {
|
|
231
|
+
response = buildRedirect(baseResponse, localeUrl(fa.verifyEmailPath));
|
|
232
|
+
}
|
|
226
233
|
else {
|
|
227
234
|
response = baseResponse;
|
|
228
235
|
}
|