@tern-secure/nextjs 4.2.1 → 4.2.3

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.
Files changed (77) hide show
  1. package/dist/cjs/app-router/admin/sessionTernSecure.js +29 -25
  2. package/dist/cjs/app-router/admin/sessionTernSecure.js.map +1 -1
  3. package/dist/cjs/components/sign-in.js +3 -0
  4. package/dist/cjs/components/sign-in.js.map +1 -1
  5. package/dist/cjs/server/auth.js +59 -44
  6. package/dist/cjs/server/auth.js.map +1 -1
  7. package/dist/cjs/server/crypto.js +43 -0
  8. package/dist/cjs/server/crypto.js.map +1 -0
  9. package/dist/cjs/server/ctx-store.js +65 -0
  10. package/dist/cjs/server/ctx-store.js.map +1 -0
  11. package/dist/cjs/server/edge-session.js +23 -19
  12. package/dist/cjs/server/edge-session.js.map +1 -1
  13. package/dist/cjs/server/index.js +0 -2
  14. package/dist/cjs/server/index.js.map +1 -1
  15. package/dist/cjs/server/jwt-edge.js +50 -24
  16. package/dist/cjs/server/jwt-edge.js.map +1 -1
  17. package/dist/cjs/server/jwt.js +0 -10
  18. package/dist/cjs/server/jwt.js.map +1 -1
  19. package/dist/cjs/server/session-store.js +72 -0
  20. package/dist/cjs/server/session-store.js.map +1 -0
  21. package/dist/cjs/server/ternSecureMiddleware.js +25 -53
  22. package/dist/cjs/server/ternSecureMiddleware.js.map +1 -1
  23. package/dist/cjs/server/types.js +17 -0
  24. package/dist/cjs/server/types.js.map +1 -0
  25. package/dist/cjs/server/utils.js +108 -0
  26. package/dist/cjs/server/utils.js.map +1 -0
  27. package/dist/esm/app-router/admin/sessionTernSecure.js +29 -25
  28. package/dist/esm/app-router/admin/sessionTernSecure.js.map +1 -1
  29. package/dist/esm/components/sign-in.js +3 -0
  30. package/dist/esm/components/sign-in.js.map +1 -1
  31. package/dist/esm/server/auth.js +57 -43
  32. package/dist/esm/server/auth.js.map +1 -1
  33. package/dist/esm/server/crypto.js +18 -0
  34. package/dist/esm/server/crypto.js.map +1 -0
  35. package/dist/esm/server/ctx-store.js +41 -0
  36. package/dist/esm/server/ctx-store.js.map +1 -0
  37. package/dist/esm/server/edge-session.js +23 -19
  38. package/dist/esm/server/edge-session.js.map +1 -1
  39. package/dist/esm/server/index.js +1 -2
  40. package/dist/esm/server/index.js.map +1 -1
  41. package/dist/esm/server/jwt-edge.js +50 -24
  42. package/dist/esm/server/jwt-edge.js.map +1 -1
  43. package/dist/esm/server/jwt.js +0 -10
  44. package/dist/esm/server/jwt.js.map +1 -1
  45. package/dist/esm/server/session-store.js +47 -0
  46. package/dist/esm/server/session-store.js.map +1 -0
  47. package/dist/esm/server/ternSecureMiddleware.js +25 -52
  48. package/dist/esm/server/ternSecureMiddleware.js.map +1 -1
  49. package/dist/esm/server/types.js +1 -0
  50. package/dist/esm/server/types.js.map +1 -0
  51. package/dist/esm/server/utils.js +84 -0
  52. package/dist/esm/server/utils.js.map +1 -0
  53. package/dist/types/app-router/admin/sessionTernSecure.d.ts +9 -10
  54. package/dist/types/app-router/admin/sessionTernSecure.d.ts.map +1 -1
  55. package/dist/types/components/sign-in.d.ts.map +1 -1
  56. package/dist/types/server/auth.d.ts +9 -5
  57. package/dist/types/server/auth.d.ts.map +1 -1
  58. package/dist/types/server/crypto.d.ts +3 -0
  59. package/dist/types/server/crypto.d.ts.map +1 -0
  60. package/dist/types/server/ctx-store.d.ts +24 -0
  61. package/dist/types/server/ctx-store.d.ts.map +1 -0
  62. package/dist/types/server/edge-session.d.ts +1 -12
  63. package/dist/types/server/edge-session.d.ts.map +1 -1
  64. package/dist/types/server/index.d.ts +2 -2
  65. package/dist/types/server/index.d.ts.map +1 -1
  66. package/dist/types/server/jwt-edge.d.ts +4 -0
  67. package/dist/types/server/jwt-edge.d.ts.map +1 -1
  68. package/dist/types/server/jwt.d.ts.map +1 -1
  69. package/dist/types/server/session-store.d.ts +30 -0
  70. package/dist/types/server/session-store.d.ts.map +1 -0
  71. package/dist/types/server/ternSecureMiddleware.d.ts +3 -4
  72. package/dist/types/server/ternSecureMiddleware.d.ts.map +1 -1
  73. package/dist/types/server/types.d.ts +14 -0
  74. package/dist/types/server/types.d.ts.map +1 -0
  75. package/dist/types/server/utils.d.ts +22 -0
  76. package/dist/types/server/utils.d.ts.map +1 -0
  77. package/package.json +1 -1
