@tern-secure/nextjs 4.2.1 → 4.2.2

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.
@@ -52,7 +52,7 @@ async function auth() {
52
52
  error: new Error("No valid session or token found")
53
53
  };
54
54
  } catch (error) {
55
- console.error("Error in getAuthResult:", error);
55
+ console.error("Error in get AuthResult:", error);
56
56
  return {
57
57
  user: null,
58
58
  token: null,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/server/auth.ts"],"sourcesContent":["'use server'\nimport { cookies, headers } from \"next/headers\"\nimport type { UserInfo, SessionResult } from \"./edge-session\"\n\n\nexport interface AuthResult {\n user: UserInfo | null\n token: string | null\n error: Error | null\n}\n\n\n /**\n * Get the current authenticated user from the session or token\n */\n export async function auth(): Promise<AuthResult> {\n try {\n const headersList = await headers()\n const cookieStore = await cookies()\n\n const userId = headersList.get('x-user-id')\n const authTime = headersList.get('x-auth-time')\n const emailVerified = headersList.get('x-auth-verified') === 'true'\n\n if (userId) {\n const token = cookieStore.get(\"_session_cookie\")?.value || \n cookieStore.get(\"_session_token\")?.value || \n null\n \n return {\n user: {\n uid: userId,\n email: headersList.get('x-user-email') || null,\n emailVerified,\n authTime: authTime ? parseInt(authTime) : undefined\n },\n token,\n error: null\n }\n }\n\n return {\n user: null,\n token: null,\n error: new Error(\"No valid session or token found\"),\n }\n } catch (error) {\n console.error(\"Error in getAuthResult:\", error)\n return {\n user: null,\n token: null,\n error: error instanceof Error ? error : new Error(\"An unknown error occurred\"),\n }\n }\n}\n\n/**\n * Type guard to check if user is authenticated\n */\nexport async function isAuthenticated(): Promise<boolean> {\n const authResult = await auth()\n return authResult.user !== null\n}\n\n/**\n * Get user info from auth result\n */\nexport async function getUserInfo(): Promise<UserInfo | null> {\n const authResult = await auth()\n if (!authResult.user) {\n return null\n }\n\n return {\n uid: authResult.user.uid,\n email: authResult.user.email,\n emailVerified: authResult.user.emailVerified,\n authTime: authResult.user.authTime\n }\n }\n\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,qBAAiC;AAc/B,eAAsB,OAA4B;AAfpD;AAgBI,MAAI;AACF,UAAM,cAAc,UAAM,wBAAQ;AAClC,UAAM,cAAc,UAAM,wBAAQ;AAElC,UAAM,SAAS,YAAY,IAAI,WAAW;AAC1C,UAAM,WAAW,YAAY,IAAI,aAAa;AAC9C,UAAM,gBAAgB,YAAY,IAAI,iBAAiB,MAAM;AAE7D,QAAI,QAAQ;AACV,YAAM,UAAQ,iBAAY,IAAI,iBAAiB,MAAjC,mBAAoC,YACrC,iBAAY,IAAI,gBAAgB,MAAhC,mBAAmC,UACnC;AAEb,aAAO;AAAA,QACL,MAAM;AAAA,UACJ,KAAK;AAAA,UACL,OAAO,YAAY,IAAI,cAAc,KAAK;AAAA,UAC1C;AAAA,UACA,UAAU,WAAW,SAAS,QAAQ,IAAI;AAAA,QAC5C;AAAA,QACA;AAAA,QACA,OAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AAAA,MACP,OAAO,IAAI,MAAM,iCAAiC;AAAA,IACpD;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,2BAA2B,KAAK;AAC9C,WAAO;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AAAA,MACP,OAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,2BAA2B;AAAA,IAC/E;AAAA,EACF;AACJ;AAKA,eAAsB,kBAAoC;AACxD,QAAM,aAAa,MAAM,KAAK;AAC9B,SAAO,WAAW,SAAS;AAC7B;AAKA,eAAsB,cAAwC;AAC5D,QAAM,aAAa,MAAM,KAAK;AAC9B,MAAI,CAAC,WAAW,MAAM;AACpB,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,KAAK,WAAW,KAAK;AAAA,IACrB,OAAO,WAAW,KAAK;AAAA,IACvB,eAAe,WAAW,KAAK;AAAA,IAC/B,UAAU,WAAW,KAAK;AAAA,EAC5B;AACA;","names":[]}
1
+ {"version":3,"sources":["../../../src/server/auth.ts"],"sourcesContent":["'use server'\nimport { cookies, headers } from \"next/headers\"\nimport type { UserInfo } from \"./types\"\n\n\nexport interface AuthResult {\n user: UserInfo | null\n token: string | null\n error: Error | null\n}\n\n\n /**\n * Get the current authenticated user from the session or token\n */\n export async function auth(): Promise<AuthResult> {\n try {\n const headersList = await headers()\n const cookieStore = await cookies()\n\n const userId = headersList.get('x-user-id')\n const authTime = headersList.get('x-auth-time')\n const emailVerified = headersList.get('x-auth-verified') === 'true'\n\n if (userId) {\n const token = cookieStore.get(\"_session_cookie\")?.value || \n cookieStore.get(\"_session_token\")?.value || \n null\n \n return {\n user: {\n uid: userId,\n email: headersList.get('x-user-email') || null,\n emailVerified,\n authTime: authTime ? parseInt(authTime) : undefined\n },\n token,\n error: null\n }\n }\n\n return {\n user: null,\n token: null,\n error: new Error(\"No valid session or token found\"),\n }\n } catch (error) {\n console.error(\"Error in get AuthResult:\", error)\n return {\n user: null,\n token: null,\n error: error instanceof Error ? error : new Error(\"An unknown error occurred\"),\n }\n }\n}\n\n/**\n * Type guard to check if user is authenticated\n */\nexport async function isAuthenticated(): Promise<boolean> {\n const authResult = await auth()\n return authResult.user !== null\n}\n\n/**\n * Get user info from auth result\n */\nexport async function getUserInfo(): Promise<UserInfo | null> {\n const authResult = await auth()\n if (!authResult.user) {\n return null\n }\n\n return {\n uid: authResult.user.uid,\n email: authResult.user.email,\n emailVerified: authResult.user.emailVerified,\n authTime: authResult.user.authTime\n }\n }\n\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,qBAAiC;AAc/B,eAAsB,OAA4B;AAfpD;AAgBI,MAAI;AACF,UAAM,cAAc,UAAM,wBAAQ;AAClC,UAAM,cAAc,UAAM,wBAAQ;AAElC,UAAM,SAAS,YAAY,IAAI,WAAW;AAC1C,UAAM,WAAW,YAAY,IAAI,aAAa;AAC9C,UAAM,gBAAgB,YAAY,IAAI,iBAAiB,MAAM;AAE7D,QAAI,QAAQ;AACV,YAAM,UAAQ,iBAAY,IAAI,iBAAiB,MAAjC,mBAAoC,YACrC,iBAAY,IAAI,gBAAgB,MAAhC,mBAAmC,UACnC;AAEb,aAAO;AAAA,QACL,MAAM;AAAA,UACJ,KAAK;AAAA,UACL,OAAO,YAAY,IAAI,cAAc,KAAK;AAAA,UAC1C;AAAA,UACA,UAAU,WAAW,SAAS,QAAQ,IAAI;AAAA,QAC5C;AAAA,QACA;AAAA,QACA,OAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AAAA,MACP,OAAO,IAAI,MAAM,iCAAiC;AAAA,IACpD;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,4BAA4B,KAAK;AAC/C,WAAO;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AAAA,MACP,OAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,2BAA2B;AAAA,IAC/E;AAAA,EACF;AACJ;AAKA,eAAsB,kBAAoC;AACxD,QAAM,aAAa,MAAM,KAAK;AAC9B,SAAO,WAAW,SAAS;AAC7B;AAKA,eAAsB,cAAwC;AAC5D,QAAM,aAAa,MAAM,KAAK;AAC9B,MAAI,CAAC,WAAW,MAAM;AACpB,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,KAAK,WAAW,KAAK;AAAA,IACrB,OAAO,WAAW,KAAK;AAAA,IACvB,eAAe,WAAW,KAAK;AAAA,IAC/B,UAAU,WAAW,KAAK;AAAA,EAC5B;AACA;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/server/edge-session.ts"],"sourcesContent":["import { verifyFirebaseToken } from \"./jwt-edge\"\nimport type { NextRequest } from \"next/server\"\n\nexport interface UserInfo {\n uid: string\n email: string | null\n emailVerified?: boolean\n authTime?: number\n disabled?: boolean\n}\n\n\nexport interface SessionResult {\n isAuthenticated: boolean\n user: UserInfo | null\n error?: string\n}\n\n\nexport async function verifySession(request: NextRequest): Promise<SessionResult> {\n try {\n //const cookieStore = await cookies()\n\n // First try session cookie\n const sessionCookie = request.cookies.get(\"_session_cookie\")?.value\n if (sessionCookie) {\n const result = await verifyFirebaseToken(sessionCookie, true)\n if (result.valid) {\n return {\n isAuthenticated: true,\n user: {\n uid: result.uid ?? '',\n email: result.email || null,\n emailVerified: result.emailVerified ?? false,\n disabled: false,\n },\n }\n }\n console.log(\"Session cookie verification failed:\", result.error)\n }\n\n // Then try ID token\n const idToken = request.cookies.get(\"_session_token\")?.value\n if (idToken) {\n const result = await verifyFirebaseToken(idToken, false)\n if (result.valid) {\n return {\n isAuthenticated: true,\n user: {\n uid: result.uid ?? '',\n email: result.email || null,\n emailVerified: result.emailVerified ?? false,\n disabled: false,\n },\n }\n }\n console.log(\"ID token verification failed:\", result.error)\n }\n\n return {\n isAuthenticated: false,\n user: null,\n error: \"No valid session found\",\n }\n } catch (error) {\n console.error(\"Session verification error:\", error)\n return {\n isAuthenticated: false,\n user: null,\n error: error instanceof Error ? error.message : \"Session verification failed\",\n }\n }\n}"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sBAAoC;AAmBpC,eAAsB,cAAc,SAA8C;AAnBlF;AAoBE,MAAI;AAIF,UAAM,iBAAgB,aAAQ,QAAQ,IAAI,iBAAiB,MAArC,mBAAwC;AAC9D,QAAI,eAAe;AACjB,YAAM,SAAS,UAAM,qCAAoB,eAAe,IAAI;AAC5D,UAAI,OAAO,OAAO;AAChB,eAAO;AAAA,UACL,iBAAiB;AAAA,UACjB,MAAM;AAAA,YACJ,MAAK,YAAO,QAAP,YAAc;AAAA,YACnB,OAAO,OAAO,SAAS;AAAA,YACvB,gBAAe,YAAO,kBAAP,YAAwB;AAAA,YACvC,UAAU;AAAA,UACZ;AAAA,QACF;AAAA,MACF;AACA,cAAQ,IAAI,uCAAuC,OAAO,KAAK;AAAA,IACjE;AAGA,UAAM,WAAU,aAAQ,QAAQ,IAAI,gBAAgB,MAApC,mBAAuC;AACvD,QAAI,SAAS;AACX,YAAM,SAAS,UAAM,qCAAoB,SAAS,KAAK;AACvD,UAAI,OAAO,OAAO;AAChB,eAAO;AAAA,UACL,iBAAiB;AAAA,UACjB,MAAM;AAAA,YACJ,MAAK,YAAO,QAAP,YAAc;AAAA,YACnB,OAAO,OAAO,SAAS;AAAA,YACvB,gBAAe,YAAO,kBAAP,YAAwB;AAAA,YACvC,UAAU;AAAA,UACZ;AAAA,QACF;AAAA,MACF;AACA,cAAQ,IAAI,iCAAiC,OAAO,KAAK;AAAA,IAC3D;AAEA,WAAO;AAAA,MACL,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,OAAO;AAAA,IACT;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,+BAA+B,KAAK;AAClD,WAAO;AAAA,MACL,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,IAClD;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../../../src/server/edge-session.ts"],"sourcesContent":["import { verifyFirebaseToken } from \"./jwt-edge\"\nimport type { NextRequest } from \"next/server\"\nimport type { SessionResult } from \"./types\"\n\n\nexport async function verifySession(request: NextRequest): Promise<SessionResult> {\n try {\n //const cookieStore = await cookies()\n\n // First try session cookie\n const sessionCookie = request.cookies.get(\"_session_cookie\")?.value\n if (sessionCookie) {\n const result = await verifyFirebaseToken(sessionCookie, true)\n if (result.valid) {\n return {\n isAuthenticated: true,\n user: {\n uid: result.uid ?? '',\n email: result.email || null,\n emailVerified: result.emailVerified ?? false,\n disabled: false,\n },\n }\n }\n console.log(\"Session cookie verification failed:\", result.error)\n }\n\n // Then try ID token\n const idToken = request.cookies.get(\"_session_token\")?.value\n if (idToken) {\n const result = await verifyFirebaseToken(idToken, false)\n if (result.valid) {\n return {\n isAuthenticated: true,\n user: {\n uid: result.uid ?? '',\n email: result.email || null,\n emailVerified: result.emailVerified ?? false,\n disabled: false,\n },\n }\n }\n console.log(\"ID token verification failed:\", result.error)\n }\n\n return {\n isAuthenticated: false,\n user: null,\n error: \"No valid session found\",\n }\n } catch (error) {\n console.error(\"Session verification error:\", error)\n return {\n isAuthenticated: false,\n user: null,\n error: error instanceof Error ? error.message : \"Session verification failed\",\n }\n }\n}"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sBAAoC;AAKpC,eAAsB,cAAc,SAA8C;AALlF;AAME,MAAI;AAIF,UAAM,iBAAgB,aAAQ,QAAQ,IAAI,iBAAiB,MAArC,mBAAwC;AAC9D,QAAI,eAAe;AACjB,YAAM,SAAS,UAAM,qCAAoB,eAAe,IAAI;AAC5D,UAAI,OAAO,OAAO;AAChB,eAAO;AAAA,UACL,iBAAiB;AAAA,UACjB,MAAM;AAAA,YACJ,MAAK,YAAO,QAAP,YAAc;AAAA,YACnB,OAAO,OAAO,SAAS;AAAA,YACvB,gBAAe,YAAO,kBAAP,YAAwB;AAAA,YACvC,UAAU;AAAA,UACZ;AAAA,QACF;AAAA,MACF;AACA,cAAQ,IAAI,uCAAuC,OAAO,KAAK;AAAA,IACjE;AAGA,UAAM,WAAU,aAAQ,QAAQ,IAAI,gBAAgB,MAApC,mBAAuC;AACvD,QAAI,SAAS;AACX,YAAM,SAAS,UAAM,qCAAoB,SAAS,KAAK;AACvD,UAAI,OAAO,OAAO;AAChB,eAAO;AAAA,UACL,iBAAiB;AAAA,UACjB,MAAM;AAAA,YACJ,MAAK,YAAO,QAAP,YAAc;AAAA,YACnB,OAAO,OAAO,SAAS;AAAA,YACvB,gBAAe,YAAO,kBAAP,YAAwB;AAAA,YACvC,UAAU;AAAA,UACZ;AAAA,QACF;AAAA,MACF;AACA,cAAQ,IAAI,iCAAiC,OAAO,KAAK;AAAA,IAC3D;AAEA,WAAO;AAAA,MACL,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,OAAO;AAAA,IACT;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,+BAA+B,KAAK;AAClD,WAAO;AAAA,MACL,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,IAClD;AAAA,EACF;AACF;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/server/index.ts"],"sourcesContent":["\n\nexport { ternSecureMiddleware, createRouteMatcher } from './ternSecureMiddleware'\nexport { auth, getUserInfo } from './auth'\nexport type { AuthResult } from './auth'"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,kCAAyD;AACzD,kBAAkC;","names":[]}
1
+ {"version":3,"sources":["../../../src/server/index.ts"],"sourcesContent":["\n\nexport { ternSecureMiddleware, createRouteMatcher } from './ternSecureMiddleware'\nexport { auth, getUserInfo } from './auth'\nexport type { AuthResult } from './auth'\nexport type { UserInfo, SessionResult } from './types'"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,kCAAyD;AACzD,kBAAkC;","names":[]}
@@ -22,59 +22,94 @@ __export(jwt_edge_exports, {
22
22
  });
23
23
  module.exports = __toCommonJS(jwt_edge_exports);
24
24
  var import_jose = require("jose");
25
- const JWKS_URLS = {
26
- session: new URL("https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com"),
27
- token: new URL("https://identitytoolkit.googleapis.com/v1/sessionCookiePublicKeys")
28
- };
29
- const JWKS = {
30
- session: (0, import_jose.createRemoteJWKSet)(new URL(JWKS_URLS.session), {
25
+ var import_react = require("react");
26
+ const FIREBASE_ID_TOKEN_URL = "https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com";
27
+ const FIREBASE_SESSION_CERT_URL = "https://identitytoolkit.googleapis.com/v1/sessionCookiePublicKeys";
28
+ const getIdTokenJWKS = (0, import_react.cache)(() => {
29
+ return (0, import_jose.createRemoteJWKSet)(new URL(FIREBASE_ID_TOKEN_URL), {
31
30
  cacheMaxAge: 36e5,
32
31
  // 1 hour
33
32
  timeoutDuration: 5e3,
34
33
  // 5 seconds
35
34
  cooldownDuration: 3e4
36
35
  // 30 seconds between retries
37
- }),
38
- token: (0, import_jose.createRemoteJWKSet)(new URL(JWKS_URLS.token), {
36
+ });
37
+ });
38
+ const getSessionJWKS = (0, import_react.cache)(() => {
39
+ return (0, import_jose.createRemoteJWKSet)(new URL(FIREBASE_SESSION_CERT_URL), {
39
40
  cacheMaxAge: 36e5,
40
41
  // 1 hour
41
42
  timeoutDuration: 5e3,
42
43
  // 5 seconds
43
44
  cooldownDuration: 3e4
44
45
  // 30 seconds between retries
45
- })
46
- };
46
+ });
47
+ });
48
+ function decodeJwt(token) {
49
+ try {
50
+ const [headerB64, payloadB64] = token.split(".");
51
+ const header = JSON.parse(Buffer.from(headerB64, "base64").toString());
52
+ const payload = JSON.parse(Buffer.from(payloadB64, "base64").toString());
53
+ return { header, payload };
54
+ } catch (error) {
55
+ console.error("Error decoding JWT:", error);
56
+ return null;
57
+ }
58
+ }
47
59
  async function verifyFirebaseToken(token, isSessionCookie = false) {
48
60
  try {
49
61
  const projectId = process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID;
50
62
  if (!projectId) {
51
63
  throw new Error("Firebase Project ID is not configured");
52
64
  }
53
- const keySet = isSessionCookie ? JWKS.session : JWKS.token;
54
- const { payload } = await (0, import_jose.jwtVerify)(token, keySet, {
65
+ const decoded = decodeJwt(token);
66
+ if (!decoded) {
67
+ throw new Error("Invalid token format");
68
+ }
69
+ console.log("Token details:", {
70
+ header: decoded.header,
71
+ type: isSessionCookie ? "session_cookie" : "id_token"
72
+ });
73
+ const JWKS = isSessionCookie ? await getSessionJWKS() : await getIdTokenJWKS();
74
+ const { payload } = await (0, import_jose.jwtVerify)(token, JWKS, {
55
75
  issuer: isSessionCookie ? "https://session.firebase.google.com/" + projectId : "https://securetoken.google.com/" + projectId,
56
76
  audience: projectId,
57
77
  algorithms: ["RS256"]
58
78
  });
79
+ const firebasePayload = payload;
59
80
  const now = Math.floor(Date.now() / 1e3);
60
- if (payload.exp && payload.exp <= now) {
81
+ if (firebasePayload.exp <= now) {
61
82
  throw new Error("Token has expired");
62
83
  }
63
- if (payload.iat && payload.iat > now) {
84
+ if (firebasePayload.iat > now) {
64
85
  throw new Error("Token issued time is in the future");
65
86
  }
66
- if (!payload.sub) {
87
+ if (!firebasePayload.sub) {
67
88
  throw new Error("Token subject is empty");
68
89
  }
90
+ if (firebasePayload.auth_time > now) {
91
+ throw new Error("Token auth time is in the future");
92
+ }
69
93
  return {
70
94
  valid: true,
71
- uid: payload.sub,
72
- email: payload.email,
73
- emailVerified: payload.email_verified,
74
- authTime: payload.auth_time
95
+ uid: firebasePayload.sub,
96
+ email: firebasePayload.email,
97
+ emailVerified: firebasePayload.email_verified,
98
+ authTime: firebasePayload.auth_time,
99
+ issuedAt: firebasePayload.iat,
100
+ expiresAt: firebasePayload.exp
75
101
  };
76
102
  } catch (error) {
77
- console.error("Token verification error:", error);
103
+ console.error("Token verification details:", {
104
+ error: error instanceof Error ? {
105
+ name: error.name,
106
+ message: error.message,
107
+ stack: error.stack
108
+ } : error,
109
+ decoded: decodeJwt(token),
110
+ //projectId,
111
+ isSessionCookie
112
+ });
78
113
  return {
79
114
  valid: false,
80
115
  error: error instanceof Error ? error.message : "Invalid token"
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/server/jwt-edge.ts"],"sourcesContent":["import { jwtVerify, createRemoteJWKSet } from \"jose\"\n\n// Firebase public key endpoints with simplified configuration for Edge\nconst JWKS_URLS = {\n session: new URL(\"https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com\"),\n token: new URL(\"https://identitytoolkit.googleapis.com/v1/sessionCookiePublicKeys\")\n}\n\n// Simplified JWKS for Edge Runtime\nconst JWKS = {\n session: createRemoteJWKSet(new URL(JWKS_URLS.session), {\n cacheMaxAge: 3600000, // 1 hour\n timeoutDuration: 5000, // 5 seconds\n cooldownDuration: 30000, // 30 seconds between retries\n }),\n token: createRemoteJWKSet(new URL(JWKS_URLS.token), {\n cacheMaxAge: 3600000, // 1 hour\n timeoutDuration: 5000, // 5 seconds\n cooldownDuration: 30000, // 30 seconds between retries\n })\n }\n\nexport async function verifyFirebaseToken(token: string, isSessionCookie = false) {\n try {\n const projectId = process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID\n if (!projectId) {\n throw new Error(\"Firebase Project ID is not configured\")\n }\n\n const keySet = isSessionCookie ? JWKS.session : JWKS.token\n\n\n const { payload } = await jwtVerify(token, keySet, {\n issuer: isSessionCookie\n ? \"https://session.firebase.google.com/\" + projectId\n : \"https://securetoken.google.com/\" + projectId,\n audience: projectId,\n algorithms: [\"RS256\"],\n })\n\n const now = Math.floor(Date.now() / 1000)\n if (payload.exp && payload.exp <= now) {\n throw new Error(\"Token has expired\")\n }\n\n if (payload.iat && payload.iat > now) {\n throw new Error(\"Token issued time is in the future\")\n }\n\n if (!payload.sub) {\n throw new Error(\"Token subject is empty\")\n }\n\n return {\n valid: true,\n uid: payload.sub,\n email: payload.email as string | undefined,\n emailVerified: payload.email_verified as boolean | undefined,\n authTime: payload.auth_time as number,\n }\n } catch (error) {\n console.error(\"Token verification error:\", error)\n return {\n valid: false,\n error: error instanceof Error ? error.message : \"Invalid token\",\n }\n }\n}"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAA8C;AAG9C,MAAM,YAAY;AAAA,EAChB,SAAS,IAAI,IAAI,0FAA0F;AAAA,EAC3G,OAAO,IAAI,IAAI,mEAAmE;AACpF;AAGA,MAAM,OAAO;AAAA,EACT,aAAS,gCAAmB,IAAI,IAAI,UAAU,OAAO,GAAG;AAAA,IACtD,aAAa;AAAA;AAAA,IACb,iBAAiB;AAAA;AAAA,IACjB,kBAAkB;AAAA;AAAA,EACpB,CAAC;AAAA,EACD,WAAO,gCAAmB,IAAI,IAAI,UAAU,KAAK,GAAG;AAAA,IAClD,aAAa;AAAA;AAAA,IACb,iBAAiB;AAAA;AAAA,IACjB,kBAAkB;AAAA;AAAA,EACpB,CAAC;AACH;AAEF,eAAsB,oBAAoB,OAAe,kBAAkB,OAAO;AAChF,MAAI;AACF,UAAM,YAAY,QAAQ,IAAI;AAC9B,QAAI,CAAC,WAAW;AACd,YAAM,IAAI,MAAM,uCAAuC;AAAA,IACzD;AAEA,UAAM,SAAS,kBAAkB,KAAK,UAAU,KAAK;AAGrD,UAAM,EAAE,QAAQ,IAAI,UAAM,uBAAU,OAAO,QAAQ;AAAA,MACjD,QAAQ,kBACJ,yCAAyC,YACzC,oCAAoC;AAAA,MACxC,UAAU;AAAA,MACV,YAAY,CAAC,OAAO;AAAA,IACtB,CAAC;AAED,UAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AACxC,QAAI,QAAQ,OAAO,QAAQ,OAAO,KAAK;AACrC,YAAM,IAAI,MAAM,mBAAmB;AAAA,IACrC;AAEA,QAAI,QAAQ,OAAO,QAAQ,MAAM,KAAK;AACpC,YAAM,IAAI,MAAM,oCAAoC;AAAA,IACtD;AAEA,QAAI,CAAC,QAAQ,KAAK;AAChB,YAAM,IAAI,MAAM,wBAAwB;AAAA,IAC1C;AAEA,WAAO;AAAA,MACL,OAAO;AAAA,MACP,KAAK,QAAQ;AAAA,MACb,OAAO,QAAQ;AAAA,MACf,eAAe,QAAQ;AAAA,MACvB,UAAU,QAAQ;AAAA,IACpB;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,6BAA6B,KAAK;AAChD,WAAO;AAAA,MACL,OAAO;AAAA,MACP,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,IAClD;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../../../src/server/jwt-edge.ts"],"sourcesContent":["import { jwtVerify, createRemoteJWKSet } from \"jose\"\nimport { cache } from \"react\"\n\ninterface FirebaseIdTokenPayload {\n iss: string\n aud: string\n auth_time: number\n user_id: string\n sub: string\n iat: number\n exp: number\n email?: string\n email_verified?: boolean\n firebase: {\n identities: {\n [key: string]: any\n }\n sign_in_provider: string\n }\n}\n\n// Firebase public key endpoints\nconst FIREBASE_ID_TOKEN_URL = \"https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com\"\nconst FIREBASE_SESSION_CERT_URL = \"https://identitytoolkit.googleapis.com/v1/sessionCookiePublicKeys\"\n\n// Cache the JWKS using React cache\nconst getIdTokenJWKS = cache(() => {\n return createRemoteJWKSet(new URL(FIREBASE_ID_TOKEN_URL), {\n cacheMaxAge: 3600000, // 1 hour\n timeoutDuration: 5000, // 5 seconds\n cooldownDuration: 30000, // 30 seconds between retries\n })\n})\n\nconst getSessionJWKS = cache(() => {\n return createRemoteJWKSet(new URL(FIREBASE_SESSION_CERT_URL), {\n cacheMaxAge: 3600000, // 1 hour\n timeoutDuration: 5000, // 5 seconds\n cooldownDuration: 30000, // 30 seconds between retries\n })\n})\n\n// Helper to decode JWT without verification\nfunction decodeJwt(token: string) {\n try {\n const [headerB64, payloadB64] = token.split(\".\")\n const header = JSON.parse(Buffer.from(headerB64, \"base64\").toString())\n const payload = JSON.parse(Buffer.from(payloadB64, \"base64\").toString())\n return { header, payload }\n } catch (error) {\n console.error(\"Error decoding JWT:\", error)\n return null\n }\n}\n\nexport async function verifyFirebaseToken(token: string, isSessionCookie = false) {\n try {\n const projectId = process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID\n if (!projectId) {\n throw new Error(\"Firebase Project ID is not configured\")\n }\n\n // Decode token for debugging and type checking\n const decoded = decodeJwt(token)\n if (!decoded) {\n throw new Error(\"Invalid token format\")\n }\n\n console.log(\"Token details:\", {\n header: decoded.header,\n type: isSessionCookie ? \"session_cookie\" : \"id_token\",\n })\n\n\n // Use different JWKS based on token type\n const JWKS = isSessionCookie ? await getSessionJWKS() : await getIdTokenJWKS()\n\n const { payload } = await jwtVerify(token, JWKS, {\n issuer: isSessionCookie\n ? \"https://session.firebase.google.com/\" + projectId\n : \"https://securetoken.google.com/\" + projectId,\n audience: projectId,\n algorithms: [\"RS256\"],\n })\n\n const firebasePayload = payload as unknown as FirebaseIdTokenPayload\n const now = Math.floor(Date.now() / 1000)\n\n // Verify token claims\n if (firebasePayload.exp <= now) {\n throw new Error(\"Token has expired\")\n }\n\n if (firebasePayload.iat > now) {\n throw new Error(\"Token issued time is in the future\")\n }\n\n if (!firebasePayload.sub) {\n throw new Error(\"Token subject is empty\")\n }\n\n if (firebasePayload.auth_time > now) {\n throw new Error(\"Token auth time is in the future\")\n }\n\n return {\n valid: true,\n uid: firebasePayload.sub,\n email: firebasePayload.email,\n emailVerified: firebasePayload.email_verified,\n authTime: firebasePayload.auth_time,\n issuedAt: firebasePayload.iat,\n expiresAt: firebasePayload.exp,\n }\n } catch (error) {\n console.error(\"Token verification details:\", {\n error:\n error instanceof Error\n ? {\n name: error.name,\n message: error.message,\n stack: error.stack,\n }\n : error,\n decoded: decodeJwt(token),\n //projectId,\n isSessionCookie,\n })\n \n return {\n valid: false,\n error: error instanceof Error ? error.message : \"Invalid token\",\n }\n }\n }"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAA8C;AAC9C,mBAAsB;AAqBtB,MAAM,wBAAwB;AAC9B,MAAM,4BAA4B;AAGlC,MAAM,qBAAiB,oBAAM,MAAM;AACjC,aAAO,gCAAmB,IAAI,IAAI,qBAAqB,GAAG;AAAA,IACxD,aAAa;AAAA;AAAA,IACb,iBAAiB;AAAA;AAAA,IACjB,kBAAkB;AAAA;AAAA,EACpB,CAAC;AACH,CAAC;AAED,MAAM,qBAAiB,oBAAM,MAAM;AACjC,aAAO,gCAAmB,IAAI,IAAI,yBAAyB,GAAG;AAAA,IAC5D,aAAa;AAAA;AAAA,IACb,iBAAiB;AAAA;AAAA,IACjB,kBAAkB;AAAA;AAAA,EACpB,CAAC;AACH,CAAC;AAGD,SAAS,UAAU,OAAe;AAChC,MAAI;AACF,UAAM,CAAC,WAAW,UAAU,IAAI,MAAM,MAAM,GAAG;AAC/C,UAAM,SAAS,KAAK,MAAM,OAAO,KAAK,WAAW,QAAQ,EAAE,SAAS,CAAC;AACrE,UAAM,UAAU,KAAK,MAAM,OAAO,KAAK,YAAY,QAAQ,EAAE,SAAS,CAAC;AACvE,WAAO,EAAE,QAAQ,QAAQ;AAAA,EAC3B,SAAS,OAAO;AACd,YAAQ,MAAM,uBAAuB,KAAK;AAC1C,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,oBAAoB,OAAe,kBAAkB,OAAO;AAChF,MAAI;AACF,UAAM,YAAY,QAAQ,IAAI;AAC9B,QAAI,CAAC,WAAW;AACd,YAAM,IAAI,MAAM,uCAAuC;AAAA,IACzD;AAGA,UAAM,UAAU,UAAU,KAAK;AAC/B,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI,MAAM,sBAAsB;AAAA,IACxC;AAEA,YAAQ,IAAI,kBAAkB;AAAA,MAC5B,QAAQ,QAAQ;AAAA,MAChB,MAAM,kBAAkB,mBAAmB;AAAA,IAC7C,CAAC;AAID,UAAM,OAAO,kBAAkB,MAAM,eAAe,IAAI,MAAM,eAAe;AAE7E,UAAM,EAAE,QAAQ,IAAI,UAAM,uBAAU,OAAO,MAAM;AAAA,MAC3C,QAAQ,kBACJ,yCAAyC,YACzC,oCAAoC;AAAA,MACxC,UAAU;AAAA,MACV,YAAY,CAAC,OAAO;AAAA,IAC1B,CAAC;AAED,UAAM,kBAAkB;AACxB,UAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAGvC,QAAI,gBAAgB,OAAO,KAAK;AAC3B,YAAM,IAAI,MAAM,mBAAmB;AAAA,IACzC;AAEA,QAAI,gBAAgB,MAAM,KAAK;AACzB,YAAM,IAAI,MAAM,oCAAoC;AAAA,IAC1D;AAEA,QAAI,CAAC,gBAAgB,KAAK;AACpB,YAAM,IAAI,MAAM,wBAAwB;AAAA,IAC9C;AAEA,QAAI,gBAAgB,YAAY,KAAK;AAC/B,YAAM,IAAI,MAAM,kCAAkC;AAAA,IACxD;AAEA,WAAO;AAAA,MACD,OAAO;AAAA,MACP,KAAK,gBAAgB;AAAA,MACrB,OAAO,gBAAgB;AAAA,MACvB,eAAe,gBAAgB;AAAA,MAC/B,UAAU,gBAAgB;AAAA,MAC1B,UAAU,gBAAgB;AAAA,MAC1B,WAAW,gBAAgB;AAAA,IAC7B;AAAA,EACJ,SAAS,OAAO;AACZ,YAAQ,MAAM,+BAA+B;AAAA,MAC3C,OACE,iBAAiB,QACb;AAAA,QACE,MAAM,MAAM;AAAA,QACZ,SAAS,MAAM;AAAA,QACf,OAAO,MAAM;AAAA,MACf,IACA;AAAA,MACN,SAAS,UAAU,KAAK;AAAA;AAAA,MAExB;AAAA,IACF,CAAC;AAED,WAAO;AAAA,MACL,OAAO;AAAA,MACP,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,IAClD;AAAA,EACF;AACF;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/server/ternSecureMiddleware.ts"],"sourcesContent":["import { NextRequest, NextResponse } from 'next/server';\nimport { verifySession, type UserInfo } from './edge-session'\n\nexport const runtime = \"edge\"\n\n\ninterface Auth {\n user: UserInfo | null\n token: string | null\n protect: () => Promise<void>\n}\n\ntype MiddlewareCallback = (\n auth: Auth,\n request: NextRequest\n) => Promise<void>\n\n\n/**\n * Create a route matcher function for public paths\n */\nexport function createRouteMatcher(patterns: string[]) {\n return (request: NextRequest): boolean => {\n const { pathname } = request.nextUrl\n return patterns.some(pattern => {\n // Convert route pattern to regex\n const regexPattern = new RegExp(\n `^${pattern.replace(/\\*/g, '.*').replace(/\\((.*)\\)/, '(?:$1)?')}$`\n )\n return regexPattern.test(pathname)\n })\n }\n}\n\n\n/**\n * Edge-compatible auth check\n */\nasync function edgeAuth(request: NextRequest): Promise<Auth> {\n async function protect() {\n throw new Error(\"Unauthorized access\")\n }\n\n try {\n const sessionResult = await verifySession(request)\n\n if (sessionResult.isAuthenticated && sessionResult.user) {\n return {\n user: sessionResult.user,\n token: request.cookies.get(\"_session_cookie\")?.value || request.cookies.get(\"_session_token\")?.value || null,\n protect: async () => {},\n }\n }\n\n return {\n user: null,\n token: null,\n protect,\n }\n } catch (error) {\n console.error(\"Auth check error:\", error)\n return {\n user: null,\n token: null,\n protect,\n }\n }\n}\n\n\n\n/**\n * Middleware factory that handles authentication and custom logic\n * @param customHandler Optional function for additional custom logic\n */\n\nexport function ternSecureMiddleware(callback: MiddlewareCallback) {\n return async function middleware(request: NextRequest) {\n try {\n const auth = await edgeAuth(request)\n\n try {\n \n await callback(auth, request)\n\n const response = NextResponse.next()\n\n if (auth.user) {\n // Set auth headers\n response.headers.set(\"x-user-id\", auth.user.uid)\n if (auth.user.email) {\n response.headers.set(\"x-user-email\", auth.user.email)\n }\n if (auth.user.emailVerified !== undefined) {\n response.headers.set(\"x-email-verified\", auth.user.emailVerified.toString())\n }\n if (auth.user.authTime) {\n response.headers.set(\"x-auth-time\", auth.user.authTime.toString())\n }\n }\n\n return response\n } catch (error) {\n // Handle unauthorized access\n if (error instanceof Error && error.message === 'Unauthorized access') {\n const redirectUrl = new URL('/sign-in', request.url)\n redirectUrl.searchParams.set('redirect', request.nextUrl.pathname)\n return NextResponse.redirect(redirectUrl)\n }\n throw error\n }\n\n } catch (error) {\n console.error(\"Middleware error:\", {\n error:\n error instanceof Error\n ? {\n name: error.name,\n message: error.message,\n stack: error.stack,\n }\n : error,\n path: request.nextUrl.pathname,\n })\n\n const redirectUrl = new URL(\"/sign-in\", request.url)\n return NextResponse.redirect(redirectUrl)\n }\n }\n}"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAA0C;AAC1C,0BAA6C;AAEtC,MAAM,UAAU;AAkBhB,SAAS,mBAAmB,UAAoB;AACrD,SAAO,CAAC,YAAkC;AACxC,UAAM,EAAE,SAAS,IAAI,QAAQ;AAC7B,WAAO,SAAS,KAAK,aAAW;AAE9B,YAAM,eAAe,IAAI;AAAA,QACvB,IAAI,QAAQ,QAAQ,OAAO,IAAI,EAAE,QAAQ,YAAY,SAAS,CAAC;AAAA,MACjE;AACA,aAAO,aAAa,KAAK,QAAQ;AAAA,IACnC,CAAC;AAAA,EACH;AACF;AAMA,eAAe,SAAS,SAAqC;AAtC7D;AAuCE,iBAAe,UAAU;AACvB,UAAM,IAAI,MAAM,qBAAqB;AAAA,EACvC;AAEA,MAAI;AACF,UAAM,gBAAgB,UAAM,mCAAc,OAAO;AAEjD,QAAI,cAAc,mBAAmB,cAAc,MAAM;AACvD,aAAO;AAAA,QACL,MAAM,cAAc;AAAA,QACpB,SAAO,aAAQ,QAAQ,IAAI,iBAAiB,MAArC,mBAAwC,YAAS,aAAQ,QAAQ,IAAI,gBAAgB,MAApC,mBAAuC,UAAS;AAAA,QACxG,SAAS,YAAY;AAAA,QAAC;AAAA,MACxB;AAAA,IACF;AAEA,WAAO;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AAAA,MACP;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,qBAAqB,KAAK;AACxC,WAAO;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AAAA,MACP;AAAA,IACF;AAAA,EACF;AACF;AASO,SAAS,qBAAqB,UAA8B;AACjE,SAAO,eAAe,WAAW,SAAsB;AACrD,QAAI;AACF,YAAM,OAAO,MAAM,SAAS,OAAO;AAEnC,UAAI;AAEF,cAAM,SAAS,MAAM,OAAO;AAE5B,cAAM,WAAW,2BAAa,KAAK;AAEnC,YAAI,KAAK,MAAM;AAEb,mBAAS,QAAQ,IAAI,aAAa,KAAK,KAAK,GAAG;AAC/C,cAAI,KAAK,KAAK,OAAO;AACnB,qBAAS,QAAQ,IAAI,gBAAgB,KAAK,KAAK,KAAK;AAAA,UACtD;AACA,cAAI,KAAK,KAAK,kBAAkB,QAAW;AACzC,qBAAS,QAAQ,IAAI,oBAAoB,KAAK,KAAK,cAAc,SAAS,CAAC;AAAA,UAC7E;AACA,cAAI,KAAK,KAAK,UAAU;AACtB,qBAAS,QAAQ,IAAI,eAAe,KAAK,KAAK,SAAS,SAAS,CAAC;AAAA,UACnE;AAAA,QACF;AAEA,eAAO;AAAA,MACT,SAAS,OAAO;AAEd,YAAI,iBAAiB,SAAS,MAAM,YAAY,uBAAuB;AACrE,gBAAM,cAAc,IAAI,IAAI,YAAY,QAAQ,GAAG;AACnD,sBAAY,aAAa,IAAI,YAAY,QAAQ,QAAQ,QAAQ;AACjE,iBAAO,2BAAa,SAAS,WAAW;AAAA,QAC1C;AACA,cAAM;AAAA,MACR;AAAA,IAEF,SAAS,OAAO;AACd,cAAQ,MAAM,qBAAqB;AAAA,QACjC,OACE,iBAAiB,QACb;AAAA,UACE,MAAM,MAAM;AAAA,UACZ,SAAS,MAAM;AAAA,UACf,OAAO,MAAM;AAAA,QACf,IACA;AAAA,QACN,MAAM,QAAQ,QAAQ;AAAA,MACxB,CAAC;AAED,YAAM,cAAc,IAAI,IAAI,YAAY,QAAQ,GAAG;AACnD,aAAO,2BAAa,SAAS,WAAW;AAAA,IAC1C;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../../../src/server/ternSecureMiddleware.ts"],"sourcesContent":["import { NextRequest, NextResponse } from 'next/server';\nimport { verifySession } from './edge-session'\nimport type { UserInfo } from './types'\n\n\nexport const runtime = \"edge\"\n\n\ninterface Auth {\n user: UserInfo | null\n token: string | null\n protect: () => Promise<void>\n}\n\ntype MiddlewareCallback = (\n auth: Auth,\n request: NextRequest\n) => Promise<void>\n\n\n/**\n * Create a route matcher function for public paths\n */\nexport function createRouteMatcher(patterns: string[]) {\n return (request: NextRequest): boolean => {\n const { pathname } = request.nextUrl\n return patterns.some(pattern => {\n // Convert route pattern to regex\n const regexPattern = new RegExp(\n `^${pattern.replace(/\\*/g, '.*').replace(/\\((.*)\\)/, '(?:$1)?')}$`\n )\n return regexPattern.test(pathname)\n })\n }\n}\n\n\n/**\n * Edge-compatible auth check\n */\nasync function edgeAuth(request: NextRequest): Promise<Auth> {\n async function protect() {\n throw new Error(\"Unauthorized access\")\n }\n\n try {\n const sessionResult = await verifySession(request)\n\n if (sessionResult.isAuthenticated && sessionResult.user) {\n return {\n user: sessionResult.user,\n token: request.cookies.get(\"_session_cookie\")?.value || request.cookies.get(\"_session_token\")?.value || null,\n protect: async () => {},\n }\n }\n\n return {\n user: null,\n token: null,\n protect,\n }\n } catch (error) {\n console.error(\"Auth check error:\", error)\n return {\n user: null,\n token: null,\n protect,\n }\n }\n}\n\n\n\n/**\n * Middleware factory that handles authentication and custom logic\n * @param customHandler Optional function for additional custom logic\n */\n\nexport function ternSecureMiddleware(callback: MiddlewareCallback) {\n return async function middleware(request: NextRequest) {\n try {\n const auth = await edgeAuth(request)\n\n try {\n \n await callback(auth, request)\n\n const response = NextResponse.next()\n\n if (auth.user) {\n // Set auth headers\n response.headers.set(\"x-user-id\", auth.user.uid)\n if (auth.user.email) {\n response.headers.set(\"x-user-email\", auth.user.email)\n }\n if (auth.user.emailVerified !== undefined) {\n response.headers.set(\"x-email-verified\", auth.user.emailVerified.toString())\n }\n if (auth.user.authTime) {\n response.headers.set(\"x-auth-time\", auth.user.authTime.toString())\n }\n }\n\n return response\n } catch (error) {\n // Handle unauthorized access\n if (error instanceof Error && error.message === 'Unauthorized access') {\n const redirectUrl = new URL('/sign-in', request.url)\n redirectUrl.searchParams.set('redirect', request.nextUrl.pathname)\n return NextResponse.redirect(redirectUrl)\n }\n throw error\n }\n\n } catch (error) {\n console.error(\"Middleware error:\", {\n error:\n error instanceof Error\n ? {\n name: error.name,\n message: error.message,\n stack: error.stack,\n }\n : error,\n path: request.nextUrl.pathname,\n })\n\n const redirectUrl = new URL(\"/sign-in\", request.url)\n return NextResponse.redirect(redirectUrl)\n }\n }\n}"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAA0C;AAC1C,0BAA8B;AAIvB,MAAM,UAAU;AAkBhB,SAAS,mBAAmB,UAAoB;AACrD,SAAO,CAAC,YAAkC;AACxC,UAAM,EAAE,SAAS,IAAI,QAAQ;AAC7B,WAAO,SAAS,KAAK,aAAW;AAE9B,YAAM,eAAe,IAAI;AAAA,QACvB,IAAI,QAAQ,QAAQ,OAAO,IAAI,EAAE,QAAQ,YAAY,SAAS,CAAC;AAAA,MACjE;AACA,aAAO,aAAa,KAAK,QAAQ;AAAA,IACnC,CAAC;AAAA,EACH;AACF;AAMA,eAAe,SAAS,SAAqC;AAxC7D;AAyCE,iBAAe,UAAU;AACvB,UAAM,IAAI,MAAM,qBAAqB;AAAA,EACvC;AAEA,MAAI;AACF,UAAM,gBAAgB,UAAM,mCAAc,OAAO;AAEjD,QAAI,cAAc,mBAAmB,cAAc,MAAM;AACvD,aAAO;AAAA,QACL,MAAM,cAAc;AAAA,QACpB,SAAO,aAAQ,QAAQ,IAAI,iBAAiB,MAArC,mBAAwC,YAAS,aAAQ,QAAQ,IAAI,gBAAgB,MAApC,mBAAuC,UAAS;AAAA,QACxG,SAAS,YAAY;AAAA,QAAC;AAAA,MACxB;AAAA,IACF;AAEA,WAAO;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AAAA,MACP;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,qBAAqB,KAAK;AACxC,WAAO;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AAAA,MACP;AAAA,IACF;AAAA,EACF;AACF;AASO,SAAS,qBAAqB,UAA8B;AACjE,SAAO,eAAe,WAAW,SAAsB;AACrD,QAAI;AACF,YAAM,OAAO,MAAM,SAAS,OAAO;AAEnC,UAAI;AAEF,cAAM,SAAS,MAAM,OAAO;AAE5B,cAAM,WAAW,2BAAa,KAAK;AAEnC,YAAI,KAAK,MAAM;AAEb,mBAAS,QAAQ,IAAI,aAAa,KAAK,KAAK,GAAG;AAC/C,cAAI,KAAK,KAAK,OAAO;AACnB,qBAAS,QAAQ,IAAI,gBAAgB,KAAK,KAAK,KAAK;AAAA,UACtD;AACA,cAAI,KAAK,KAAK,kBAAkB,QAAW;AACzC,qBAAS,QAAQ,IAAI,oBAAoB,KAAK,KAAK,cAAc,SAAS,CAAC;AAAA,UAC7E;AACA,cAAI,KAAK,KAAK,UAAU;AACtB,qBAAS,QAAQ,IAAI,eAAe,KAAK,KAAK,SAAS,SAAS,CAAC;AAAA,UACnE;AAAA,QACF;AAEA,eAAO;AAAA,MACT,SAAS,OAAO;AAEd,YAAI,iBAAiB,SAAS,MAAM,YAAY,uBAAuB;AACrE,gBAAM,cAAc,IAAI,IAAI,YAAY,QAAQ,GAAG;AACnD,sBAAY,aAAa,IAAI,YAAY,QAAQ,QAAQ,QAAQ;AACjE,iBAAO,2BAAa,SAAS,WAAW;AAAA,QAC1C;AACA,cAAM;AAAA,MACR;AAAA,IAEF,SAAS,OAAO;AACd,cAAQ,MAAM,qBAAqB;AAAA,QACjC,OACE,iBAAiB,QACb;AAAA,UACE,MAAM,MAAM;AAAA,UACZ,SAAS,MAAM;AAAA,UACf,OAAO,MAAM;AAAA,QACf,IACA;AAAA,QACN,MAAM,QAAQ,QAAQ;AAAA,MACxB,CAAC;AAED,YAAM,cAAc,IAAI,IAAI,YAAY,QAAQ,GAAG;AACnD,aAAO,2BAAa,SAAS,WAAW;AAAA,IAC1C;AAAA,EACF;AACF;","names":[]}
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __copyProps = (to, from, except, desc) => {
7
+ if (from && typeof from === "object" || typeof from === "function") {
8
+ for (let key of __getOwnPropNames(from))
9
+ if (!__hasOwnProp.call(to, key) && key !== except)
10
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
11
+ }
12
+ return to;
13
+ };
14
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
15
+ var types_exports = {};
16
+ module.exports = __toCommonJS(types_exports);
17
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/server/types.ts"],"sourcesContent":["export interface UserInfo {\n uid: string\n email: string | null\n emailVerified?: boolean\n authTime?: number\n disabled?: boolean\n }\n \n \n export interface SessionResult {\n isAuthenticated: boolean\n user: UserInfo | null\n error?: string\n }"],"mappings":";;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
@@ -27,7 +27,7 @@ async function auth() {
27
27
  error: new Error("No valid session or token found")
28
28
  };
29
29
  } catch (error) {
30
- console.error("Error in getAuthResult:", error);
30
+ console.error("Error in get AuthResult:", error);
31
31
  return {
32
32
  user: null,
33
33
  token: null,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/server/auth.ts"],"sourcesContent":["'use server'\nimport { cookies, headers } from \"next/headers\"\nimport type { UserInfo, SessionResult } from \"./edge-session\"\n\n\nexport interface AuthResult {\n user: UserInfo | null\n token: string | null\n error: Error | null\n}\n\n\n /**\n * Get the current authenticated user from the session or token\n */\n export async function auth(): Promise<AuthResult> {\n try {\n const headersList = await headers()\n const cookieStore = await cookies()\n\n const userId = headersList.get('x-user-id')\n const authTime = headersList.get('x-auth-time')\n const emailVerified = headersList.get('x-auth-verified') === 'true'\n\n if (userId) {\n const token = cookieStore.get(\"_session_cookie\")?.value || \n cookieStore.get(\"_session_token\")?.value || \n null\n \n return {\n user: {\n uid: userId,\n email: headersList.get('x-user-email') || null,\n emailVerified,\n authTime: authTime ? parseInt(authTime) : undefined\n },\n token,\n error: null\n }\n }\n\n return {\n user: null,\n token: null,\n error: new Error(\"No valid session or token found\"),\n }\n } catch (error) {\n console.error(\"Error in getAuthResult:\", error)\n return {\n user: null,\n token: null,\n error: error instanceof Error ? error : new Error(\"An unknown error occurred\"),\n }\n }\n}\n\n/**\n * Type guard to check if user is authenticated\n */\nexport async function isAuthenticated(): Promise<boolean> {\n const authResult = await auth()\n return authResult.user !== null\n}\n\n/**\n * Get user info from auth result\n */\nexport async function getUserInfo(): Promise<UserInfo | null> {\n const authResult = await auth()\n if (!authResult.user) {\n return null\n }\n\n return {\n uid: authResult.user.uid,\n email: authResult.user.email,\n emailVerified: authResult.user.emailVerified,\n authTime: authResult.user.authTime\n }\n }\n\n"],"mappings":";AACA,SAAS,SAAS,eAAe;AAc/B,eAAsB,OAA4B;AAfpD;AAgBI,MAAI;AACF,UAAM,cAAc,MAAM,QAAQ;AAClC,UAAM,cAAc,MAAM,QAAQ;AAElC,UAAM,SAAS,YAAY,IAAI,WAAW;AAC1C,UAAM,WAAW,YAAY,IAAI,aAAa;AAC9C,UAAM,gBAAgB,YAAY,IAAI,iBAAiB,MAAM;AAE7D,QAAI,QAAQ;AACV,YAAM,UAAQ,iBAAY,IAAI,iBAAiB,MAAjC,mBAAoC,YACrC,iBAAY,IAAI,gBAAgB,MAAhC,mBAAmC,UACnC;AAEb,aAAO;AAAA,QACL,MAAM;AAAA,UACJ,KAAK;AAAA,UACL,OAAO,YAAY,IAAI,cAAc,KAAK;AAAA,UAC1C;AAAA,UACA,UAAU,WAAW,SAAS,QAAQ,IAAI;AAAA,QAC5C;AAAA,QACA;AAAA,QACA,OAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AAAA,MACP,OAAO,IAAI,MAAM,iCAAiC;AAAA,IACpD;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,2BAA2B,KAAK;AAC9C,WAAO;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AAAA,MACP,OAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,2BAA2B;AAAA,IAC/E;AAAA,EACF;AACJ;AAKA,eAAsB,kBAAoC;AACxD,QAAM,aAAa,MAAM,KAAK;AAC9B,SAAO,WAAW,SAAS;AAC7B;AAKA,eAAsB,cAAwC;AAC5D,QAAM,aAAa,MAAM,KAAK;AAC9B,MAAI,CAAC,WAAW,MAAM;AACpB,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,KAAK,WAAW,KAAK;AAAA,IACrB,OAAO,WAAW,KAAK;AAAA,IACvB,eAAe,WAAW,KAAK;AAAA,IAC/B,UAAU,WAAW,KAAK;AAAA,EAC5B;AACA;","names":[]}
1
+ {"version":3,"sources":["../../../src/server/auth.ts"],"sourcesContent":["'use server'\nimport { cookies, headers } from \"next/headers\"\nimport type { UserInfo } from \"./types\"\n\n\nexport interface AuthResult {\n user: UserInfo | null\n token: string | null\n error: Error | null\n}\n\n\n /**\n * Get the current authenticated user from the session or token\n */\n export async function auth(): Promise<AuthResult> {\n try {\n const headersList = await headers()\n const cookieStore = await cookies()\n\n const userId = headersList.get('x-user-id')\n const authTime = headersList.get('x-auth-time')\n const emailVerified = headersList.get('x-auth-verified') === 'true'\n\n if (userId) {\n const token = cookieStore.get(\"_session_cookie\")?.value || \n cookieStore.get(\"_session_token\")?.value || \n null\n \n return {\n user: {\n uid: userId,\n email: headersList.get('x-user-email') || null,\n emailVerified,\n authTime: authTime ? parseInt(authTime) : undefined\n },\n token,\n error: null\n }\n }\n\n return {\n user: null,\n token: null,\n error: new Error(\"No valid session or token found\"),\n }\n } catch (error) {\n console.error(\"Error in get AuthResult:\", error)\n return {\n user: null,\n token: null,\n error: error instanceof Error ? error : new Error(\"An unknown error occurred\"),\n }\n }\n}\n\n/**\n * Type guard to check if user is authenticated\n */\nexport async function isAuthenticated(): Promise<boolean> {\n const authResult = await auth()\n return authResult.user !== null\n}\n\n/**\n * Get user info from auth result\n */\nexport async function getUserInfo(): Promise<UserInfo | null> {\n const authResult = await auth()\n if (!authResult.user) {\n return null\n }\n\n return {\n uid: authResult.user.uid,\n email: authResult.user.email,\n emailVerified: authResult.user.emailVerified,\n authTime: authResult.user.authTime\n }\n }\n\n"],"mappings":";AACA,SAAS,SAAS,eAAe;AAc/B,eAAsB,OAA4B;AAfpD;AAgBI,MAAI;AACF,UAAM,cAAc,MAAM,QAAQ;AAClC,UAAM,cAAc,MAAM,QAAQ;AAElC,UAAM,SAAS,YAAY,IAAI,WAAW;AAC1C,UAAM,WAAW,YAAY,IAAI,aAAa;AAC9C,UAAM,gBAAgB,YAAY,IAAI,iBAAiB,MAAM;AAE7D,QAAI,QAAQ;AACV,YAAM,UAAQ,iBAAY,IAAI,iBAAiB,MAAjC,mBAAoC,YACrC,iBAAY,IAAI,gBAAgB,MAAhC,mBAAmC,UACnC;AAEb,aAAO;AAAA,QACL,MAAM;AAAA,UACJ,KAAK;AAAA,UACL,OAAO,YAAY,IAAI,cAAc,KAAK;AAAA,UAC1C;AAAA,UACA,UAAU,WAAW,SAAS,QAAQ,IAAI;AAAA,QAC5C;AAAA,QACA;AAAA,QACA,OAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AAAA,MACP,OAAO,IAAI,MAAM,iCAAiC;AAAA,IACpD;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,4BAA4B,KAAK;AAC/C,WAAO;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AAAA,MACP,OAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,2BAA2B;AAAA,IAC/E;AAAA,EACF;AACJ;AAKA,eAAsB,kBAAoC;AACxD,QAAM,aAAa,MAAM,KAAK;AAC9B,SAAO,WAAW,SAAS;AAC7B;AAKA,eAAsB,cAAwC;AAC5D,QAAM,aAAa,MAAM,KAAK;AAC9B,MAAI,CAAC,WAAW,MAAM;AACpB,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,KAAK,WAAW,KAAK;AAAA,IACrB,OAAO,WAAW,KAAK;AAAA,IACvB,eAAe,WAAW,KAAK;AAAA,IAC/B,UAAU,WAAW,KAAK;AAAA,EAC5B;AACA;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/server/edge-session.ts"],"sourcesContent":["import { verifyFirebaseToken } from \"./jwt-edge\"\nimport type { NextRequest } from \"next/server\"\n\nexport interface UserInfo {\n uid: string\n email: string | null\n emailVerified?: boolean\n authTime?: number\n disabled?: boolean\n}\n\n\nexport interface SessionResult {\n isAuthenticated: boolean\n user: UserInfo | null\n error?: string\n}\n\n\nexport async function verifySession(request: NextRequest): Promise<SessionResult> {\n try {\n //const cookieStore = await cookies()\n\n // First try session cookie\n const sessionCookie = request.cookies.get(\"_session_cookie\")?.value\n if (sessionCookie) {\n const result = await verifyFirebaseToken(sessionCookie, true)\n if (result.valid) {\n return {\n isAuthenticated: true,\n user: {\n uid: result.uid ?? '',\n email: result.email || null,\n emailVerified: result.emailVerified ?? false,\n disabled: false,\n },\n }\n }\n console.log(\"Session cookie verification failed:\", result.error)\n }\n\n // Then try ID token\n const idToken = request.cookies.get(\"_session_token\")?.value\n if (idToken) {\n const result = await verifyFirebaseToken(idToken, false)\n if (result.valid) {\n return {\n isAuthenticated: true,\n user: {\n uid: result.uid ?? '',\n email: result.email || null,\n emailVerified: result.emailVerified ?? false,\n disabled: false,\n },\n }\n }\n console.log(\"ID token verification failed:\", result.error)\n }\n\n return {\n isAuthenticated: false,\n user: null,\n error: \"No valid session found\",\n }\n } catch (error) {\n console.error(\"Session verification error:\", error)\n return {\n isAuthenticated: false,\n user: null,\n error: error instanceof Error ? error.message : \"Session verification failed\",\n }\n }\n}"],"mappings":"AAAA,SAAS,2BAA2B;AAmBpC,eAAsB,cAAc,SAA8C;AAnBlF;AAoBE,MAAI;AAIF,UAAM,iBAAgB,aAAQ,QAAQ,IAAI,iBAAiB,MAArC,mBAAwC;AAC9D,QAAI,eAAe;AACjB,YAAM,SAAS,MAAM,oBAAoB,eAAe,IAAI;AAC5D,UAAI,OAAO,OAAO;AAChB,eAAO;AAAA,UACL,iBAAiB;AAAA,UACjB,MAAM;AAAA,YACJ,MAAK,YAAO,QAAP,YAAc;AAAA,YACnB,OAAO,OAAO,SAAS;AAAA,YACvB,gBAAe,YAAO,kBAAP,YAAwB;AAAA,YACvC,UAAU;AAAA,UACZ;AAAA,QACF;AAAA,MACF;AACA,cAAQ,IAAI,uCAAuC,OAAO,KAAK;AAAA,IACjE;AAGA,UAAM,WAAU,aAAQ,QAAQ,IAAI,gBAAgB,MAApC,mBAAuC;AACvD,QAAI,SAAS;AACX,YAAM,SAAS,MAAM,oBAAoB,SAAS,KAAK;AACvD,UAAI,OAAO,OAAO;AAChB,eAAO;AAAA,UACL,iBAAiB;AAAA,UACjB,MAAM;AAAA,YACJ,MAAK,YAAO,QAAP,YAAc;AAAA,YACnB,OAAO,OAAO,SAAS;AAAA,YACvB,gBAAe,YAAO,kBAAP,YAAwB;AAAA,YACvC,UAAU;AAAA,UACZ;AAAA,QACF;AAAA,MACF;AACA,cAAQ,IAAI,iCAAiC,OAAO,KAAK;AAAA,IAC3D;AAEA,WAAO;AAAA,MACL,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,OAAO;AAAA,IACT;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,+BAA+B,KAAK;AAClD,WAAO;AAAA,MACL,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,IAClD;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../../../src/server/edge-session.ts"],"sourcesContent":["import { verifyFirebaseToken } from \"./jwt-edge\"\nimport type { NextRequest } from \"next/server\"\nimport type { SessionResult } from \"./types\"\n\n\nexport async function verifySession(request: NextRequest): Promise<SessionResult> {\n try {\n //const cookieStore = await cookies()\n\n // First try session cookie\n const sessionCookie = request.cookies.get(\"_session_cookie\")?.value\n if (sessionCookie) {\n const result = await verifyFirebaseToken(sessionCookie, true)\n if (result.valid) {\n return {\n isAuthenticated: true,\n user: {\n uid: result.uid ?? '',\n email: result.email || null,\n emailVerified: result.emailVerified ?? false,\n disabled: false,\n },\n }\n }\n console.log(\"Session cookie verification failed:\", result.error)\n }\n\n // Then try ID token\n const idToken = request.cookies.get(\"_session_token\")?.value\n if (idToken) {\n const result = await verifyFirebaseToken(idToken, false)\n if (result.valid) {\n return {\n isAuthenticated: true,\n user: {\n uid: result.uid ?? '',\n email: result.email || null,\n emailVerified: result.emailVerified ?? false,\n disabled: false,\n },\n }\n }\n console.log(\"ID token verification failed:\", result.error)\n }\n\n return {\n isAuthenticated: false,\n user: null,\n error: \"No valid session found\",\n }\n } catch (error) {\n console.error(\"Session verification error:\", error)\n return {\n isAuthenticated: false,\n user: null,\n error: error instanceof Error ? error.message : \"Session verification failed\",\n }\n }\n}"],"mappings":"AAAA,SAAS,2BAA2B;AAKpC,eAAsB,cAAc,SAA8C;AALlF;AAME,MAAI;AAIF,UAAM,iBAAgB,aAAQ,QAAQ,IAAI,iBAAiB,MAArC,mBAAwC;AAC9D,QAAI,eAAe;AACjB,YAAM,SAAS,MAAM,oBAAoB,eAAe,IAAI;AAC5D,UAAI,OAAO,OAAO;AAChB,eAAO;AAAA,UACL,iBAAiB;AAAA,UACjB,MAAM;AAAA,YACJ,MAAK,YAAO,QAAP,YAAc;AAAA,YACnB,OAAO,OAAO,SAAS;AAAA,YACvB,gBAAe,YAAO,kBAAP,YAAwB;AAAA,YACvC,UAAU;AAAA,UACZ;AAAA,QACF;AAAA,MACF;AACA,cAAQ,IAAI,uCAAuC,OAAO,KAAK;AAAA,IACjE;AAGA,UAAM,WAAU,aAAQ,QAAQ,IAAI,gBAAgB,MAApC,mBAAuC;AACvD,QAAI,SAAS;AACX,YAAM,SAAS,MAAM,oBAAoB,SAAS,KAAK;AACvD,UAAI,OAAO,OAAO;AAChB,eAAO;AAAA,UACL,iBAAiB;AAAA,UACjB,MAAM;AAAA,YACJ,MAAK,YAAO,QAAP,YAAc;AAAA,YACnB,OAAO,OAAO,SAAS;AAAA,YACvB,gBAAe,YAAO,kBAAP,YAAwB;AAAA,YACvC,UAAU;AAAA,UACZ;AAAA,QACF;AAAA,MACF;AACA,cAAQ,IAAI,iCAAiC,OAAO,KAAK;AAAA,IAC3D;AAEA,WAAO;AAAA,MACL,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,OAAO;AAAA,IACT;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,+BAA+B,KAAK;AAClD,WAAO;AAAA,MACL,iBAAiB;AAAA,MACjB,MAAM;AAAA,MACN,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,IAClD;AAAA,EACF;AACF;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/server/index.ts"],"sourcesContent":["\n\nexport { ternSecureMiddleware, createRouteMatcher } from './ternSecureMiddleware'\nexport { auth, getUserInfo } from './auth'\nexport type { AuthResult } from './auth'"],"mappings":"AAEA,SAAS,sBAAsB,0BAA0B;AACzD,SAAS,MAAM,mBAAmB;","names":[]}
1
+ {"version":3,"sources":["../../../src/server/index.ts"],"sourcesContent":["\n\nexport { ternSecureMiddleware, createRouteMatcher } from './ternSecureMiddleware'\nexport { auth, getUserInfo } from './auth'\nexport type { AuthResult } from './auth'\nexport type { UserInfo, SessionResult } from './types'"],"mappings":"AAEA,SAAS,sBAAsB,0BAA0B;AACzD,SAAS,MAAM,mBAAmB;","names":[]}
@@ -1,57 +1,92 @@
1
1
  import { jwtVerify, createRemoteJWKSet } from "jose";
2
- const JWKS_URLS = {
3
- session: new URL("https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com"),
4
- token: new URL("https://identitytoolkit.googleapis.com/v1/sessionCookiePublicKeys")
5
- };
6
- const JWKS = {
7
- session: createRemoteJWKSet(new URL(JWKS_URLS.session), {
2
+ import { cache } from "react";
3
+ const FIREBASE_ID_TOKEN_URL = "https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com";
4
+ const FIREBASE_SESSION_CERT_URL = "https://identitytoolkit.googleapis.com/v1/sessionCookiePublicKeys";
5
+ const getIdTokenJWKS = cache(() => {
6
+ return createRemoteJWKSet(new URL(FIREBASE_ID_TOKEN_URL), {
8
7
  cacheMaxAge: 36e5,
9
8
  // 1 hour
10
9
  timeoutDuration: 5e3,
11
10
  // 5 seconds
12
11
  cooldownDuration: 3e4
13
12
  // 30 seconds between retries
14
- }),
15
- token: createRemoteJWKSet(new URL(JWKS_URLS.token), {
13
+ });
14
+ });
15
+ const getSessionJWKS = cache(() => {
16
+ return createRemoteJWKSet(new URL(FIREBASE_SESSION_CERT_URL), {
16
17
  cacheMaxAge: 36e5,
17
18
  // 1 hour
18
19
  timeoutDuration: 5e3,
19
20
  // 5 seconds
20
21
  cooldownDuration: 3e4
21
22
  // 30 seconds between retries
22
- })
23
- };
23
+ });
24
+ });
25
+ function decodeJwt(token) {
26
+ try {
27
+ const [headerB64, payloadB64] = token.split(".");
28
+ const header = JSON.parse(Buffer.from(headerB64, "base64").toString());
29
+ const payload = JSON.parse(Buffer.from(payloadB64, "base64").toString());
30
+ return { header, payload };
31
+ } catch (error) {
32
+ console.error("Error decoding JWT:", error);
33
+ return null;
34
+ }
35
+ }
24
36
  async function verifyFirebaseToken(token, isSessionCookie = false) {
25
37
  try {
26
38
  const projectId = process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID;
27
39
  if (!projectId) {
28
40
  throw new Error("Firebase Project ID is not configured");
29
41
  }
30
- const keySet = isSessionCookie ? JWKS.session : JWKS.token;
31
- const { payload } = await jwtVerify(token, keySet, {
42
+ const decoded = decodeJwt(token);
43
+ if (!decoded) {
44
+ throw new Error("Invalid token format");
45
+ }
46
+ console.log("Token details:", {
47
+ header: decoded.header,
48
+ type: isSessionCookie ? "session_cookie" : "id_token"
49
+ });
50
+ const JWKS = isSessionCookie ? await getSessionJWKS() : await getIdTokenJWKS();
51
+ const { payload } = await jwtVerify(token, JWKS, {
32
52
  issuer: isSessionCookie ? "https://session.firebase.google.com/" + projectId : "https://securetoken.google.com/" + projectId,
33
53
  audience: projectId,
34
54
  algorithms: ["RS256"]
35
55
  });
56
+ const firebasePayload = payload;
36
57
  const now = Math.floor(Date.now() / 1e3);
37
- if (payload.exp && payload.exp <= now) {
58
+ if (firebasePayload.exp <= now) {
38
59
  throw new Error("Token has expired");
39
60
  }
40
- if (payload.iat && payload.iat > now) {
61
+ if (firebasePayload.iat > now) {
41
62
  throw new Error("Token issued time is in the future");
42
63
  }
43
- if (!payload.sub) {
64
+ if (!firebasePayload.sub) {
44
65
  throw new Error("Token subject is empty");
45
66
  }
67
+ if (firebasePayload.auth_time > now) {
68
+ throw new Error("Token auth time is in the future");
69
+ }
46
70
  return {
47
71
  valid: true,
48
- uid: payload.sub,
49
- email: payload.email,
50
- emailVerified: payload.email_verified,
51
- authTime: payload.auth_time
72
+ uid: firebasePayload.sub,
73
+ email: firebasePayload.email,
74
+ emailVerified: firebasePayload.email_verified,
75
+ authTime: firebasePayload.auth_time,
76
+ issuedAt: firebasePayload.iat,
77
+ expiresAt: firebasePayload.exp
52
78
  };
53
79
  } catch (error) {
54
- console.error("Token verification error:", error);
80
+ console.error("Token verification details:", {
81
+ error: error instanceof Error ? {
82
+ name: error.name,
83
+ message: error.message,
84
+ stack: error.stack
85
+ } : error,
86
+ decoded: decodeJwt(token),
87
+ //projectId,
88
+ isSessionCookie
89
+ });
55
90
  return {
56
91
  valid: false,
57
92
  error: error instanceof Error ? error.message : "Invalid token"
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/server/jwt-edge.ts"],"sourcesContent":["import { jwtVerify, createRemoteJWKSet } from \"jose\"\n\n// Firebase public key endpoints with simplified configuration for Edge\nconst JWKS_URLS = {\n session: new URL(\"https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com\"),\n token: new URL(\"https://identitytoolkit.googleapis.com/v1/sessionCookiePublicKeys\")\n}\n\n// Simplified JWKS for Edge Runtime\nconst JWKS = {\n session: createRemoteJWKSet(new URL(JWKS_URLS.session), {\n cacheMaxAge: 3600000, // 1 hour\n timeoutDuration: 5000, // 5 seconds\n cooldownDuration: 30000, // 30 seconds between retries\n }),\n token: createRemoteJWKSet(new URL(JWKS_URLS.token), {\n cacheMaxAge: 3600000, // 1 hour\n timeoutDuration: 5000, // 5 seconds\n cooldownDuration: 30000, // 30 seconds between retries\n })\n }\n\nexport async function verifyFirebaseToken(token: string, isSessionCookie = false) {\n try {\n const projectId = process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID\n if (!projectId) {\n throw new Error(\"Firebase Project ID is not configured\")\n }\n\n const keySet = isSessionCookie ? JWKS.session : JWKS.token\n\n\n const { payload } = await jwtVerify(token, keySet, {\n issuer: isSessionCookie\n ? \"https://session.firebase.google.com/\" + projectId\n : \"https://securetoken.google.com/\" + projectId,\n audience: projectId,\n algorithms: [\"RS256\"],\n })\n\n const now = Math.floor(Date.now() / 1000)\n if (payload.exp && payload.exp <= now) {\n throw new Error(\"Token has expired\")\n }\n\n if (payload.iat && payload.iat > now) {\n throw new Error(\"Token issued time is in the future\")\n }\n\n if (!payload.sub) {\n throw new Error(\"Token subject is empty\")\n }\n\n return {\n valid: true,\n uid: payload.sub,\n email: payload.email as string | undefined,\n emailVerified: payload.email_verified as boolean | undefined,\n authTime: payload.auth_time as number,\n }\n } catch (error) {\n console.error(\"Token verification error:\", error)\n return {\n valid: false,\n error: error instanceof Error ? error.message : \"Invalid token\",\n }\n }\n}"],"mappings":"AAAA,SAAS,WAAW,0BAA0B;AAG9C,MAAM,YAAY;AAAA,EAChB,SAAS,IAAI,IAAI,0FAA0F;AAAA,EAC3G,OAAO,IAAI,IAAI,mEAAmE;AACpF;AAGA,MAAM,OAAO;AAAA,EACT,SAAS,mBAAmB,IAAI,IAAI,UAAU,OAAO,GAAG;AAAA,IACtD,aAAa;AAAA;AAAA,IACb,iBAAiB;AAAA;AAAA,IACjB,kBAAkB;AAAA;AAAA,EACpB,CAAC;AAAA,EACD,OAAO,mBAAmB,IAAI,IAAI,UAAU,KAAK,GAAG;AAAA,IAClD,aAAa;AAAA;AAAA,IACb,iBAAiB;AAAA;AAAA,IACjB,kBAAkB;AAAA;AAAA,EACpB,CAAC;AACH;AAEF,eAAsB,oBAAoB,OAAe,kBAAkB,OAAO;AAChF,MAAI;AACF,UAAM,YAAY,QAAQ,IAAI;AAC9B,QAAI,CAAC,WAAW;AACd,YAAM,IAAI,MAAM,uCAAuC;AAAA,IACzD;AAEA,UAAM,SAAS,kBAAkB,KAAK,UAAU,KAAK;AAGrD,UAAM,EAAE,QAAQ,IAAI,MAAM,UAAU,OAAO,QAAQ;AAAA,MACjD,QAAQ,kBACJ,yCAAyC,YACzC,oCAAoC;AAAA,MACxC,UAAU;AAAA,MACV,YAAY,CAAC,OAAO;AAAA,IACtB,CAAC;AAED,UAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AACxC,QAAI,QAAQ,OAAO,QAAQ,OAAO,KAAK;AACrC,YAAM,IAAI,MAAM,mBAAmB;AAAA,IACrC;AAEA,QAAI,QAAQ,OAAO,QAAQ,MAAM,KAAK;AACpC,YAAM,IAAI,MAAM,oCAAoC;AAAA,IACtD;AAEA,QAAI,CAAC,QAAQ,KAAK;AAChB,YAAM,IAAI,MAAM,wBAAwB;AAAA,IAC1C;AAEA,WAAO;AAAA,MACL,OAAO;AAAA,MACP,KAAK,QAAQ;AAAA,MACb,OAAO,QAAQ;AAAA,MACf,eAAe,QAAQ;AAAA,MACvB,UAAU,QAAQ;AAAA,IACpB;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,6BAA6B,KAAK;AAChD,WAAO;AAAA,MACL,OAAO;AAAA,MACP,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,IAClD;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../../../src/server/jwt-edge.ts"],"sourcesContent":["import { jwtVerify, createRemoteJWKSet } from \"jose\"\nimport { cache } from \"react\"\n\ninterface FirebaseIdTokenPayload {\n iss: string\n aud: string\n auth_time: number\n user_id: string\n sub: string\n iat: number\n exp: number\n email?: string\n email_verified?: boolean\n firebase: {\n identities: {\n [key: string]: any\n }\n sign_in_provider: string\n }\n}\n\n// Firebase public key endpoints\nconst FIREBASE_ID_TOKEN_URL = \"https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com\"\nconst FIREBASE_SESSION_CERT_URL = \"https://identitytoolkit.googleapis.com/v1/sessionCookiePublicKeys\"\n\n// Cache the JWKS using React cache\nconst getIdTokenJWKS = cache(() => {\n return createRemoteJWKSet(new URL(FIREBASE_ID_TOKEN_URL), {\n cacheMaxAge: 3600000, // 1 hour\n timeoutDuration: 5000, // 5 seconds\n cooldownDuration: 30000, // 30 seconds between retries\n })\n})\n\nconst getSessionJWKS = cache(() => {\n return createRemoteJWKSet(new URL(FIREBASE_SESSION_CERT_URL), {\n cacheMaxAge: 3600000, // 1 hour\n timeoutDuration: 5000, // 5 seconds\n cooldownDuration: 30000, // 30 seconds between retries\n })\n})\n\n// Helper to decode JWT without verification\nfunction decodeJwt(token: string) {\n try {\n const [headerB64, payloadB64] = token.split(\".\")\n const header = JSON.parse(Buffer.from(headerB64, \"base64\").toString())\n const payload = JSON.parse(Buffer.from(payloadB64, \"base64\").toString())\n return { header, payload }\n } catch (error) {\n console.error(\"Error decoding JWT:\", error)\n return null\n }\n}\n\nexport async function verifyFirebaseToken(token: string, isSessionCookie = false) {\n try {\n const projectId = process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID\n if (!projectId) {\n throw new Error(\"Firebase Project ID is not configured\")\n }\n\n // Decode token for debugging and type checking\n const decoded = decodeJwt(token)\n if (!decoded) {\n throw new Error(\"Invalid token format\")\n }\n\n console.log(\"Token details:\", {\n header: decoded.header,\n type: isSessionCookie ? \"session_cookie\" : \"id_token\",\n })\n\n\n // Use different JWKS based on token type\n const JWKS = isSessionCookie ? await getSessionJWKS() : await getIdTokenJWKS()\n\n const { payload } = await jwtVerify(token, JWKS, {\n issuer: isSessionCookie\n ? \"https://session.firebase.google.com/\" + projectId\n : \"https://securetoken.google.com/\" + projectId,\n audience: projectId,\n algorithms: [\"RS256\"],\n })\n\n const firebasePayload = payload as unknown as FirebaseIdTokenPayload\n const now = Math.floor(Date.now() / 1000)\n\n // Verify token claims\n if (firebasePayload.exp <= now) {\n throw new Error(\"Token has expired\")\n }\n\n if (firebasePayload.iat > now) {\n throw new Error(\"Token issued time is in the future\")\n }\n\n if (!firebasePayload.sub) {\n throw new Error(\"Token subject is empty\")\n }\n\n if (firebasePayload.auth_time > now) {\n throw new Error(\"Token auth time is in the future\")\n }\n\n return {\n valid: true,\n uid: firebasePayload.sub,\n email: firebasePayload.email,\n emailVerified: firebasePayload.email_verified,\n authTime: firebasePayload.auth_time,\n issuedAt: firebasePayload.iat,\n expiresAt: firebasePayload.exp,\n }\n } catch (error) {\n console.error(\"Token verification details:\", {\n error:\n error instanceof Error\n ? {\n name: error.name,\n message: error.message,\n stack: error.stack,\n }\n : error,\n decoded: decodeJwt(token),\n //projectId,\n isSessionCookie,\n })\n \n return {\n valid: false,\n error: error instanceof Error ? error.message : \"Invalid token\",\n }\n }\n }"],"mappings":"AAAA,SAAS,WAAW,0BAA0B;AAC9C,SAAS,aAAa;AAqBtB,MAAM,wBAAwB;AAC9B,MAAM,4BAA4B;AAGlC,MAAM,iBAAiB,MAAM,MAAM;AACjC,SAAO,mBAAmB,IAAI,IAAI,qBAAqB,GAAG;AAAA,IACxD,aAAa;AAAA;AAAA,IACb,iBAAiB;AAAA;AAAA,IACjB,kBAAkB;AAAA;AAAA,EACpB,CAAC;AACH,CAAC;AAED,MAAM,iBAAiB,MAAM,MAAM;AACjC,SAAO,mBAAmB,IAAI,IAAI,yBAAyB,GAAG;AAAA,IAC5D,aAAa;AAAA;AAAA,IACb,iBAAiB;AAAA;AAAA,IACjB,kBAAkB;AAAA;AAAA,EACpB,CAAC;AACH,CAAC;AAGD,SAAS,UAAU,OAAe;AAChC,MAAI;AACF,UAAM,CAAC,WAAW,UAAU,IAAI,MAAM,MAAM,GAAG;AAC/C,UAAM,SAAS,KAAK,MAAM,OAAO,KAAK,WAAW,QAAQ,EAAE,SAAS,CAAC;AACrE,UAAM,UAAU,KAAK,MAAM,OAAO,KAAK,YAAY,QAAQ,EAAE,SAAS,CAAC;AACvE,WAAO,EAAE,QAAQ,QAAQ;AAAA,EAC3B,SAAS,OAAO;AACd,YAAQ,MAAM,uBAAuB,KAAK;AAC1C,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,oBAAoB,OAAe,kBAAkB,OAAO;AAChF,MAAI;AACF,UAAM,YAAY,QAAQ,IAAI;AAC9B,QAAI,CAAC,WAAW;AACd,YAAM,IAAI,MAAM,uCAAuC;AAAA,IACzD;AAGA,UAAM,UAAU,UAAU,KAAK;AAC/B,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI,MAAM,sBAAsB;AAAA,IACxC;AAEA,YAAQ,IAAI,kBAAkB;AAAA,MAC5B,QAAQ,QAAQ;AAAA,MAChB,MAAM,kBAAkB,mBAAmB;AAAA,IAC7C,CAAC;AAID,UAAM,OAAO,kBAAkB,MAAM,eAAe,IAAI,MAAM,eAAe;AAE7E,UAAM,EAAE,QAAQ,IAAI,MAAM,UAAU,OAAO,MAAM;AAAA,MAC3C,QAAQ,kBACJ,yCAAyC,YACzC,oCAAoC;AAAA,MACxC,UAAU;AAAA,MACV,YAAY,CAAC,OAAO;AAAA,IAC1B,CAAC;AAED,UAAM,kBAAkB;AACxB,UAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAGvC,QAAI,gBAAgB,OAAO,KAAK;AAC3B,YAAM,IAAI,MAAM,mBAAmB;AAAA,IACzC;AAEA,QAAI,gBAAgB,MAAM,KAAK;AACzB,YAAM,IAAI,MAAM,oCAAoC;AAAA,IAC1D;AAEA,QAAI,CAAC,gBAAgB,KAAK;AACpB,YAAM,IAAI,MAAM,wBAAwB;AAAA,IAC9C;AAEA,QAAI,gBAAgB,YAAY,KAAK;AAC/B,YAAM,IAAI,MAAM,kCAAkC;AAAA,IACxD;AAEA,WAAO;AAAA,MACD,OAAO;AAAA,MACP,KAAK,gBAAgB;AAAA,MACrB,OAAO,gBAAgB;AAAA,MACvB,eAAe,gBAAgB;AAAA,MAC/B,UAAU,gBAAgB;AAAA,MAC1B,UAAU,gBAAgB;AAAA,MAC1B,WAAW,gBAAgB;AAAA,IAC7B;AAAA,EACJ,SAAS,OAAO;AACZ,YAAQ,MAAM,+BAA+B;AAAA,MAC3C,OACE,iBAAiB,QACb;AAAA,QACE,MAAM,MAAM;AAAA,QACZ,SAAS,MAAM;AAAA,QACf,OAAO,MAAM;AAAA,MACf,IACA;AAAA,MACN,SAAS,UAAU,KAAK;AAAA;AAAA,MAExB;AAAA,IACF,CAAC;AAED,WAAO;AAAA,MACL,OAAO;AAAA,MACP,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,IAClD;AAAA,EACF;AACF;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/server/ternSecureMiddleware.ts"],"sourcesContent":["import { NextRequest, NextResponse } from 'next/server';\nimport { verifySession, type UserInfo } from './edge-session'\n\nexport const runtime = \"edge\"\n\n\ninterface Auth {\n user: UserInfo | null\n token: string | null\n protect: () => Promise<void>\n}\n\ntype MiddlewareCallback = (\n auth: Auth,\n request: NextRequest\n) => Promise<void>\n\n\n/**\n * Create a route matcher function for public paths\n */\nexport function createRouteMatcher(patterns: string[]) {\n return (request: NextRequest): boolean => {\n const { pathname } = request.nextUrl\n return patterns.some(pattern => {\n // Convert route pattern to regex\n const regexPattern = new RegExp(\n `^${pattern.replace(/\\*/g, '.*').replace(/\\((.*)\\)/, '(?:$1)?')}$`\n )\n return regexPattern.test(pathname)\n })\n }\n}\n\n\n/**\n * Edge-compatible auth check\n */\nasync function edgeAuth(request: NextRequest): Promise<Auth> {\n async function protect() {\n throw new Error(\"Unauthorized access\")\n }\n\n try {\n const sessionResult = await verifySession(request)\n\n if (sessionResult.isAuthenticated && sessionResult.user) {\n return {\n user: sessionResult.user,\n token: request.cookies.get(\"_session_cookie\")?.value || request.cookies.get(\"_session_token\")?.value || null,\n protect: async () => {},\n }\n }\n\n return {\n user: null,\n token: null,\n protect,\n }\n } catch (error) {\n console.error(\"Auth check error:\", error)\n return {\n user: null,\n token: null,\n protect,\n }\n }\n}\n\n\n\n/**\n * Middleware factory that handles authentication and custom logic\n * @param customHandler Optional function for additional custom logic\n */\n\nexport function ternSecureMiddleware(callback: MiddlewareCallback) {\n return async function middleware(request: NextRequest) {\n try {\n const auth = await edgeAuth(request)\n\n try {\n \n await callback(auth, request)\n\n const response = NextResponse.next()\n\n if (auth.user) {\n // Set auth headers\n response.headers.set(\"x-user-id\", auth.user.uid)\n if (auth.user.email) {\n response.headers.set(\"x-user-email\", auth.user.email)\n }\n if (auth.user.emailVerified !== undefined) {\n response.headers.set(\"x-email-verified\", auth.user.emailVerified.toString())\n }\n if (auth.user.authTime) {\n response.headers.set(\"x-auth-time\", auth.user.authTime.toString())\n }\n }\n\n return response\n } catch (error) {\n // Handle unauthorized access\n if (error instanceof Error && error.message === 'Unauthorized access') {\n const redirectUrl = new URL('/sign-in', request.url)\n redirectUrl.searchParams.set('redirect', request.nextUrl.pathname)\n return NextResponse.redirect(redirectUrl)\n }\n throw error\n }\n\n } catch (error) {\n console.error(\"Middleware error:\", {\n error:\n error instanceof Error\n ? {\n name: error.name,\n message: error.message,\n stack: error.stack,\n }\n : error,\n path: request.nextUrl.pathname,\n })\n\n const redirectUrl = new URL(\"/sign-in\", request.url)\n return NextResponse.redirect(redirectUrl)\n }\n }\n}"],"mappings":"AAAA,SAAsB,oBAAoB;AAC1C,SAAS,qBAAoC;AAEtC,MAAM,UAAU;AAkBhB,SAAS,mBAAmB,UAAoB;AACrD,SAAO,CAAC,YAAkC;AACxC,UAAM,EAAE,SAAS,IAAI,QAAQ;AAC7B,WAAO,SAAS,KAAK,aAAW;AAE9B,YAAM,eAAe,IAAI;AAAA,QACvB,IAAI,QAAQ,QAAQ,OAAO,IAAI,EAAE,QAAQ,YAAY,SAAS,CAAC;AAAA,MACjE;AACA,aAAO,aAAa,KAAK,QAAQ;AAAA,IACnC,CAAC;AAAA,EACH;AACF;AAMA,eAAe,SAAS,SAAqC;AAtC7D;AAuCE,iBAAe,UAAU;AACvB,UAAM,IAAI,MAAM,qBAAqB;AAAA,EACvC;AAEA,MAAI;AACF,UAAM,gBAAgB,MAAM,cAAc,OAAO;AAEjD,QAAI,cAAc,mBAAmB,cAAc,MAAM;AACvD,aAAO;AAAA,QACL,MAAM,cAAc;AAAA,QACpB,SAAO,aAAQ,QAAQ,IAAI,iBAAiB,MAArC,mBAAwC,YAAS,aAAQ,QAAQ,IAAI,gBAAgB,MAApC,mBAAuC,UAAS;AAAA,QACxG,SAAS,YAAY;AAAA,QAAC;AAAA,MACxB;AAAA,IACF;AAEA,WAAO;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AAAA,MACP;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,qBAAqB,KAAK;AACxC,WAAO;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AAAA,MACP;AAAA,IACF;AAAA,EACF;AACF;AASO,SAAS,qBAAqB,UAA8B;AACjE,SAAO,eAAe,WAAW,SAAsB;AACrD,QAAI;AACF,YAAM,OAAO,MAAM,SAAS,OAAO;AAEnC,UAAI;AAEF,cAAM,SAAS,MAAM,OAAO;AAE5B,cAAM,WAAW,aAAa,KAAK;AAEnC,YAAI,KAAK,MAAM;AAEb,mBAAS,QAAQ,IAAI,aAAa,KAAK,KAAK,GAAG;AAC/C,cAAI,KAAK,KAAK,OAAO;AACnB,qBAAS,QAAQ,IAAI,gBAAgB,KAAK,KAAK,KAAK;AAAA,UACtD;AACA,cAAI,KAAK,KAAK,kBAAkB,QAAW;AACzC,qBAAS,QAAQ,IAAI,oBAAoB,KAAK,KAAK,cAAc,SAAS,CAAC;AAAA,UAC7E;AACA,cAAI,KAAK,KAAK,UAAU;AACtB,qBAAS,QAAQ,IAAI,eAAe,KAAK,KAAK,SAAS,SAAS,CAAC;AAAA,UACnE;AAAA,QACF;AAEA,eAAO;AAAA,MACT,SAAS,OAAO;AAEd,YAAI,iBAAiB,SAAS,MAAM,YAAY,uBAAuB;AACrE,gBAAM,cAAc,IAAI,IAAI,YAAY,QAAQ,GAAG;AACnD,sBAAY,aAAa,IAAI,YAAY,QAAQ,QAAQ,QAAQ;AACjE,iBAAO,aAAa,SAAS,WAAW;AAAA,QAC1C;AACA,cAAM;AAAA,MACR;AAAA,IAEF,SAAS,OAAO;AACd,cAAQ,MAAM,qBAAqB;AAAA,QACjC,OACE,iBAAiB,QACb;AAAA,UACE,MAAM,MAAM;AAAA,UACZ,SAAS,MAAM;AAAA,UACf,OAAO,MAAM;AAAA,QACf,IACA;AAAA,QACN,MAAM,QAAQ,QAAQ;AAAA,MACxB,CAAC;AAED,YAAM,cAAc,IAAI,IAAI,YAAY,QAAQ,GAAG;AACnD,aAAO,aAAa,SAAS,WAAW;AAAA,IAC1C;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../../../src/server/ternSecureMiddleware.ts"],"sourcesContent":["import { NextRequest, NextResponse } from 'next/server';\nimport { verifySession } from './edge-session'\nimport type { UserInfo } from './types'\n\n\nexport const runtime = \"edge\"\n\n\ninterface Auth {\n user: UserInfo | null\n token: string | null\n protect: () => Promise<void>\n}\n\ntype MiddlewareCallback = (\n auth: Auth,\n request: NextRequest\n) => Promise<void>\n\n\n/**\n * Create a route matcher function for public paths\n */\nexport function createRouteMatcher(patterns: string[]) {\n return (request: NextRequest): boolean => {\n const { pathname } = request.nextUrl\n return patterns.some(pattern => {\n // Convert route pattern to regex\n const regexPattern = new RegExp(\n `^${pattern.replace(/\\*/g, '.*').replace(/\\((.*)\\)/, '(?:$1)?')}$`\n )\n return regexPattern.test(pathname)\n })\n }\n}\n\n\n/**\n * Edge-compatible auth check\n */\nasync function edgeAuth(request: NextRequest): Promise<Auth> {\n async function protect() {\n throw new Error(\"Unauthorized access\")\n }\n\n try {\n const sessionResult = await verifySession(request)\n\n if (sessionResult.isAuthenticated && sessionResult.user) {\n return {\n user: sessionResult.user,\n token: request.cookies.get(\"_session_cookie\")?.value || request.cookies.get(\"_session_token\")?.value || null,\n protect: async () => {},\n }\n }\n\n return {\n user: null,\n token: null,\n protect,\n }\n } catch (error) {\n console.error(\"Auth check error:\", error)\n return {\n user: null,\n token: null,\n protect,\n }\n }\n}\n\n\n\n/**\n * Middleware factory that handles authentication and custom logic\n * @param customHandler Optional function for additional custom logic\n */\n\nexport function ternSecureMiddleware(callback: MiddlewareCallback) {\n return async function middleware(request: NextRequest) {\n try {\n const auth = await edgeAuth(request)\n\n try {\n \n await callback(auth, request)\n\n const response = NextResponse.next()\n\n if (auth.user) {\n // Set auth headers\n response.headers.set(\"x-user-id\", auth.user.uid)\n if (auth.user.email) {\n response.headers.set(\"x-user-email\", auth.user.email)\n }\n if (auth.user.emailVerified !== undefined) {\n response.headers.set(\"x-email-verified\", auth.user.emailVerified.toString())\n }\n if (auth.user.authTime) {\n response.headers.set(\"x-auth-time\", auth.user.authTime.toString())\n }\n }\n\n return response\n } catch (error) {\n // Handle unauthorized access\n if (error instanceof Error && error.message === 'Unauthorized access') {\n const redirectUrl = new URL('/sign-in', request.url)\n redirectUrl.searchParams.set('redirect', request.nextUrl.pathname)\n return NextResponse.redirect(redirectUrl)\n }\n throw error\n }\n\n } catch (error) {\n console.error(\"Middleware error:\", {\n error:\n error instanceof Error\n ? {\n name: error.name,\n message: error.message,\n stack: error.stack,\n }\n : error,\n path: request.nextUrl.pathname,\n })\n\n const redirectUrl = new URL(\"/sign-in\", request.url)\n return NextResponse.redirect(redirectUrl)\n }\n }\n}"],"mappings":"AAAA,SAAsB,oBAAoB;AAC1C,SAAS,qBAAqB;AAIvB,MAAM,UAAU;AAkBhB,SAAS,mBAAmB,UAAoB;AACrD,SAAO,CAAC,YAAkC;AACxC,UAAM,EAAE,SAAS,IAAI,QAAQ;AAC7B,WAAO,SAAS,KAAK,aAAW;AAE9B,YAAM,eAAe,IAAI;AAAA,QACvB,IAAI,QAAQ,QAAQ,OAAO,IAAI,EAAE,QAAQ,YAAY,SAAS,CAAC;AAAA,MACjE;AACA,aAAO,aAAa,KAAK,QAAQ;AAAA,IACnC,CAAC;AAAA,EACH;AACF;AAMA,eAAe,SAAS,SAAqC;AAxC7D;AAyCE,iBAAe,UAAU;AACvB,UAAM,IAAI,MAAM,qBAAqB;AAAA,EACvC;AAEA,MAAI;AACF,UAAM,gBAAgB,MAAM,cAAc,OAAO;AAEjD,QAAI,cAAc,mBAAmB,cAAc,MAAM;AACvD,aAAO;AAAA,QACL,MAAM,cAAc;AAAA,QACpB,SAAO,aAAQ,QAAQ,IAAI,iBAAiB,MAArC,mBAAwC,YAAS,aAAQ,QAAQ,IAAI,gBAAgB,MAApC,mBAAuC,UAAS;AAAA,QACxG,SAAS,YAAY;AAAA,QAAC;AAAA,MACxB;AAAA,IACF;AAEA,WAAO;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AAAA,MACP;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,qBAAqB,KAAK;AACxC,WAAO;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AAAA,MACP;AAAA,IACF;AAAA,EACF;AACF;AASO,SAAS,qBAAqB,UAA8B;AACjE,SAAO,eAAe,WAAW,SAAsB;AACrD,QAAI;AACF,YAAM,OAAO,MAAM,SAAS,OAAO;AAEnC,UAAI;AAEF,cAAM,SAAS,MAAM,OAAO;AAE5B,cAAM,WAAW,aAAa,KAAK;AAEnC,YAAI,KAAK,MAAM;AAEb,mBAAS,QAAQ,IAAI,aAAa,KAAK,KAAK,GAAG;AAC/C,cAAI,KAAK,KAAK,OAAO;AACnB,qBAAS,QAAQ,IAAI,gBAAgB,KAAK,KAAK,KAAK;AAAA,UACtD;AACA,cAAI,KAAK,KAAK,kBAAkB,QAAW;AACzC,qBAAS,QAAQ,IAAI,oBAAoB,KAAK,KAAK,cAAc,SAAS,CAAC;AAAA,UAC7E;AACA,cAAI,KAAK,KAAK,UAAU;AACtB,qBAAS,QAAQ,IAAI,eAAe,KAAK,KAAK,SAAS,SAAS,CAAC;AAAA,UACnE;AAAA,QACF;AAEA,eAAO;AAAA,MACT,SAAS,OAAO;AAEd,YAAI,iBAAiB,SAAS,MAAM,YAAY,uBAAuB;AACrE,gBAAM,cAAc,IAAI,IAAI,YAAY,QAAQ,GAAG;AACnD,sBAAY,aAAa,IAAI,YAAY,QAAQ,QAAQ,QAAQ;AACjE,iBAAO,aAAa,SAAS,WAAW;AAAA,QAC1C;AACA,cAAM;AAAA,MACR;AAAA,IAEF,SAAS,OAAO;AACd,cAAQ,MAAM,qBAAqB;AAAA,QACjC,OACE,iBAAiB,QACb;AAAA,UACE,MAAM,MAAM;AAAA,UACZ,SAAS,MAAM;AAAA,UACf,OAAO,MAAM;AAAA,QACf,IACA;AAAA,QACN,MAAM,QAAQ,QAAQ;AAAA,MACxB,CAAC;AAED,YAAM,cAAc,IAAI,IAAI,YAAY,QAAQ,GAAG;AACnD,aAAO,aAAa,SAAS,WAAW;AAAA,IAC1C;AAAA,EACF;AACF;","names":[]}
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1,4 +1,4 @@
1
- import type { UserInfo } from "./edge-session";
1
+ import type { UserInfo } from "./types";
2
2
  export interface AuthResult {
3
3
  user: UserInfo | null;
4
4
  token: string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/server/auth.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAiB,MAAM,gBAAgB,CAAA;AAG7D,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAA;IACrB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;CACpB;AAGC;;GAEG;AACH,wBAAsB,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC,CAuClD;AAED;;GAEG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC,CAGxD;AAED;;GAEG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAY1D"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/server/auth.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAGvC,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAA;IACrB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;CACpB;AAGC;;GAEG;AACH,wBAAsB,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC,CAuClD;AAED;;GAEG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC,CAGxD;AAED;;GAEG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAY1D"}
@@ -1,15 +1,4 @@
1
1
  import type { NextRequest } from "next/server";
2
- export interface UserInfo {
3
- uid: string;
4
- email: string | null;
5
- emailVerified?: boolean;
6
- authTime?: number;
7
- disabled?: boolean;
8
- }
9
- export interface SessionResult {
10
- isAuthenticated: boolean;
11
- user: UserInfo | null;
12
- error?: string;
13
- }
2
+ import type { SessionResult } from "./types";
14
3
  export declare function verifySession(request: NextRequest): Promise<SessionResult>;
15
4
  //# sourceMappingURL=edge-session.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"edge-session.d.ts","sourceRoot":"","sources":["../../../src/server/edge-session.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAE9C,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAGD,MAAM,WAAW,aAAa;IAC5B,eAAe,EAAE,OAAO,CAAA;IACxB,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAA;IACrB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAGD,wBAAsB,aAAa,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC,CAqDhF"}
1
+ {"version":3,"file":"edge-session.d.ts","sourceRoot":"","sources":["../../../src/server/edge-session.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAG5C,wBAAsB,aAAa,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC,CAqDhF"}
@@ -1,4 +1,5 @@
1
1
  export { ternSecureMiddleware, createRouteMatcher } from './ternSecureMiddleware';
2
2
  export { auth, getUserInfo } from './auth';
3
3
  export type { AuthResult } from './auth';
4
+ export type { UserInfo, SessionResult } from './types';
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/server/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AACjF,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAA;AAC1C,YAAY,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/server/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AACjF,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAA;AAC1C,YAAY,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AACxC,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA"}
@@ -4,6 +4,8 @@ export declare function verifyFirebaseToken(token: string, isSessionCookie?: boo
4
4
  email: string | undefined;
5
5
  emailVerified: boolean | undefined;
6
6
  authTime: number;
7
+ issuedAt: number;
8
+ expiresAt: number;
7
9
  error?: undefined;
8
10
  } | {
9
11
  valid: boolean;
@@ -12,5 +14,7 @@ export declare function verifyFirebaseToken(token: string, isSessionCookie?: boo
12
14
  email?: undefined;
13
15
  emailVerified?: undefined;
14
16
  authTime?: undefined;
17
+ issuedAt?: undefined;
18
+ expiresAt?: undefined;
15
19
  }>;
16
20
  //# sourceMappingURL=jwt-edge.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"jwt-edge.d.ts","sourceRoot":"","sources":["../../../src/server/jwt-edge.ts"],"names":[],"mappings":"AAsBA,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,eAAe,UAAQ;;;WAkClD,MAAM,GAAG,SAAS;mBACD,OAAO,GAAG,SAAS;cAC7B,MAAM;;;;;;;;;GAS1C"}
1
+ {"version":3,"file":"jwt-edge.d.ts","sourceRoot":"","sources":["../../../src/server/jwt-edge.ts"],"names":[],"mappings":"AAuDA,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,eAAe,UAAQ;;;;;;;;;;;;;;;;;;GA+E3E"}
@@ -1,5 +1,5 @@
1
1
  import { NextRequest, NextResponse } from 'next/server';
2
- import { type UserInfo } from './edge-session';
2
+ import type { UserInfo } from './types';
3
3
  export declare const runtime = "edge";
4
4
  interface Auth {
5
5
  user: UserInfo | null;
@@ -1 +1 @@
1
- {"version":3,"file":"ternSecureMiddleware.d.ts","sourceRoot":"","sources":["../../../src/server/ternSecureMiddleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAiB,KAAK,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAE7D,eAAO,MAAM,OAAO,SAAS,CAAA;AAG7B,UAAU,IAAI;IACZ,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAA;IACrB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC7B;AAED,KAAK,kBAAkB,GAAG,CACxB,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,WAAW,KACjB,OAAO,CAAC,IAAI,CAAC,CAAA;AAGlB;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,aAClC,WAAW,KAAG,OAAO,CAUvC;AAuCD;;;GAGG;AAEH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,kBAAkB,aACrB,WAAW,oCAoDtD"}
1
+ {"version":3,"file":"ternSecureMiddleware.d.ts","sourceRoot":"","sources":["../../../src/server/ternSecureMiddleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAExD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAGvC,eAAO,MAAM,OAAO,SAAS,CAAA;AAG7B,UAAU,IAAI;IACZ,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAA;IACrB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC7B;AAED,KAAK,kBAAkB,GAAG,CACxB,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,WAAW,KACjB,OAAO,CAAC,IAAI,CAAC,CAAA;AAGlB;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,aAClC,WAAW,KAAG,OAAO,CAUvC;AAuCD;;;GAGG;AAEH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,kBAAkB,aACrB,WAAW,oCAoDtD"}
@@ -0,0 +1,13 @@
1
+ export interface UserInfo {
2
+ uid: string;
3
+ email: string | null;
4
+ emailVerified?: boolean;
5
+ authTime?: number;
6
+ disabled?: boolean;
7
+ }
8
+ export interface SessionResult {
9
+ isAuthenticated: boolean;
10
+ user: UserInfo | null;
11
+ error?: string;
12
+ }
13
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/server/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACrB,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAGD,MAAM,WAAW,aAAa;IAC5B,eAAe,EAAE,OAAO,CAAA;IACxB,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAA;IACrB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tern-secure/nextjs",
3
- "version": "4.2.1",
3
+ "version": "4.2.2",
4
4
  "packageManager": "npm@11.0.0",
5
5
  "publishConfig": {
6
6
  "access": "public"