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 isJwtExpired(token) {
12
+ function decodeJwtPayload(token) {
13
13
  try {
14
14
  const payload = token.split('.')[1];
15
- const { exp } = JSON.parse(atob(payload.replace(/[-_]/g, (c) => c === '-' ? '+' : '/')));
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 true;
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.locales[0] ? '' : requestPrefix;
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloudflare-next-intl",
3
- "version": "0.6.7",
3
+ "version": "0.6.8",
4
4
  "description": "Optimized Next Intl Package Special for App Router and Cloudflare",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",