@@ -1,57 +1,83 @@
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) {
38
- throw new Error("Token has expired");
39
- }
40
- if (payload.iat && payload.iat > now) {
41
- throw new Error("Token issued time is in the future");
42
- }
43
- if (!payload.sub) {
58
+ if (!firebasePayload.sub) {
44
59
  throw new Error("Token subject is empty");
45
60
  }
46
61
  return {
47
62
  valid: true,
48
- uid: payload.sub,
49
- email: payload.email,
50
- emailVerified: payload.email_verified,
51
- authTime: payload.auth_time
63
+ uid: firebasePayload.sub,
64
+ email: firebasePayload.email,
65
+ emailVerified: firebasePayload.email_verified,
66
+ authTime: firebasePayload.auth_time,
67
+ issuedAt: firebasePayload.iat,
68
+ expiresAt: firebasePayload.exp
52
69
  };
53
70
  } catch (error) {
54
- console.error("Token verification error:", error);
71
+ console.error("Token verification details:", {
72
+ error: error instanceof Error ? {
73
+ name: error.name,
74
+ message: error.message,
75
+ stack: error.stack
76
+ } : error,
77
+ decoded: decodeJwt(token),
78
+ //projectId,
79
+ isSessionCookie
80
+ });
55
81
  return {
56
82
  valid: false,
57
83
  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\n if (!firebasePayload.sub) {\n throw new Error(\"Token subject is empty\")\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;AAGxC,QAAI,CAAC,gBAAgB,KAAK;AACpB,YAAM,IAAI,MAAM,wBAAwB;AAAA,IAC9C;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":[]}
@@ -58,19 +58,9 @@ async function verifyFirebaseToken(token, isSessionCookie = false) {
58
58
  algorithms: ["RS256"]
59
59
  });
60
60
  const firebasePayload = payload;
61
- const now = Math.floor(Date.now() / 1e3);
62
- if (firebasePayload.exp <= now) {
63
- throw new Error("Token has expired");
64
- }
65
- if (firebasePayload.iat > now) {
66
- throw new Error("Token issued time is in the future");
67
- }
68
61
  if (!firebasePayload.sub) {
69
62
  throw new Error("Token subject is empty");
70
63
  }
71
- if (firebasePayload.auth_time > now) {
72
- throw new Error("Token auth time is in the future");
73
- }
74
64
  return {
75
65
  valid: true,
76
66
  uid: firebasePayload.sub,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/server/jwt.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 let retries = 3\n let lastError: Error | null = null\n\n while (retries > 0) {\n try {\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 lastError = error as Error\n if (error instanceof Error && error.name === \"JWKSNoMatchingKey\") {\n console.warn(`JWKS retry attempt ${4 - retries}:`, error.message)\n retries--\n if (retries > 0) {\n await new Promise((resolve) => setTimeout(resolve, 1000))\n continue\n }\n }\n throw error\n }\n }\n\n throw lastError || new Error(\"Failed to verify token after retries\")\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":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/server/jwt.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 let retries = 3\n let lastError: Error | null = null\n\n while (retries > 0) {\n try {\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\n if (!firebasePayload.sub) {\n throw new Error(\"Token subject is empty\")\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 lastError = error as Error\n if (error instanceof Error && error.name === \"JWKSNoMatchingKey\") {\n console.warn(`JWKS retry attempt ${4 - retries}:`, error.message)\n retries--\n if (retries > 0) {\n await new Promise((resolve) => setTimeout(resolve, 1000))\n continue\n }\n }\n throw error\n }\n }\n\n throw lastError || new Error(\"Failed to verify token after retries\")\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":"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","names":[]}
@@ -0,0 +1,47 @@
1
+ import { cache } from "react";
2
+ class SessionStore {
3
+ constructor() {
4
+ this.currentSessionId = null;
5
+ this.sessions = /* @__PURE__ */ new Map();
6
+ }
7
+ static getInstance() {
8
+ if (!SessionStore.instance) {
9
+ SessionStore.instance = new SessionStore();
10
+ }
11
+ return SessionStore.instance;
12
+ }
13
+ setUser(sessionId, user) {
14
+ console.log("SessionStore: Setting user:", { sessionId, user });
15
+ this.sessions.set(sessionId, user);
16
+ this.currentSessionId = sessionId;
17
+ }
18
+ getUser(sessionId) {
19
+ return this.sessions.get(sessionId) || null;
20
+ }
21
+ getCurrentUser() {
22
+ if (!this.currentSessionId) return null;
23
+ return this.sessions.get(this.currentSessionId) || null;
24
+ }
25
+ removeUser(sessionId) {
26
+ this.sessions.delete(sessionId);
27
+ }
28
+ clear() {
29
+ this.sessions.clear();
30
+ }
31
+ debug() {
32
+ return {
33
+ sessionsCount: this.sessions.size,
34
+ currentSessionId: this.currentSessionId,
35
+ sessions: Array.from(this.sessions.entries())
36
+ };
37
+ }
38
+ }
39
+ const sessionStore = SessionStore.getInstance();
40
+ const getVerifiedUser = cache((sessionId) => {
41
+ return sessionStore.getUser(sessionId);
42
+ });
43
+ export {
44
+ getVerifiedUser,
45
+ sessionStore
46
+ };
47
+ //# sourceMappingURL=session-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/server/session-store.ts"],"sourcesContent":["import { cache } from \"react\"\nimport type { UserInfo } from \"./types\"\n\n/**\n * Simple in-memory session store\n * In a real app, this would be backed by Redis/etc\n */\nclass SessionStore {\n private static instance: SessionStore\n private sessions: Map<string, UserInfo>\n private currentSessionId: string | null = null\n\n private constructor() {\n this.sessions = new Map()\n }\n\n static getInstance(): SessionStore {\n if (!SessionStore.instance) {\n SessionStore.instance = new SessionStore()\n }\n return SessionStore.instance\n }\n\n setUser(sessionId: string, user: UserInfo) {\n console.log(\"SessionStore: Setting user:\", { sessionId, user })\n this.sessions.set(sessionId, user)\n this.currentSessionId = sessionId\n }\n\n getUser(sessionId: string): UserInfo | null {\n return this.sessions.get(sessionId) || null\n }\n\n getCurrentUser(): UserInfo | null {\n if (!this.currentSessionId) return null\n return this.sessions.get(this.currentSessionId) || null\n }\n\n removeUser(sessionId: string) {\n this.sessions.delete(sessionId)\n }\n\n clear() {\n this.sessions.clear()\n }\n\n debug() {\n return {\n sessionsCount: this.sessions.size,\n currentSessionId: this.currentSessionId,\n sessions: Array.from(this.sessions.entries())\n }\n}\n}\n\n// Export singleton instance\nexport const sessionStore = SessionStore.getInstance()\n\n/**\n * Cached function to get user from session store\n * Uses React cache for SSR optimization\n */\nexport const getVerifiedUser = cache((sessionId: string): UserInfo | null => {\n return sessionStore.getUser(sessionId)\n})\n\n"],"mappings":"AAAA,SAAS,aAAa;AAOtB,MAAM,aAAa;AAAA,EAKT,cAAc;AAFtB,SAAQ,mBAAkC;AAGxC,SAAK,WAAW,oBAAI,IAAI;AAAA,EAC1B;AAAA,EAEA,OAAO,cAA4B;AACjC,QAAI,CAAC,aAAa,UAAU;AAC1B,mBAAa,WAAW,IAAI,aAAa;AAAA,IAC3C;AACA,WAAO,aAAa;AAAA,EACtB;AAAA,EAEA,QAAQ,WAAmB,MAAgB;AACzC,YAAQ,IAAI,+BAA+B,EAAE,WAAW,KAAK,CAAC;AAC9D,SAAK,SAAS,IAAI,WAAW,IAAI;AACjC,SAAK,mBAAmB;AAAA,EAC1B;AAAA,EAEA,QAAQ,WAAoC;AAC1C,WAAO,KAAK,SAAS,IAAI,SAAS,KAAK;AAAA,EACzC;AAAA,EAEA,iBAAkC;AAChC,QAAI,CAAC,KAAK,iBAAkB,QAAO;AACnC,WAAO,KAAK,SAAS,IAAI,KAAK,gBAAgB,KAAK;AAAA,EACrD;AAAA,EAEA,WAAW,WAAmB;AAC5B,SAAK,SAAS,OAAO,SAAS;AAAA,EAChC;AAAA,EAEA,QAAQ;AACN,SAAK,SAAS,MAAM;AAAA,EACtB;AAAA,EAEA,QAAQ;AACN,WAAO;AAAA,MACL,eAAe,KAAK,SAAS;AAAA,MAC7B,kBAAkB,KAAK;AAAA,MACvB,UAAU,MAAM,KAAK,KAAK,SAAS,QAAQ,CAAC;AAAA,IAC9C;AAAA,EACJ;AACA;AAGO,MAAM,eAAe,aAAa,YAAY;AAM9C,MAAM,kBAAkB,MAAM,CAAC,cAAuC;AAC3E,SAAO,aAAa,QAAQ,SAAS;AACvC,CAAC;","names":[]}
@@ -1,6 +1,5 @@
1
1
  import { NextResponse } from "next/server";
2
- import { verifySession } from "./edge-session";
3
- const runtime = "edge";
2
+ import { TernSecureError } from "../errors";
4
3
  function createRouteMatcher(patterns) {
5
4
  return (request) => {
6
5
  const { pathname } = request.nextUrl;
@@ -12,60 +11,36 @@ function createRouteMatcher(patterns) {
12
11
  });
13
12
  };
14
13
  }
15
- async function edgeAuth(request) {
16
- var _a, _b;
17
- async function protect() {
18
- throw new Error("Unauthorized access");
19
- }
20
- try {
21
- const sessionResult = await verifySession(request);
22
- if (sessionResult.isAuthenticated && sessionResult.user) {
23
- return {
24
- user: sessionResult.user,
25
- token: ((_a = request.cookies.get("_session_cookie")) == null ? void 0 : _a.value) || ((_b = request.cookies.get("_session_token")) == null ? void 0 : _b.value) || null,
26
- protect: async () => {
27
- }
28
- };
29
- }
30
- return {
31
- user: null,
32
- token: null,
33
- protect
34
- };
35
- } catch (error) {
36
- console.error("Auth check error:", error);
37
- return {
38
- user: null,
39
- token: null,
40
- protect
41
- };
42
- }
43
- }
44
14
  function ternSecureMiddleware(callback) {
45
15
  return async function middleware(request) {
46
16
  try {
47
- const auth = await edgeAuth(request);
48
- try {
49
- await callback(auth, request);
50
- const response = NextResponse.next();
51
- if (auth.user) {
52
- response.headers.set("x-user-id", auth.user.uid);
53
- if (auth.user.email) {
54
- response.headers.set("x-user-email", auth.user.email);
55
- }
56
- if (auth.user.emailVerified !== void 0) {
57
- response.headers.set("x-email-verified", auth.user.emailVerified.toString());
58
- }
59
- if (auth.user.authTime) {
60
- response.headers.set("x-auth-time", auth.user.authTime.toString());
17
+ const hasCookies = request.cookies.has("_session_cookie") || request.cookies.has("_session_token");
18
+ const auth = {
19
+ user: null,
20
+ sessionId: null,
21
+ protect: async () => {
22
+ if (!hasCookies) {
23
+ const currentPath = request.nextUrl.pathname;
24
+ if (currentPath !== "/sign-in") {
25
+ const redirectUrl = new URL("/sign-in", request.url);
26
+ redirectUrl.searchParams.set("redirect", currentPath);
27
+ throw new TernSecureError("UNAUTHENTICATED", redirectUrl.toString());
28
+ } else {
29
+ throw new Error("UNAUTHENTICATED");
30
+ }
61
31
  }
62
32
  }
63
- return response;
33
+ };
34
+ if (!callback) {
35
+ return NextResponse.next();
36
+ }
37
+ try {
38
+ await callback(auth, request);
39
+ return NextResponse.next();
64
40
  } catch (error) {
65
41
  if (error instanceof Error && error.message === "Unauthorized access") {
66
- const redirectUrl = new URL("/sign-in", request.url);
67
- redirectUrl.searchParams.set("redirect", request.nextUrl.pathname);
68
- return NextResponse.redirect(redirectUrl);
42
+ console.log("middleware: Unauthorized access, redirecting to sign-in");
43
+ return NextResponse.redirect(error.message);
69
44
  }
70
45
  throw error;
71
46
  }
@@ -78,14 +53,12 @@ function ternSecureMiddleware(callback) {
78
53
  } : error,
79
54
  path: request.nextUrl.pathname
80
55
  });
81
- const redirectUrl = new URL("/sign-in", request.url);
82
- return NextResponse.redirect(redirectUrl);
56
+ return NextResponse.redirect(new URL("/sign-in", request.url));
83
57
  }
84
58
  };
85
59
  }
86
60
  export {
87
61
  createRouteMatcher,
88
- runtime,
89
62
  ternSecureMiddleware
90
63
  };
91
64
  //# sourceMappingURL=ternSecureMiddleware.js.map
@@ -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 { type NextRequest, NextResponse } from 'next/server';\nimport type { UserInfo } from './types'\nimport { TernSecureError } from '../errors';\n\n\n\ninterface Auth {\n user: UserInfo | null\n sessionId : 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 * 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\n const hasCookies = request.cookies.has('_session_cookie') || request.cookies.has('_session_token')\n\n const auth: Auth = {\n user: null,\n sessionId: null,\n protect: async () => {\n if (!hasCookies) {\n const currentPath = request.nextUrl.pathname\n if (currentPath !== '/sign-in') {\n const redirectUrl = new URL('/sign-in', request.url)\n redirectUrl.searchParams.set('redirect', currentPath)\n throw new TernSecureError('UNAUTHENTICATED', redirectUrl.toString())\n } else {\n throw new Error('UNAUTHENTICATED')\n }\n }\n }\n }\n\n if (!callback) {\n return NextResponse.next()\n }\n\n\n\n try {\n await callback(auth, request)\n return NextResponse.next()\n } catch (error) {\n if (error instanceof Error && error.message === 'Unauthorized access') {\n console.log('middleware: Unauthorized access, redirecting to sign-in')\n return NextResponse.redirect(error.message)\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 return NextResponse.redirect(new URL('/sign-in', request.url))\n }\n }\n}"],"mappings":"AAAA,SAA2B,oBAAoB;AAE/C,SAAS,uBAAuB;AAmBzB,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;AAQO,SAAS,qBAAqB,UAA8B;AACjE,SAAO,eAAe,WAAW,SAAsB;AACrD,QAAI;AAEF,YAAM,aAAa,QAAQ,QAAQ,IAAI,iBAAiB,KAAK,QAAQ,QAAQ,IAAI,gBAAgB;AAEjG,YAAM,OAAa;AAAA,QACjB,MAAM;AAAA,QACN,WAAW;AAAA,QACX,SAAS,YAAY;AACnB,cAAI,CAAC,YAAY;AACf,kBAAM,cAAc,QAAQ,QAAQ;AACpC,gBAAI,gBAAgB,YAAY;AAC9B,oBAAM,cAAc,IAAI,IAAI,YAAY,QAAQ,GAAG;AACnD,0BAAY,aAAa,IAAI,YAAY,WAAW;AACpD,oBAAM,IAAI,gBAAgB,mBAAmB,YAAY,SAAS,CAAC;AAAA,YACrE,OAAO;AACL,oBAAM,IAAI,MAAM,iBAAiB;AAAA,YACnC;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,UAAI,CAAC,UAAU;AACb,eAAO,aAAa,KAAK;AAAA,MAC3B;AAIA,UAAI;AACF,cAAM,SAAS,MAAM,OAAO;AAC5B,eAAO,aAAa,KAAK;AAAA,MAC3B,SAAS,OAAO;AACd,YAAI,iBAAiB,SAAS,MAAM,YAAY,uBAAuB;AACrE,kBAAQ,IAAI,yDAAyD;AACrE,iBAAO,aAAa,SAAS,MAAM,OAAO;AAAA,QAC5C;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,aAAO,aAAa,SAAS,IAAI,IAAI,YAAY,QAAQ,GAAG,CAAC;AAAA,IAC/D;AAAA,EACF;AACF;","names":[]}
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,84 @@
1
+ const getGlobalObject = () => {
2
+ if (typeof process !== "undefined") {
3
+ return process;
4
+ }
5
+ return globalThis;
6
+ };
7
+ const STORE_KEY = "__TERN_AUTH_STORE__";
8
+ class Store {
9
+ static getStore() {
10
+ const global = getGlobalObject();
11
+ if (!global[STORE_KEY]) {
12
+ global[STORE_KEY] = {
13
+ contexts: /* @__PURE__ */ new Map(),
14
+ sessions: /* @__PURE__ */ new Map(),
15
+ currentSession: null
16
+ };
17
+ }
18
+ return global[STORE_KEY];
19
+ }
20
+ static setContext(context) {
21
+ const store = this.getStore();
22
+ const { user, sessionId } = context;
23
+ console.log("Store: Setting context:", { sessionId, user });
24
+ store.contexts.set(sessionId, context);
25
+ store.sessions.set(sessionId, user);
26
+ store.currentSession = context;
27
+ console.log("Store: Updated state:", {
28
+ contextsSize: store.contexts.size,
29
+ sessionsSize: store.sessions.size,
30
+ currentSession: store.currentSession
31
+ });
32
+ }
33
+ static getContext() {
34
+ const store = this.getStore();
35
+ if (store.currentSession) {
36
+ const session = this.getSession(store.currentSession.sessionId);
37
+ if (session && session.uid === store.currentSession.user.uid) {
38
+ return store.currentSession;
39
+ }
40
+ }
41
+ for (const [sessionId, user] of store.sessions.entries()) {
42
+ const context = store.contexts.get(sessionId);
43
+ if (context && context.user.uid === user.uid) {
44
+ store.currentSession = context;
45
+ return context;
46
+ }
47
+ }
48
+ return null;
49
+ }
50
+ static setSession(sessionId, user) {
51
+ const store = this.getStore();
52
+ store.sessions.set(sessionId, user);
53
+ }
54
+ static getSession(sessionId) {
55
+ const store = this.getStore();
56
+ return store.sessions.get(sessionId) || null;
57
+ }
58
+ static debug() {
59
+ const store = this.getStore();
60
+ return {
61
+ contextsSize: store.contexts.size,
62
+ sessionsSize: store.sessions.size,
63
+ currentSession: store.currentSession,
64
+ contexts: Array.from(store.contexts.entries()),
65
+ sessions: Array.from(store.sessions.entries())
66
+ };
67
+ }
68
+ static cleanup() {
69
+ const store = this.getStore();
70
+ const MAX_ENTRIES = 1e3;
71
+ if (store.contexts.size > MAX_ENTRIES) {
72
+ const keys = Array.from(store.contexts.keys());
73
+ const toDelete = keys.slice(0, keys.length - MAX_ENTRIES);
74
+ toDelete.forEach((key) => {
75
+ store.contexts.delete(key);
76
+ store.sessions.delete(key);
77
+ });
78
+ }
79
+ }
80
+ }
81
+ export {
82
+ Store
83
+ };
84
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/server/utils.ts"],"sourcesContent":["import type { UserInfo } from \"./types\"\n\ninterface RequestContext {\n user: UserInfo\n sessionId: string\n}\n\n// Use process.env in Node.js and globalThis in Edge\nconst getGlobalObject = () => {\n if (typeof process !== 'undefined') {\n return process\n }\n return globalThis\n}\n\nconst STORE_KEY = '__TERN_AUTH_STORE__'\n\nexport class Store {\n private static getStore() {\n const global = getGlobalObject() as any\n \n if (!global[STORE_KEY]) {\n global[STORE_KEY] = {\n contexts: new Map<string, RequestContext>(),\n sessions: new Map<string, UserInfo>(),\n currentSession: null as RequestContext | null\n }\n }\n \n return global[STORE_KEY]\n }\n\n static setContext(context: RequestContext) {\n const store = this.getStore()\n const { user, sessionId } = context\n \n console.log(\"Store: Setting context:\", { sessionId, user })\n \n // Store in both maps\n store.contexts.set(sessionId, context)\n store.sessions.set(sessionId, user)\n \n // Set as current session\n store.currentSession = context\n \n console.log(\"Store: Updated state:\", {\n contextsSize: store.contexts.size,\n sessionsSize: store.sessions.size,\n currentSession: store.currentSession\n })\n }\n\n static getContext(): RequestContext | null {\n const store = this.getStore()\n \n // First try current session\n if (store.currentSession) {\n const session = this.getSession(store.currentSession.sessionId)\n if (session && session.uid === store.currentSession.user.uid) {\n return store.currentSession\n }\n }\n \n // Then try to find any valid context\n for (const [sessionId, user] of store.sessions.entries()) {\n const context = store.contexts.get(sessionId)\n if (context && context.user.uid === user.uid) {\n // Update current session\n store.currentSession = context\n return context\n }\n }\n \n return null\n }\n\n static setSession(sessionId: string, user: UserInfo) {\n const store = this.getStore()\n store.sessions.set(sessionId, user)\n }\n\n static getSession(sessionId: string): UserInfo | null {\n const store = this.getStore()\n return store.sessions.get(sessionId) || null\n }\n\n static debug() {\n const store = this.getStore()\n return {\n contextsSize: store.contexts.size,\n sessionsSize: store.sessions.size,\n currentSession: store.currentSession,\n contexts: Array.from(store.contexts.entries()),\n sessions: Array.from(store.sessions.entries())\n }\n }\n\n static cleanup() {\n const store = this.getStore()\n const MAX_ENTRIES = 1000\n \n if (store.contexts.size > MAX_ENTRIES) {\n const keys = Array.from(store.contexts.keys())\n const toDelete = keys.slice(0, keys.length - MAX_ENTRIES)\n \n toDelete.forEach(key => {\n store.contexts.delete(key)\n store.sessions.delete(key)\n })\n }\n }\n}"],"mappings":"AAQA,MAAM,kBAAkB,MAAM;AAC5B,MAAI,OAAO,YAAY,aAAa;AAClC,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,MAAM,YAAY;AAEX,MAAM,MAAM;AAAA,EACjB,OAAe,WAAW;AACxB,UAAM,SAAS,gBAAgB;AAE/B,QAAI,CAAC,OAAO,SAAS,GAAG;AACtB,aAAO,SAAS,IAAI;AAAA,QAClB,UAAU,oBAAI,IAA4B;AAAA,QAC1C,UAAU,oBAAI,IAAsB;AAAA,QACpC,gBAAgB;AAAA,MAClB;AAAA,IACF;AAEA,WAAO,OAAO,SAAS;AAAA,EACzB;AAAA,EAEA,OAAO,WAAW,SAAyB;AACzC,UAAM,QAAQ,KAAK,SAAS;AAC5B,UAAM,EAAE,MAAM,UAAU,IAAI;AAE5B,YAAQ,IAAI,2BAA2B,EAAE,WAAW,KAAK,CAAC;AAG1D,UAAM,SAAS,IAAI,WAAW,OAAO;AACrC,UAAM,SAAS,IAAI,WAAW,IAAI;AAGlC,UAAM,iBAAiB;AAEvB,YAAQ,IAAI,yBAAyB;AAAA,MACnC,cAAc,MAAM,SAAS;AAAA,MAC7B,cAAc,MAAM,SAAS;AAAA,MAC7B,gBAAgB,MAAM;AAAA,IACxB,CAAC;AAAA,EACH;AAAA,EAEA,OAAO,aAAoC;AACzC,UAAM,QAAQ,KAAK,SAAS;AAG5B,QAAI,MAAM,gBAAgB;AACxB,YAAM,UAAU,KAAK,WAAW,MAAM,eAAe,SAAS;AAC9D,UAAI,WAAW,QAAQ,QAAQ,MAAM,eAAe,KAAK,KAAK;AAC5D,eAAO,MAAM;AAAA,MACf;AAAA,IACF;AAGA,eAAW,CAAC,WAAW,IAAI,KAAK,MAAM,SAAS,QAAQ,GAAG;AACxD,YAAM,UAAU,MAAM,SAAS,IAAI,SAAS;AAC5C,UAAI,WAAW,QAAQ,KAAK,QAAQ,KAAK,KAAK;AAE5C,cAAM,iBAAiB;AACvB,eAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,OAAO,WAAW,WAAmB,MAAgB;AACnD,UAAM,QAAQ,KAAK,SAAS;AAC5B,UAAM,SAAS,IAAI,WAAW,IAAI;AAAA,EACpC;AAAA,EAEA,OAAO,WAAW,WAAoC;AACpD,UAAM,QAAQ,KAAK,SAAS;AAC5B,WAAO,MAAM,SAAS,IAAI,SAAS,KAAK;AAAA,EAC1C;AAAA,EAEA,OAAO,QAAQ;AACb,UAAM,QAAQ,KAAK,SAAS;AAC5B,WAAO;AAAA,MACL,cAAc,MAAM,SAAS;AAAA,MAC7B,cAAc,MAAM,SAAS;AAAA,MAC7B,gBAAgB,MAAM;AAAA,MACtB,UAAU,MAAM,KAAK,MAAM,SAAS,QAAQ,CAAC;AAAA,MAC7C,UAAU,MAAM,KAAK,MAAM,SAAS,QAAQ,CAAC;AAAA,IAC/C;AAAA,EACF;AAAA,EAEA,OAAO,UAAU;AACf,UAAM,QAAQ,KAAK,SAAS;AAC5B,UAAM,cAAc;AAEpB,QAAI,MAAM,SAAS,OAAO,aAAa;AACrC,YAAM,OAAO,MAAM,KAAK,MAAM,SAAS,KAAK,CAAC;AAC7C,YAAM,WAAW,KAAK,MAAM,GAAG,KAAK,SAAS,WAAW;AAExD,eAAS,QAAQ,SAAO;AACtB,cAAM,SAAS,OAAO,GAAG;AACzB,cAAM,SAAS,OAAO,GAAG;AAAA,MAC3B,CAAC;AAAA,IACH;AAAA,EACF;AACF;","names":[]}
@@ -1,3 +1,4 @@
1
+ import { type AuthErrorResponse } from '../../errors';
1
2
  export interface User {
2
3
  uid: string | null;
3
4
  email: string | null;
@@ -7,6 +8,11 @@ export interface Session {
7
8
  token: string | null;
8
9
  error: Error | null;
9
10
  }
11
+ interface TernVerificationResult extends User {
12
+ valid: boolean;
13
+ authTime?: number;
14
+ error?: AuthErrorResponse;
15
+ }
10
16
  export declare function createSessionCookie(idToken: string): Promise<{
11
17
  success: boolean;
12
18
  message: string;
@@ -23,18 +29,11 @@ export declare function setServerSession(token: string): Promise<{
23
29
  success: boolean;
24
30
  message: string;
25
31
  }>;
26
- export declare function verifyTernIdToken(token: string): Promise<{
27
- valid: boolean;
28
- uid?: string;
29
- error?: string;
30
- }>;
31
- export declare function verifyTernSessionCookie(session: string): Promise<{
32
- valid: boolean;
33
- uid?: any;
34
- error?: any;
35
- }>;
32
+ export declare function verifyTernIdToken(token: string): Promise<TernVerificationResult>;
33
+ export declare function verifyTernSessionCookie(session: string): Promise<TernVerificationResult>;
36
34
  export declare function clearSessionCookie(): Promise<{
37
35
  success: boolean;
38
36
  message: string;
39
37
  }>;
38
+ export {};
40
39
  //# sourceMappingURL=sessionTernSecure.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sessionTernSecure.d.ts","sourceRoot":"","sources":["../../../../src/app-router/admin/sessionTernSecure.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,IAAI;IACjB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAEH,MAAM,WAAW,OAAO;IACpB,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACvB;AAED,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,MAAM;;;GAgBxD;AAID,wBAAsB,sBAAsB;;;GAkB3C;AAGD,wBAAsB,UAAU;;;GAkB/B;AAED,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,MAAM;;;GAcnD;AAEC,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAuBhH;AAGD,wBAAsB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,GAAG,CAAC;IAAC,KAAK,CAAC,EAAE,GAAG,CAAA;CAAE,CAAC,CAWlH;AAGD,wBAAsB,kBAAkB;;;GAwBvC"}
1
+ {"version":3,"file":"sessionTernSecure.d.ts","sourceRoot":"","sources":["../../../../src/app-router/admin/sessionTernSecure.ts"],"names":[],"mappings":"AAIA,OAAO,EAA2B,KAAK,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAM/E,MAAM,WAAW,IAAI;IACjB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAEH,MAAM,WAAW,OAAO;IACpB,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACvB;AAED,UAAU,sBAAuB,SAAQ,IAAI;IAC3C,KAAK,EAAE,OAAO,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,iBAAiB,CAAA;CAC1B;AAED,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,MAAM;;;GAgBxD;AAID,wBAAsB,sBAAsB;;;GAkB3C;AAGD,wBAAsB,UAAU;;;GAkB/B;AAED,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,MAAM;;;GAcnD;AAEC,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAkBtF;AAGD,wBAAsB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAkB9F;AAGD,wBAAsB,kBAAkB;;;GAwBvC"}
@@ -1 +1 @@
1
- {"version":3,"file":"sign-in.d.ts","sourceRoot":"","sources":["../../../src/components/sign-in.tsx"],"names":[],"mappings":"AA6BA,MAAM,WAAW,WAAW;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IAChC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,YAAY,CAAC,EAAE;QACb,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,WAAW,CAAC,EAAE,MAAM,CAAA;QACpB,YAAY,CAAC,EAAE,MAAM,CAAA;KACtB,CAAA;CACF;AAGD,wBAAgB,MAAM,CAAC,EACrB,WAAW,EACX,OAAO,EACP,SAAS,EACT,SAAS,EACT,YAAiB,EAClB,EAAE,WAAW,2CAgWb"}
1
+ {"version":3,"file":"sign-in.d.ts","sourceRoot":"","sources":["../../../src/components/sign-in.tsx"],"names":[],"mappings":"AA8BA,MAAM,WAAW,WAAW;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IAChC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,YAAY,CAAC,EAAE;QACb,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,WAAW,CAAC,EAAE,MAAM,CAAA;QACpB,YAAY,CAAC,EAAE,MAAM,CAAA;KACtB,CAAA;CACF;AAOD,wBAAgB,MAAM,CAAC,EACrB,WAAW,EACX,OAAO,EACP,SAAS,EACT,SAAS,EACT,YAAiB,EAClB,EAAE,WAAW,2CAgWb"}
@@ -1,19 +1,23 @@
1
- import type { UserInfo } from "./edge-session";
1
+ import type { UserInfo } from "./types";
2
2
  export interface AuthResult {
3
3
  user: UserInfo | null;
4
- token: string | null;
5
4
  error: Error | null;
6
5
  }
7
6
  /**
8
7
  * Get the current authenticated user from the session or token
9
8
  */
10
- export declare function auth(): Promise<AuthResult>;
9
+ export declare const auth: () => Promise<AuthResult>;
11
10
  /**
12
11
  * Type guard to check if user is authenticated
13
12
  */
14
- export declare function isAuthenticated(): Promise<boolean>;
13
+ export declare const isAuthenticated: () => Promise<boolean>;
15
14
  /**
16
15
  * Get user info from auth result
17
16
  */
18
- export declare function getUserInfo(): Promise<UserInfo | null>;
17
+ export declare const getUser: () => Promise<UserInfo | null>;
18
+ /**
19
+ * Require authentication
20
+ * Throws error if not authenticated
21
+ */
22
+ export declare const requireAuth: () => Promise<UserInfo>;
19
23
  //# sourceMappingURL=auth.d.ts.map
@@ -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;AAMvC,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAA;IACrB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;CACpB;AAEC;;GAEG;AACL,eAAO,MAAM,IAAI,QAAmB,OAAO,CAAC,UAAU,CAoDlD,CAAA;AAEJ;;GAEG;AACH,eAAO,MAAM,eAAe,QAAmB,OAAO,CAAC,OAAO,CAG5D,CAAA;AAEF;;GAEG;AACH,eAAO,MAAM,OAAO,QAAmB,OAAO,CAAC,QAAQ,GAAG,IAAI,CAG5D,CAAA;AAEF;;;GAGG;AACH,eAAO,MAAM,WAAW,QAAmB,OAAO,CAAC,QAAQ,CAQzD,CAAA"}
@@ -0,0 +1,3 @@
1
+ export declare function encrypt(text: string): string;
2
+ export declare function decrypt(encoded: string): string;
3
+ //# sourceMappingURL=crypto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../../../src/server/crypto.ts"],"names":[],"mappings":"AAGA,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAI5C;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAM/C"}
@@ -0,0 +1,24 @@
1
+ import type { UserInfo } from "./types";
2
+ interface RequestContext {
3
+ user: UserInfo;
4
+ sessionId: string;
5
+ }
6
+ declare global {
7
+ var __ternSecure: {
8
+ context: RequestContext | null;
9
+ sessions: Map<string, UserInfo>;
10
+ };
11
+ }
12
+ export declare class ContextStore {
13
+ static setContext(context: RequestContext): void;
14
+ static getContext(): RequestContext | null;
15
+ static setSession(sessionId: string, user: UserInfo): void;
16
+ static getSession(sessionId: string): UserInfo | null;
17
+ static debug(): {
18
+ sessionsCount: number;
19
+ currentSessionId: string | null;
20
+ sessions: [string, UserInfo][];
21
+ };
22
+ }
23
+ export {};
24
+ //# sourceMappingURL=ctx-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ctx-store.d.ts","sourceRoot":"","sources":["../../../src/server/ctx-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAEvC,UAAU,cAAc;IACtB,IAAI,EAAE,QAAQ,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;CAClB;AAGD,OAAO,CAAC,MAAM,CAAC;IACb,IAAI,YAAY,EAAE;QAChB,OAAO,EAAE,cAAc,GAAG,IAAI,CAAA;QAC9B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;KAChC,CAAA;CACF;AAWD,qBAAa,YAAY;IACvB,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,cAAc;IAMzC,MAAM,CAAC,UAAU,IAAI,cAAc,GAAG,IAAI;IAM1C,MAAM,CAAC,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ;IAMnD,MAAM,CAAC,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI;IAMrD,MAAM,CAAC,KAAK;;;;;CAOb"}