@tern-secure/nextjs 5.2.0-canary.v20251127221555 → 5.2.0-canary.v20251202162458

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 (101) hide show
  1. package/dist/cjs/__tests__/gemini_fnTernSecureNextHandler.bench.js +2 -2
  2. package/dist/cjs/__tests__/gemini_fnTernSecureNextHandler.bench.js.map +1 -1
  3. package/dist/cjs/app-router/admin/actions.js.map +1 -1
  4. package/dist/cjs/app-router/admin/endpointRouter.js +4 -13
  5. package/dist/cjs/app-router/admin/endpointRouter.js.map +1 -1
  6. package/dist/cjs/app-router/admin/{sessionHandlers.js → handlers.js} +16 -115
  7. package/dist/cjs/app-router/admin/handlers.js.map +1 -0
  8. package/dist/cjs/app-router/admin/index.js.map +1 -1
  9. package/dist/cjs/app-router/admin/request.js +1 -8
  10. package/dist/cjs/app-router/admin/request.js.map +1 -1
  11. package/dist/cjs/app-router/admin/signInCreateHandler.js.map +1 -1
  12. package/dist/cjs/app-router/admin/ternsecureNextjsHandler.js +7 -17
  13. package/dist/cjs/app-router/admin/ternsecureNextjsHandler.js.map +1 -1
  14. package/dist/cjs/app-router/admin/types.js +9 -0
  15. package/dist/cjs/app-router/admin/types.js.map +1 -1
  16. package/dist/cjs/app-router/admin/validators.js +96 -171
  17. package/dist/cjs/app-router/admin/validators.js.map +1 -1
  18. package/dist/cjs/app-router/server/TernSecureProvider.js +18 -0
  19. package/dist/cjs/app-router/server/TernSecureProvider.js.map +1 -1
  20. package/dist/cjs/server/constant.js +6 -0
  21. package/dist/cjs/server/constant.js.map +1 -1
  22. package/dist/cjs/server/data/getAuthDataFromRequest.js +16 -9
  23. package/dist/cjs/server/data/getAuthDataFromRequest.js.map +1 -1
  24. package/dist/cjs/server/headers-utils.js +3 -3
  25. package/dist/cjs/server/headers-utils.js.map +1 -1
  26. package/dist/cjs/server/protect.js +2 -2
  27. package/dist/cjs/server/protect.js.map +1 -1
  28. package/dist/cjs/server/proxy-storage.js +33 -0
  29. package/dist/cjs/server/proxy-storage.js.map +1 -0
  30. package/dist/cjs/server/ternSecureProxy.js +6 -17
  31. package/dist/cjs/server/ternSecureProxy.js.map +1 -1
  32. package/dist/cjs/server/utils.js +16 -4
  33. package/dist/cjs/server/utils.js.map +1 -1
  34. package/dist/esm/__tests__/gemini_fnTernSecureNextHandler.bench.js +1 -1
  35. package/dist/esm/__tests__/gemini_fnTernSecureNextHandler.bench.js.map +1 -1
  36. package/dist/esm/app-router/admin/actions.js.map +1 -1
  37. package/dist/esm/app-router/admin/endpointRouter.js +3 -12
  38. package/dist/esm/app-router/admin/endpointRouter.js.map +1 -1
  39. package/dist/esm/app-router/admin/{sessionHandlers.js → handlers.js} +18 -110
  40. package/dist/esm/app-router/admin/handlers.js.map +1 -0
  41. package/dist/esm/app-router/admin/index.js.map +1 -1
  42. package/dist/esm/app-router/admin/request.js +2 -14
  43. package/dist/esm/app-router/admin/request.js.map +1 -1
  44. package/dist/esm/app-router/admin/signInCreateHandler.js.map +1 -1
  45. package/dist/esm/app-router/admin/ternsecureNextjsHandler.js +9 -19
  46. package/dist/esm/app-router/admin/ternsecureNextjsHandler.js.map +1 -1
  47. package/dist/esm/app-router/admin/types.js +8 -0
  48. package/dist/esm/app-router/admin/types.js.map +1 -1
  49. package/dist/esm/app-router/admin/validators.js +88 -166
  50. package/dist/esm/app-router/admin/validators.js.map +1 -1
  51. package/dist/esm/app-router/server/TernSecureProvider.js +19 -1
  52. package/dist/esm/app-router/server/TernSecureProvider.js.map +1 -1
  53. package/dist/esm/server/constant.js +4 -0
  54. package/dist/esm/server/constant.js.map +1 -1
  55. package/dist/esm/server/data/getAuthDataFromRequest.js +18 -11
  56. package/dist/esm/server/data/getAuthDataFromRequest.js.map +1 -1
  57. package/dist/esm/server/headers-utils.js +2 -2
  58. package/dist/esm/server/headers-utils.js.map +1 -1
  59. package/dist/esm/server/protect.js +2 -2
  60. package/dist/esm/server/protect.js.map +1 -1
  61. package/dist/esm/server/proxy-storage.js +8 -0
  62. package/dist/esm/server/proxy-storage.js.map +1 -0
  63. package/dist/esm/server/ternSecureProxy.js +11 -18
  64. package/dist/esm/server/ternSecureProxy.js.map +1 -1
  65. package/dist/esm/server/utils.js +16 -4
  66. package/dist/esm/server/utils.js.map +1 -1
  67. package/dist/types/app-router/admin/actions.d.ts +2 -2
  68. package/dist/types/app-router/admin/actions.d.ts.map +1 -1
  69. package/dist/types/app-router/admin/endpointRouter.d.ts +4 -4
  70. package/dist/types/app-router/admin/endpointRouter.d.ts.map +1 -1
  71. package/dist/types/app-router/admin/handlers.d.ts +5 -0
  72. package/dist/types/app-router/admin/handlers.d.ts.map +1 -0
  73. package/dist/types/app-router/admin/index.d.ts +1 -1
  74. package/dist/types/app-router/admin/index.d.ts.map +1 -1
  75. package/dist/types/app-router/admin/request.d.ts +2 -2
  76. package/dist/types/app-router/admin/request.d.ts.map +1 -1
  77. package/dist/types/app-router/admin/signInCreateHandler.d.ts +1 -1
  78. package/dist/types/app-router/admin/signInCreateHandler.d.ts.map +1 -1
  79. package/dist/types/app-router/admin/ternsecureNextjsHandler.d.ts +6 -2
  80. package/dist/types/app-router/admin/ternsecureNextjsHandler.d.ts.map +1 -1
  81. package/dist/types/app-router/admin/types.d.ts +24 -2
  82. package/dist/types/app-router/admin/types.d.ts.map +1 -1
  83. package/dist/types/app-router/admin/validators.d.ts +36 -33
  84. package/dist/types/app-router/admin/validators.d.ts.map +1 -1
  85. package/dist/types/app-router/server/TernSecureProvider.d.ts.map +1 -1
  86. package/dist/types/server/constant.d.ts +2 -0
  87. package/dist/types/server/constant.d.ts.map +1 -1
  88. package/dist/types/server/data/getAuthDataFromRequest.d.ts.map +1 -1
  89. package/dist/types/server/headers-utils.d.ts +1 -1
  90. package/dist/types/server/headers-utils.d.ts.map +1 -1
  91. package/dist/types/server/proxy-storage.d.ts +5 -0
  92. package/dist/types/server/proxy-storage.d.ts.map +1 -0
  93. package/dist/types/server/ternSecureProxy.d.ts +1 -3
  94. package/dist/types/server/ternSecureProxy.d.ts.map +1 -1
  95. package/dist/types/server/utils.d.ts +2 -2
  96. package/dist/types/server/utils.d.ts.map +1 -1
  97. package/package.json +5 -5
  98. package/dist/cjs/app-router/admin/sessionHandlers.js.map +0 -1
  99. package/dist/esm/app-router/admin/sessionHandlers.js.map +0 -1
  100. package/dist/types/app-router/admin/sessionHandlers.d.ts +0 -7
  101. package/dist/types/app-router/admin/sessionHandlers.d.ts.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/app-router/admin/handlers.ts"],"sourcesContent":["import type { AuthenticateRequestOptions, RequestProcessorContext } from '@tern-secure/backend';\nimport { clearSessionCookie } from '@tern-secure/backend/admin';\nimport { ternDecodeJwtUnguarded } from '@tern-secure/backend/jwt';\n\nimport { ternSecureBackendClient } from '../../server/ternsecureClient';\nimport { NextCookieStore } from '../../utils/NextCookieAdapter';\nimport { FIREBASE_API_KEY } from './constants';\nimport { refreshCookieWithIdToken } from './request';\nimport {\n createApiErrorResponse,\n createApiSuccessResponse,\n HttpResponseHelper,\n SessionResponseHelper,\n} from './responses';\nimport { processSignInCreate } from './signInCreateHandler';\nimport type { SessionSubEndpoint, SignInSubEndpoint, } from './types';\nimport {\n extractSessionRequestData,\n validateCsrfToken,\n validateEmail,\n validateIdToken,\n validateSubEndpointPresent,\n} from './validators';\n\nconst sessionEndpointHandler = async (\n context: RequestProcessorContext,\n config: AuthenticateRequestOptions,\n): Promise<Response> => {\n const { subEndpoint, method, referrer } = context;\n\n // Validate sub-endpoint exists\n const subEndpointError = validateSubEndpointPresent(context, 'Session');\n if (subEndpointError) return subEndpointError;\n\n const SessionGetHandler = async (subEndpoint: SessionSubEndpoint): Promise<Response> => {\n const handleSessionVerify = async (): Promise<Response> => {\n try {\n const sessionCookie = context.sessionTokenInCookie;\n if (!sessionCookie) {\n return SessionResponseHelper.createUnauthorizedResponse();\n }\n\n const { data: decodedSession, errors } = ternDecodeJwtUnguarded(sessionCookie);\n if (errors) {\n return SessionResponseHelper.createUnauthorizedResponse();\n }\n\n return SessionResponseHelper.createVerificationResponse(decodedSession);\n } catch (error) {\n return SessionResponseHelper.createUnauthorizedResponse();\n }\n };\n\n switch (subEndpoint) {\n case 'verify':\n return handleSessionVerify();\n default:\n return HttpResponseHelper.createNotFoundResponse();\n }\n };\n\n const SessionPostHandler = async (subEndpoint: SessionSubEndpoint): Promise<Response> => {\n const cookieStore = new NextCookieStore();\n\n const { idToken, csrfToken, error } = await extractSessionRequestData(context.request);\n if (error) return error;\n\n const csrfError = await validateCsrfToken(csrfToken);\n if (csrfError) return csrfError;\n\n const handleCreateSession = async (\n cookieStore: NextCookieStore,\n idToken: string,\n ): Promise<Response> => {\n try {\n await refreshCookieWithIdToken(idToken, cookieStore, config, referrer, context.appCheckToken);\n return SessionResponseHelper.createSessionCreationResponse({\n success: true,\n message: 'Session created successfully',\n });\n } catch (error) {\n console.error('[SessionHandler - createsession] Error:', error);\n const errorMessage = error instanceof Error ? error.message : 'Session creation failed';\n return createApiErrorResponse('SESSION_CREATION_FAILED', errorMessage, 500);\n }\n };\n\n const handleRefreshSession = async (\n cookieStore: NextCookieStore,\n idToken: string,\n ): Promise<Response> => {\n try {\n const decodedSession = ternDecodeJwtUnguarded(idToken);\n if (decodedSession.errors) {\n return createApiErrorResponse('INVALID_SESSION', 'Invalid session for refresh', 401);\n }\n\n const refreshRes = await refreshCookieWithIdToken(\n idToken,\n cookieStore,\n config,\n undefined,\n context.appCheckToken,\n );\n return SessionResponseHelper.createRefreshResponse(refreshRes);\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : 'Session refresh failed';\n return createApiErrorResponse('REFRESH_FAILED', errorMessage, 500);\n }\n };\n\n const handleRevokeSession = async (cookieStore: NextCookieStore): Promise<Response> => {\n const res = await clearSessionCookie(cookieStore);\n return SessionResponseHelper.createRevokeResponse(res);\n };\n\n switch (subEndpoint) {\n case 'createsession': {\n const idTokenError = validateIdToken(idToken);\n if (idTokenError) return idTokenError;\n //eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n return handleCreateSession(cookieStore, idToken!);\n }\n\n case 'refresh':\n //eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n return handleRefreshSession(cookieStore, idToken!);\n\n case 'revoke':\n return handleRevokeSession(cookieStore);\n\n default:\n return HttpResponseHelper.createSubEndpointNotSupportedResponse();\n }\n };\n\n switch (method) {\n case 'GET':\n return SessionGetHandler(subEndpoint as SessionSubEndpoint);\n\n case 'POST':\n return SessionPostHandler(subEndpoint as SessionSubEndpoint);\n\n default:\n return HttpResponseHelper.createMethodNotAllowedResponse();\n }\n}\n\n\nconst signInEndpointHandler = async (\n context: RequestProcessorContext,\n): Promise<Response> => {\n const { subEndpoint, method } = context;\n\n // Validate sub-endpoint exists\n const subEndpointError = validateSubEndpointPresent(context, 'Sign_ins');\n if (subEndpointError) return subEndpointError;\n\n const PostHandler = async (subEndpoint: SignInSubEndpoint): Promise<Response> => {\n const create = async (): Promise<Response> => {\n return await processSignInCreate(context);\n };\n\n const passwordResetEmail = async (): Promise<Response> => {\n try {\n const body = await context.request.json();\n const { email } = body;\n\n const emailError = validateEmail(email);\n if (emailError) return emailError;\n\n const backendClient = await ternSecureBackendClient();\n\n const response = await backendClient.signIn.resetPasswordEmail(FIREBASE_API_KEY, {\n email,\n requestType: 'PASSWORD_RESET',\n });\n\n if (!response) {\n return createApiErrorResponse(\n 'PASSWORD_RESET_FAILED',\n 'Failed to send password reset email',\n 500,\n );\n }\n\n return createApiSuccessResponse({\n email,\n });\n } catch (error) {\n return createApiErrorResponse(\n 'PASSWORD_RESET_ERROR',\n error instanceof Error\n ? error.message\n : 'An error occurred while sending password reset email',\n 500,\n );\n }\n };\n\n switch (subEndpoint) {\n case 'create':\n return create();\n case 'resetPasswordEmail':\n return passwordResetEmail();\n default:\n return HttpResponseHelper.createSubEndpointNotSupportedResponse();\n }\n };\n\n switch (method) {\n case 'POST':\n return PostHandler(subEndpoint as SignInSubEndpoint);\n\n default:\n return HttpResponseHelper.createMethodNotAllowedResponse();\n }\n\n}\n\nexport { sessionEndpointHandler, signInEndpointHandler };\n"],"mappings":"AACA,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AAEvC,SAAS,+BAA+B;AACxC,SAAS,uBAAuB;AAChC,SAAS,wBAAwB;AACjC,SAAS,gCAAgC;AACzC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,2BAA2B;AAEpC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,MAAM,yBAAyB,OAC7B,SACA,WACsB;AACtB,QAAM,EAAE,aAAa,QAAQ,SAAS,IAAI;AAG1C,QAAM,mBAAmB,2BAA2B,SAAS,SAAS;AACtE,MAAI,iBAAkB,QAAO;AAE7B,QAAM,oBAAoB,OAAOA,iBAAuD;AACtF,UAAM,sBAAsB,YAA+B;AACzD,UAAI;AACF,cAAM,gBAAgB,QAAQ;AAC9B,YAAI,CAAC,eAAe;AAClB,iBAAO,sBAAsB,2BAA2B;AAAA,QAC1D;AAEA,cAAM,EAAE,MAAM,gBAAgB,OAAO,IAAI,uBAAuB,aAAa;AAC7E,YAAI,QAAQ;AACV,iBAAO,sBAAsB,2BAA2B;AAAA,QAC1D;AAEA,eAAO,sBAAsB,2BAA2B,cAAc;AAAA,MACxE,SAAS,OAAO;AACd,eAAO,sBAAsB,2BAA2B;AAAA,MAC1D;AAAA,IACF;AAEA,YAAQA,cAAa;AAAA,MACnB,KAAK;AACH,eAAO,oBAAoB;AAAA,MAC7B;AACE,eAAO,mBAAmB,uBAAuB;AAAA,IACrD;AAAA,EACF;AAEA,QAAM,qBAAqB,OAAOA,iBAAuD;AACvF,UAAM,cAAc,IAAI,gBAAgB;AAExC,UAAM,EAAE,SAAS,WAAW,MAAM,IAAI,MAAM,0BAA0B,QAAQ,OAAO;AACrF,QAAI,MAAO,QAAO;AAElB,UAAM,YAAY,MAAM,kBAAkB,SAAS;AACnD,QAAI,UAAW,QAAO;AAEtB,UAAM,sBAAsB,OAC1BC,cACAC,aACsB;AACtB,UAAI;AACF,cAAM,yBAAyBA,UAASD,cAAa,QAAQ,UAAU,QAAQ,aAAa;AAC5F,eAAO,sBAAsB,8BAA8B;AAAA,UACzD,SAAS;AAAA,UACT,SAAS;AAAA,QACX,CAAC;AAAA,MACH,SAASE,QAAO;AACd,gBAAQ,MAAM,2CAA2CA,MAAK;AAC9D,cAAM,eAAeA,kBAAiB,QAAQA,OAAM,UAAU;AAC9D,eAAO,uBAAuB,2BAA2B,cAAc,GAAG;AAAA,MAC5E;AAAA,IACF;AAEA,UAAM,uBAAuB,OAC3BF,cACAC,aACsB;AACtB,UAAI;AACF,cAAM,iBAAiB,uBAAuBA,QAAO;AACrD,YAAI,eAAe,QAAQ;AACzB,iBAAO,uBAAuB,mBAAmB,+BAA+B,GAAG;AAAA,QACrF;AAEA,cAAM,aAAa,MAAM;AAAA,UACvBA;AAAA,UACAD;AAAA,UACA;AAAA,UACA;AAAA,UACA,QAAQ;AAAA,QACV;AACA,eAAO,sBAAsB,sBAAsB,UAAU;AAAA,MAC/D,SAASE,QAAO;AACd,cAAM,eAAeA,kBAAiB,QAAQA,OAAM,UAAU;AAC9D,eAAO,uBAAuB,kBAAkB,cAAc,GAAG;AAAA,MACnE;AAAA,IACF;AAEA,UAAM,sBAAsB,OAAOF,iBAAoD;AACrF,YAAM,MAAM,MAAM,mBAAmBA,YAAW;AAChD,aAAO,sBAAsB,qBAAqB,GAAG;AAAA,IACvD;AAEA,YAAQD,cAAa;AAAA,MACnB,KAAK,iBAAiB;AACpB,cAAM,eAAe,gBAAgB,OAAO;AAC5C,YAAI,aAAc,QAAO;AAEzB,eAAO,oBAAoB,aAAa,OAAQ;AAAA,MAClD;AAAA,MAEA,KAAK;AAEH,eAAO,qBAAqB,aAAa,OAAQ;AAAA,MAEnD,KAAK;AACH,eAAO,oBAAoB,WAAW;AAAA,MAExC;AACE,eAAO,mBAAmB,sCAAsC;AAAA,IACpE;AAAA,EACF;AAEA,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO,kBAAkB,WAAiC;AAAA,IAE5D,KAAK;AACH,aAAO,mBAAmB,WAAiC;AAAA,IAE7D;AACE,aAAO,mBAAmB,+BAA+B;AAAA,EAC7D;AACF;AAGA,MAAM,wBAAwB,OAC5B,YACsB;AACtB,QAAM,EAAE,aAAa,OAAO,IAAI;AAGhC,QAAM,mBAAmB,2BAA2B,SAAS,UAAU;AACvE,MAAI,iBAAkB,QAAO;AAE7B,QAAM,cAAc,OAAOA,iBAAsD;AAC/E,UAAM,SAAS,YAA+B;AAC5C,aAAO,MAAM,oBAAoB,OAAO;AAAA,IAC1C;AAEA,UAAM,qBAAqB,YAA+B;AACxD,UAAI;AACF,cAAM,OAAO,MAAM,QAAQ,QAAQ,KAAK;AACxC,cAAM,EAAE,MAAM,IAAI;AAElB,cAAM,aAAa,cAAc,KAAK;AACtC,YAAI,WAAY,QAAO;AAEvB,cAAM,gBAAgB,MAAM,wBAAwB;AAEpD,cAAM,WAAW,MAAM,cAAc,OAAO,mBAAmB,kBAAkB;AAAA,UAC/E;AAAA,UACA,aAAa;AAAA,QACf,CAAC;AAED,YAAI,CAAC,UAAU;AACb,iBAAO;AAAA,YACL;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,QACF;AAEA,eAAO,yBAAyB;AAAA,UAC9B;AAAA,QACF,CAAC;AAAA,MACH,SAAS,OAAO;AACd,eAAO;AAAA,UACL;AAAA,UACA,iBAAiB,QACb,MAAM,UACN;AAAA,UACJ;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,YAAQA,cAAa;AAAA,MACnB,KAAK;AACH,eAAO,OAAO;AAAA,MAChB,KAAK;AACH,eAAO,mBAAmB;AAAA,MAC5B;AACE,eAAO,mBAAmB,sCAAsC;AAAA,IACpE;AAAA,EACF;AAEA,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO,YAAY,WAAgC;AAAA,IAErD;AACE,aAAO,mBAAmB,+BAA+B;AAAA,EAC7D;AAEF;","names":["subEndpoint","cookieStore","idToken","error"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/app-router/admin/index.ts"],"sourcesContent":["export { createTernSecureNextJsHandler } from './ternsecureNextjsHandler'\n\nexport {\n clearSessionCookieServer,\n clearNextSessionCookie,\n createSessionCookieServer,\n createNextSessionCookie,\n setNextServerSession,\n setNextServerToken\n} from './actions'\n\nexport { EndpointRouter } from './endpointRouter'\n\nexport type { TernSecureHandlerOptions } from './types'"],"mappings":"AAAA,SAAS,qCAAqC;AAE9C;AAAA,EACI;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACG;AAEP,SAAS,sBAAsB;","names":[]}
1
+ {"version":3,"sources":["../../../../src/app-router/admin/index.ts"],"sourcesContent":["export { createTernSecureNextJsHandler } from './ternsecureNextjsHandler'\n\nexport {\n clearSessionCookieServer,\n clearNextSessionCookie,\n createSessionCookieServer,\n createNextSessionCookie,\n setNextServerSession,\n setNextServerToken\n} from './actions'\n\nexport { EndpointRouter } from './endpointRouter'\n\nexport type { ApiHandlerOptions, TernSecureHandlerOptions } from './types'"],"mappings":"AAAA,SAAS,qCAAqC;AAE9C;AAAA,EACI;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACG;AAEP,SAAS,sBAAsB;","names":[]}
@@ -3,26 +3,14 @@ import { getAuth } from "@tern-secure/backend/auth";
3
3
  import { getCookieName, getCookiePrefix } from "@tern-secure/shared/cookie";
4
4
  import { ternSecureBackendClient } from "../../server/ternsecureClient";
5
5
  import {
6
- FIREBASE_API_KEY,
7
- FIREBASE_APP_ID,
8
- FIREBASE_AUTH_DOMAIN,
9
- FIREBASE_MESSAGING_SENDER_ID,
10
- FIREBASE_PROJECT_ID,
11
- FIREBASE_STORAGE_BUCKET
6
+ FIREBASE_API_KEY
12
7
  } from "./constants";
13
8
  import { getIdTokenCookieOptions } from "./cookieOptionsHelper";
14
9
  async function refreshCookieWithIdToken(idToken, cookieStore, config, referrer, appCheckToken) {
15
10
  const backendClient = await ternSecureBackendClient();
16
11
  const authOptions = {
17
12
  tenantId: config?.tenantId || void 0,
18
- firebaseConfig: {
19
- apiKey: FIREBASE_API_KEY,
20
- authDomain: FIREBASE_AUTH_DOMAIN,
21
- projectId: FIREBASE_PROJECT_ID,
22
- storageBucket: FIREBASE_STORAGE_BUCKET,
23
- messagingSenderId: FIREBASE_MESSAGING_SENDER_ID,
24
- appId: FIREBASE_APP_ID
25
- },
13
+ apiKey: FIREBASE_API_KEY,
26
14
  apiClient: backendClient
27
15
  };
28
16
  const COOKIE_OPTIONS = getIdTokenCookieOptions();
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/app-router/admin/request.ts"],"sourcesContent":["import type { AuthenticateRequestOptions } from '@tern-secure/backend';\nimport { constants } from '@tern-secure/backend';\nimport { getAuth } from '@tern-secure/backend/auth';\nimport { getCookieName, getCookiePrefix } from '@tern-secure/shared/cookie';\n\nimport { ternSecureBackendClient } from '../../server/ternsecureClient';\nimport type { NextCookieStore } from '../../utils/NextCookieAdapter';\nimport {\n FIREBASE_API_KEY,\n FIREBASE_APP_ID,\n FIREBASE_AUTH_DOMAIN,\n FIREBASE_MESSAGING_SENDER_ID,\n FIREBASE_PROJECT_ID,\n FIREBASE_STORAGE_BUCKET,\n} from './constants';\nimport { getIdTokenCookieOptions } from './cookieOptionsHelper';\nimport type { TernSecureHandlerOptions } from './types';\n\nexport async function refreshCookieWithIdToken(\n idToken: string,\n cookieStore: NextCookieStore,\n config?: TernSecureHandlerOptions,\n referrer?: string,\n appCheckToken?: string,\n): Promise<void> {\n const backendClient = await ternSecureBackendClient();\n\n const authOptions: AuthenticateRequestOptions = {\n tenantId: config?.tenantId || undefined,\n firebaseConfig: {\n apiKey: FIREBASE_API_KEY,\n authDomain: FIREBASE_AUTH_DOMAIN,\n projectId: FIREBASE_PROJECT_ID,\n storageBucket: FIREBASE_STORAGE_BUCKET,\n messagingSenderId: FIREBASE_MESSAGING_SENDER_ID,\n appId: FIREBASE_APP_ID,\n },\n apiClient: backendClient,\n };\n\n const COOKIE_OPTIONS = getIdTokenCookieOptions();\n\n const { createCustomIdAndRefreshToken } = getAuth(authOptions);\n\n\n const customTokens = await createCustomIdAndRefreshToken(idToken, {\n referer: referrer,\n appCheckToken,\n });\n\n\n const cookiePrefix = getCookiePrefix();\n\n const cookiePromises = [\n cookieStore.set(\n getCookieName(constants.Cookies.IdToken, cookiePrefix),\n customTokens.idToken,\n COOKIE_OPTIONS,\n ),\n cookieStore.set(\n getCookieName(constants.Cookies.Refresh, cookiePrefix),\n customTokens.refreshToken,\n COOKIE_OPTIONS,\n ),\n\n cookieStore.set(\n constants.Cookies.TernAut,\n customTokens.auth_time.toString(),\n { secure: true, maxAge: 365 * 24 * 60 * 60 }\n ),\n ];\n\n if (config?.enableCustomToken) {\n cookiePromises.push(\n cookieStore.set(constants.Cookies.Custom, customTokens.customToken, COOKIE_OPTIONS),\n );\n }\n\n await Promise.all(cookiePromises);\n}\n"],"mappings":"AACA,SAAS,iBAAiB;AAC1B,SAAS,eAAe;AACxB,SAAS,eAAe,uBAAuB;AAE/C,SAAS,+BAA+B;AAExC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,+BAA+B;AAGxC,eAAsB,yBACpB,SACA,aACA,QACA,UACA,eACe;AACf,QAAM,gBAAgB,MAAM,wBAAwB;AAEpD,QAAM,cAA0C;AAAA,IAC9C,UAAU,QAAQ,YAAY;AAAA,IAC9B,gBAAgB;AAAA,MACd,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,WAAW;AAAA,MACX,eAAe;AAAA,MACf,mBAAmB;AAAA,MACnB,OAAO;AAAA,IACT;AAAA,IACA,WAAW;AAAA,EACb;AAEA,QAAM,iBAAiB,wBAAwB;AAE/C,QAAM,EAAE,8BAA8B,IAAI,QAAQ,WAAW;AAG7D,QAAM,eAAe,MAAM,8BAA8B,SAAS;AAAA,IAChE,SAAS;AAAA,IACT;AAAA,EACF,CAAC;AAGD,QAAM,eAAe,gBAAgB;AAErC,QAAM,iBAAiB;AAAA,IACrB,YAAY;AAAA,MACV,cAAc,UAAU,QAAQ,SAAS,YAAY;AAAA,MACrD,aAAa;AAAA,MACb;AAAA,IACF;AAAA,IACA,YAAY;AAAA,MACV,cAAc,UAAU,QAAQ,SAAS,YAAY;AAAA,MACrD,aAAa;AAAA,MACb;AAAA,IACF;AAAA,IAEA,YAAY;AAAA,MACV,UAAU,QAAQ;AAAA,MAClB,aAAa,UAAU,SAAS;AAAA,MAChC,EAAE,QAAQ,MAAM,QAAQ,MAAM,KAAK,KAAK,GAAG;AAAA,IAC7C;AAAA,EACF;AAEA,MAAI,QAAQ,mBAAmB;AAC7B,mBAAe;AAAA,MACb,YAAY,IAAI,UAAU,QAAQ,QAAQ,aAAa,aAAa,cAAc;AAAA,IACpF;AAAA,EACF;AAEA,QAAM,QAAQ,IAAI,cAAc;AAClC;","names":[]}
1
+ {"version":3,"sources":["../../../../src/app-router/admin/request.ts"],"sourcesContent":["import type { AuthenticateRequestOptions } from '@tern-secure/backend';\nimport { constants } from '@tern-secure/backend';\nimport { getAuth } from '@tern-secure/backend/auth';\nimport { getCookieName, getCookiePrefix } from '@tern-secure/shared/cookie';\n\nimport { ternSecureBackendClient } from '../../server/ternsecureClient';\nimport type { NextCookieStore } from '../../utils/NextCookieAdapter';\nimport {\n FIREBASE_API_KEY,\n} from './constants';\nimport { getIdTokenCookieOptions } from './cookieOptionsHelper';\nimport type { ApiHandlerOptions } from './types';\n\nexport async function refreshCookieWithIdToken(\n idToken: string,\n cookieStore: NextCookieStore,\n config: ApiHandlerOptions,\n referrer?: string,\n appCheckToken?: string,\n): Promise<void> {\n const backendClient = await ternSecureBackendClient();\n\n const authOptions: AuthenticateRequestOptions = {\n tenantId: config?.tenantId || undefined,\n apiKey: FIREBASE_API_KEY,\n apiClient: backendClient,\n };\n\n const COOKIE_OPTIONS = getIdTokenCookieOptions();\n\n const { createCustomIdAndRefreshToken } = getAuth(authOptions);\n\n const customTokens = await createCustomIdAndRefreshToken(idToken, {\n referer: referrer,\n appCheckToken,\n });\n\n\n const cookiePrefix = getCookiePrefix();\n\n const cookiePromises = [\n cookieStore.set(\n getCookieName(constants.Cookies.IdToken, cookiePrefix),\n customTokens.idToken,\n COOKIE_OPTIONS,\n ),\n cookieStore.set(\n getCookieName(constants.Cookies.Refresh, cookiePrefix),\n customTokens.refreshToken,\n COOKIE_OPTIONS,\n ),\n\n cookieStore.set(\n constants.Cookies.TernAut,\n customTokens.auth_time.toString(),\n { secure: true, maxAge: 365 * 24 * 60 * 60 }\n ),\n ];\n\n if (config?.enableCustomToken) {\n cookiePromises.push(\n cookieStore.set(constants.Cookies.Custom, customTokens.customToken, COOKIE_OPTIONS),\n );\n }\n\n await Promise.all(cookiePromises);\n}\n"],"mappings":"AACA,SAAS,iBAAiB;AAC1B,SAAS,eAAe;AACxB,SAAS,eAAe,uBAAuB;AAE/C,SAAS,+BAA+B;AAExC;AAAA,EACE;AAAA,OACK;AACP,SAAS,+BAA+B;AAGxC,eAAsB,yBACpB,SACA,aACA,QACA,UACA,eACe;AACf,QAAM,gBAAgB,MAAM,wBAAwB;AAEpD,QAAM,cAA0C;AAAA,IAC9C,UAAU,QAAQ,YAAY;AAAA,IAC9B,QAAQ;AAAA,IACR,WAAW;AAAA,EACb;AAEA,QAAM,iBAAiB,wBAAwB;AAE/C,QAAM,EAAE,8BAA8B,IAAI,QAAQ,WAAW;AAE7D,QAAM,eAAe,MAAM,8BAA8B,SAAS;AAAA,IAChE,SAAS;AAAA,IACT;AAAA,EACF,CAAC;AAGD,QAAM,eAAe,gBAAgB;AAErC,QAAM,iBAAiB;AAAA,IACrB,YAAY;AAAA,MACV,cAAc,UAAU,QAAQ,SAAS,YAAY;AAAA,MACrD,aAAa;AAAA,MACb;AAAA,IACF;AAAA,IACA,YAAY;AAAA,MACV,cAAc,UAAU,QAAQ,SAAS,YAAY;AAAA,MACrD,aAAa;AAAA,MACb;AAAA,IACF;AAAA,IAEA,YAAY;AAAA,MACV,UAAU,QAAQ;AAAA,MAClB,aAAa,UAAU,SAAS;AAAA,MAChC,EAAE,QAAQ,MAAM,QAAQ,MAAM,KAAK,KAAK,GAAG;AAAA,IAC7C;AAAA,EACF;AAEA,MAAI,QAAQ,mBAAmB;AAC7B,mBAAe;AAAA,MACb,YAAY,IAAI,UAAU,QAAQ,QAAQ,aAAa,aAAa,cAAc;AAAA,IACpF;AAAA,EACF;AAEA,QAAM,QAAQ,IAAI,cAAc;AAClC;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/app-router/admin/signInCreateHandler.ts"],"sourcesContent":["import type { SignInCreateParams } from '@tern-secure/types';\n\nimport { RetrieveUser } from './actions';\nimport type { RequestProcessorContext } from './c-authenticateRequestProcessor';\nimport {\n createApiErrorResponse,\n createApiSuccessResponse,\n} from './responses';\n\n\nexport const processSignInCreate = async (\n context: RequestProcessorContext\n): Promise<Response> => {\n try {\n const body = await context.request.json();\n const { strategy, identifier } = body as SignInCreateParams & { identifier?: string; password?: string };\n\n if (!strategy) {\n return createApiErrorResponse(\n 'STRATEGY_REQUIRED',\n 'Authentication strategy is required',\n 400\n );\n }\n\n if (!identifier) {\n return createApiSuccessResponse({\n status: 'needs_identifier',\n strategy,\n message: 'Identifier is required to continue',\n });\n }\n\n if (strategy === 'email_code') {\n return await processEmailCodeStrategy(identifier);\n }\n\n if (strategy === 'password') {\n return await processPasswordStrategy(identifier);\n }\n\n if (strategy === 'phone_code') {\n return processPhoneCodeStrategy(identifier);\n }\n\n if (strategy === 'reset_password_email_code' || strategy === 'reset_password_phone_code') {\n return await processResetPasswordStrategy(strategy, identifier);\n }\n\n return createApiErrorResponse(\n 'INVALID_STRATEGY',\n `Unsupported authentication strategy: ${strategy}`,\n 400\n );\n } catch (error) {\n return createApiErrorResponse(\n 'SIGN_IN_CREATE_ERROR',\n error instanceof Error\n ? error.message\n : 'An error occurred while creating sign-in',\n 500\n );\n }\n};\n\n/**\n * Processes email_code strategy\n * Verifies if user exists by email and returns needs_first_factor status\n */\nexport const processEmailCodeStrategy = async (email: string): Promise<Response> => {\n try {\n const retrieveUser = RetrieveUser();\n const { data: user, error } = await retrieveUser.getUserByEmail(email);\n\n if (error) {\n return createApiErrorResponse(\n error.code,\n error.message,\n 400\n );\n }\n\n if (!user.emailVerified) {\n return createApiSuccessResponse({\n status: 'needs_email_verification',\n identifier: email,\n supportedFirstFactors: [{ strategy: 'email_code' }],\n userId: user.uid,\n message: 'Email verification required',\n });\n }\n\n return createApiSuccessResponse({\n status: 'needs_first_factor',\n identifier: email,\n supportedFirstFactors: [{ strategy: 'email_code' }],\n userId: user.uid,\n message: 'User verified. Proceed with first factor authentication',\n });\n } catch (error) {\n if (error instanceof Error && error.message.includes('no user record')) {\n return createApiErrorResponse(\n 'USER_NOT_FOUND',\n 'No user found with this email address',\n 404\n );\n }\n\n return createApiErrorResponse(\n 'EMAIL_VERIFICATION_ERROR',\n error instanceof Error ? error.message : 'Failed to verify email',\n 500\n );\n }\n};\n\n\nexport const processPasswordStrategy = async (identifier: string): Promise<Response> => {\n try {\n const retrieveUser = RetrieveUser();\n const { data: user, error } = await retrieveUser.getUserByEmail(identifier);\n\n if (error) {\n return createApiErrorResponse(\n error.code,\n error.message,\n 400\n );\n }\n\n return createApiSuccessResponse({\n status: 'needs_first_factor',\n identifier,\n supportedFirstFactors: [{ strategy: 'password' }],\n userId: user.uid,\n message: 'User verified. Proceed with password authentication',\n });\n } catch (error) {\n if (error instanceof Error && error.message.includes('no user record')) {\n return createApiErrorResponse(\n 'USER_NOT_FOUND',\n 'No user found with this identifier',\n 404\n );\n }\n\n return createApiErrorResponse(\n 'USER_VERIFICATION_ERROR',\n error instanceof Error ? error.message : 'Failed to verify user',\n 500\n );\n }\n};\n\n\nexport const processPhoneCodeStrategy = async (phoneNumber: string): Promise<Response> => {\n try {\n //const retrieveUser = RetrieveUser();\n //const { data: user, error } = await retrieveUser.getUserByPhoneNumber(phoneNumber);\n\n //if (error) {\n // return createApiErrorResponse(\n // error.code,\n // error.message,\n // 400\n // );\n // }\n\n return createApiSuccessResponse({\n status: 'needs_first_factor',\n identifier: phoneNumber,\n supportedFirstFactors: [{ strategy: 'phone_code' }],\n //userId: user.uid,\n message: 'User verified. Proceed with phone authentication',\n });\n } catch (error) {\n if (error instanceof Error && error.message.includes('no user record')) {\n return createApiErrorResponse(\n 'USER_NOT_FOUND',\n 'No user found with this phone number',\n 404\n );\n }\n\n return createApiErrorResponse(\n 'PHONE_VERIFICATION_ERROR',\n error instanceof Error ? error.message : 'Failed to verify phone number',\n 500\n );\n }\n};\n\n\nexport const processResetPasswordStrategy = async (\n strategy: 'reset_password_email_code' | 'reset_password_phone_code',\n identifier: string\n): Promise<Response> => {\n try {\n if (strategy === 'reset_password_email_code') {\n const retrieveUser = RetrieveUser();\n const { data: user, error } = await retrieveUser.getUserByEmail(identifier);\n\n if (error) {\n return createApiErrorResponse(\n error.code,\n error.message,\n 400\n );\n }\n\n return createApiSuccessResponse({\n status: 'needs_first_factor',\n identifier,\n strategy,\n userId: user.uid,\n message: 'User verified. Proceed with password reset',\n });\n }\n\n return createApiErrorResponse(\n 'NOT_IMPLEMENTED',\n 'Phone reset password strategy not yet implemented',\n 501\n );\n } catch (error) {\n return createApiErrorResponse(\n 'RESET_PASSWORD_ERROR',\n error instanceof Error ? error.message : 'Failed to process password reset',\n 500\n );\n }\n};\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../src/app-router/admin/signInCreateHandler.ts"],"sourcesContent":["import type { RequestProcessorContext } from '@tern-secure/backend';\nimport type { SignInCreateParams } from '@tern-secure/types';\n\nimport { RetrieveUser } from './actions';\nimport {\n createApiErrorResponse,\n createApiSuccessResponse,\n} from './responses';\n\n\nexport const processSignInCreate = async (\n context: RequestProcessorContext\n): Promise<Response> => {\n try {\n const body = await context.request.json();\n const { strategy, identifier } = body as SignInCreateParams & { identifier?: string; password?: string };\n\n if (!strategy) {\n return createApiErrorResponse(\n 'STRATEGY_REQUIRED',\n 'Authentication strategy is required',\n 400\n );\n }\n\n if (!identifier) {\n return createApiSuccessResponse({\n status: 'needs_identifier',\n strategy,\n message: 'Identifier is required to continue',\n });\n }\n\n if (strategy === 'email_code') {\n return await processEmailCodeStrategy(identifier);\n }\n\n if (strategy === 'password') {\n return await processPasswordStrategy(identifier);\n }\n\n if (strategy === 'phone_code') {\n return processPhoneCodeStrategy(identifier);\n }\n\n if (strategy === 'reset_password_email_code' || strategy === 'reset_password_phone_code') {\n return await processResetPasswordStrategy(strategy, identifier);\n }\n\n return createApiErrorResponse(\n 'INVALID_STRATEGY',\n `Unsupported authentication strategy: ${strategy}`,\n 400\n );\n } catch (error) {\n return createApiErrorResponse(\n 'SIGN_IN_CREATE_ERROR',\n error instanceof Error\n ? error.message\n : 'An error occurred while creating sign-in',\n 500\n );\n }\n};\n\n/**\n * Processes email_code strategy\n * Verifies if user exists by email and returns needs_first_factor status\n */\nexport const processEmailCodeStrategy = async (email: string): Promise<Response> => {\n try {\n const retrieveUser = RetrieveUser();\n const { data: user, error } = await retrieveUser.getUserByEmail(email);\n\n if (error) {\n return createApiErrorResponse(\n error.code,\n error.message,\n 400\n );\n }\n\n if (!user.emailVerified) {\n return createApiSuccessResponse({\n status: 'needs_email_verification',\n identifier: email,\n supportedFirstFactors: [{ strategy: 'email_code' }],\n userId: user.uid,\n message: 'Email verification required',\n });\n }\n\n return createApiSuccessResponse({\n status: 'needs_first_factor',\n identifier: email,\n supportedFirstFactors: [{ strategy: 'email_code' }],\n userId: user.uid,\n message: 'User verified. Proceed with first factor authentication',\n });\n } catch (error) {\n if (error instanceof Error && error.message.includes('no user record')) {\n return createApiErrorResponse(\n 'USER_NOT_FOUND',\n 'No user found with this email address',\n 404\n );\n }\n\n return createApiErrorResponse(\n 'EMAIL_VERIFICATION_ERROR',\n error instanceof Error ? error.message : 'Failed to verify email',\n 500\n );\n }\n};\n\n\nexport const processPasswordStrategy = async (identifier: string): Promise<Response> => {\n try {\n const retrieveUser = RetrieveUser();\n const { data: user, error } = await retrieveUser.getUserByEmail(identifier);\n\n if (error) {\n return createApiErrorResponse(\n error.code,\n error.message,\n 400\n );\n }\n\n return createApiSuccessResponse({\n status: 'needs_first_factor',\n identifier,\n supportedFirstFactors: [{ strategy: 'password' }],\n userId: user.uid,\n message: 'User verified. Proceed with password authentication',\n });\n } catch (error) {\n if (error instanceof Error && error.message.includes('no user record')) {\n return createApiErrorResponse(\n 'USER_NOT_FOUND',\n 'No user found with this identifier',\n 404\n );\n }\n\n return createApiErrorResponse(\n 'USER_VERIFICATION_ERROR',\n error instanceof Error ? error.message : 'Failed to verify user',\n 500\n );\n }\n};\n\n\nexport const processPhoneCodeStrategy = async (phoneNumber: string): Promise<Response> => {\n try {\n //const retrieveUser = RetrieveUser();\n //const { data: user, error } = await retrieveUser.getUserByPhoneNumber(phoneNumber);\n\n //if (error) {\n // return createApiErrorResponse(\n // error.code,\n // error.message,\n // 400\n // );\n // }\n\n return createApiSuccessResponse({\n status: 'needs_first_factor',\n identifier: phoneNumber,\n supportedFirstFactors: [{ strategy: 'phone_code' }],\n //userId: user.uid,\n message: 'User verified. Proceed with phone authentication',\n });\n } catch (error) {\n if (error instanceof Error && error.message.includes('no user record')) {\n return createApiErrorResponse(\n 'USER_NOT_FOUND',\n 'No user found with this phone number',\n 404\n );\n }\n\n return createApiErrorResponse(\n 'PHONE_VERIFICATION_ERROR',\n error instanceof Error ? error.message : 'Failed to verify phone number',\n 500\n );\n }\n};\n\n\nexport const processResetPasswordStrategy = async (\n strategy: 'reset_password_email_code' | 'reset_password_phone_code',\n identifier: string\n): Promise<Response> => {\n try {\n if (strategy === 'reset_password_email_code') {\n const retrieveUser = RetrieveUser();\n const { data: user, error } = await retrieveUser.getUserByEmail(identifier);\n\n if (error) {\n return createApiErrorResponse(\n error.code,\n error.message,\n 400\n );\n }\n\n return createApiSuccessResponse({\n status: 'needs_first_factor',\n identifier,\n strategy,\n userId: user.uid,\n message: 'User verified. Proceed with password reset',\n });\n }\n\n return createApiErrorResponse(\n 'NOT_IMPLEMENTED',\n 'Phone reset password strategy not yet implemented',\n 501\n );\n } catch (error) {\n return createApiErrorResponse(\n 'RESET_PASSWORD_ERROR',\n error instanceof Error ? error.message : 'Failed to process password reset',\n 500\n );\n }\n};\n"],"mappings":"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","names":[]}
@@ -1,28 +1,18 @@
1
- import { createTernSecureRequest } from "@tern-secure/backend";
2
- import { createRequestProcessor } from "./c-authenticateRequestProcessor";
1
+ import { createRequestProcessor, createTernSecureRequest } from "@tern-secure/backend";
3
2
  import { TENANT_ID } from "./constants";
4
3
  import { EndpointRouter } from "./endpointRouter";
5
- import { createValidators } from "./fnValidators";
6
4
  import { createApiErrorResponse } from "./responses";
7
- import { DEFAULT_HANDLER_OPTIONS } from "./types";
8
- import { ConfigUtils } from "./utils";
9
- function createHandlerConfig(options) {
10
- const baseConfig = ConfigUtils.mergeWithDefaults(
11
- DEFAULT_HANDLER_OPTIONS,
12
- options
13
- );
14
- return {
15
- ...baseConfig,
16
- tenantId: TENANT_ID
17
- };
18
- }
5
+ import { DEFAULT_API_HANDLER_OPTIONS } from "./types";
19
6
  function createTernSecureNextJsHandler(options) {
20
- const config = createHandlerConfig(options);
7
+ const config = {
8
+ ...DEFAULT_API_HANDLER_OPTIONS,
9
+ ...options,
10
+ tenantId: options?.tenantId || TENANT_ID
11
+ };
21
12
  const handler = async (request) => {
22
13
  try {
23
- const context = createRequestProcessor(createTernSecureRequest(request), options);
24
- const { validateSecurity } = createValidators(context);
25
- await validateSecurity(options.security || {});
14
+ const ternRequest = createTernSecureRequest(request);
15
+ const context = createRequestProcessor(ternRequest, config);
26
16
  if (!context.endpoint) {
27
17
  return createApiErrorResponse("ENDPOINT_REQUIRED", "Endpoint is required", 400);
28
18
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/app-router/admin/ternsecureNextjsHandler.ts"],"sourcesContent":["import { createTernSecureRequest } from '@tern-secure/backend';\n\nimport { createRequestProcessor } from './c-authenticateRequestProcessor';\nimport { TENANT_ID } from './constants';\nimport { EndpointRouter } from './endpointRouter';\nimport { createValidators } from './fnValidators';\nimport { createApiErrorResponse } from './responses';\nimport type { TernSecureHandlerOptions } from './types';\nimport { DEFAULT_HANDLER_OPTIONS } from './types';\nimport { ConfigUtils } from './utils';\n\nfunction createHandlerConfig(options?: TernSecureHandlerOptions): TernSecureHandlerOptions {\n const baseConfig: Required<TernSecureHandlerOptions> = ConfigUtils.mergeWithDefaults(\n DEFAULT_HANDLER_OPTIONS,\n options,\n );\n\n return {\n ...baseConfig,\n tenantId: TENANT_ID,\n };\n}\n\nexport function createTernSecureNextJsHandler(options: TernSecureHandlerOptions) {\n const config = createHandlerConfig(options);\n\n const handler = async (request: Request): Promise<Response> => {\n try {\n const context = createRequestProcessor(createTernSecureRequest(request), options);\n\n const { validateSecurity } = createValidators(context);\n await validateSecurity(options.security || {});\n\n if (!context.endpoint) {\n return createApiErrorResponse('ENDPOINT_REQUIRED', 'Endpoint is required', 400);\n }\n\n return await EndpointRouter.route(context, config);\n } catch (error) {\n return createApiErrorResponse('INTERNAL_SERVER_ERROR', 'Internal server error', 500);\n }\n };\n\n return {\n GET: handler,\n POST: handler,\n } as const;\n}\n"],"mappings":"AAAA,SAAS,+BAA+B;AAExC,SAAS,8BAA8B;AACvC,SAAS,iBAAiB;AAC1B,SAAS,sBAAsB;AAC/B,SAAS,wBAAwB;AACjC,SAAS,8BAA8B;AAEvC,SAAS,+BAA+B;AACxC,SAAS,mBAAmB;AAE5B,SAAS,oBAAoB,SAA8D;AACzF,QAAM,aAAiD,YAAY;AAAA,IACjE;AAAA,IACA;AAAA,EACF;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,UAAU;AAAA,EACZ;AACF;AAEO,SAAS,8BAA8B,SAAmC;AAC/E,QAAM,SAAS,oBAAoB,OAAO;AAE1C,QAAM,UAAU,OAAO,YAAwC;AAC7D,QAAI;AACF,YAAM,UAAU,uBAAuB,wBAAwB,OAAO,GAAG,OAAO;AAEhF,YAAM,EAAE,iBAAiB,IAAI,iBAAiB,OAAO;AACrD,YAAM,iBAAiB,QAAQ,YAAY,CAAC,CAAC;AAE7C,UAAI,CAAC,QAAQ,UAAU;AACrB,eAAO,uBAAuB,qBAAqB,wBAAwB,GAAG;AAAA,MAChF;AAEA,aAAO,MAAM,eAAe,MAAM,SAAS,MAAM;AAAA,IACnD,SAAS,OAAO;AACd,aAAO,uBAAuB,yBAAyB,yBAAyB,GAAG;AAAA,IACrF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,KAAK;AAAA,IACL,MAAM;AAAA,EACR;AACF;","names":[]}
1
+ {"version":3,"sources":["../../../../src/app-router/admin/ternsecureNextjsHandler.ts"],"sourcesContent":["import type { AuthenticateRequestOptions } from '@tern-secure/backend';\nimport { createRequestProcessor, createTernSecureRequest } from '@tern-secure/backend';\n\nimport { TENANT_ID } from './constants';\nimport { EndpointRouter } from './endpointRouter';\nimport { createApiErrorResponse } from './responses';\nimport { DEFAULT_API_HANDLER_OPTIONS } from './types';\n\n/**\n * Create API route handlers with unified options\n * Uses the same AuthenticateRequestOptions as middleware\n */\nexport function createTernSecureNextJsHandler(options?: AuthenticateRequestOptions) {\n const config: AuthenticateRequestOptions = {\n ...DEFAULT_API_HANDLER_OPTIONS,\n ...options,\n tenantId: options?.tenantId || TENANT_ID,\n };\n\n const handler = async (request: Request): Promise<Response> => {\n try {\n \n const ternRequest = createTernSecureRequest(request);\n const context = createRequestProcessor(ternRequest, config);\n\n if (!context.endpoint) {\n return createApiErrorResponse('ENDPOINT_REQUIRED', 'Endpoint is required', 400);\n }\n\n return await EndpointRouter.route(context, config);\n } catch (error) {\n return createApiErrorResponse('INTERNAL_SERVER_ERROR', 'Internal server error', 500);\n }\n };\n\n return {\n GET: handler,\n POST: handler,\n } as const;\n}\n"],"mappings":"AACA,SAAS,wBAAwB,+BAA+B;AAEhE,SAAS,iBAAiB;AAC1B,SAAS,sBAAsB;AAC/B,SAAS,8BAA8B;AACvC,SAAS,mCAAmC;AAMrC,SAAS,8BAA8B,SAAsC;AAClF,QAAM,SAAqC;AAAA,IACzC,GAAG;AAAA,IACH,GAAG;AAAA,IACH,UAAU,SAAS,YAAY;AAAA,EACjC;AAEA,QAAM,UAAU,OAAO,YAAwC;AAC7D,QAAI;AAEF,YAAM,cAAc,wBAAwB,OAAO;AACnD,YAAM,UAAU,uBAAuB,aAAa,MAAM;AAE1D,UAAI,CAAC,QAAQ,UAAU;AACrB,eAAO,uBAAuB,qBAAqB,wBAAwB,GAAG;AAAA,MAChF;AAEA,aAAO,MAAM,eAAe,MAAM,SAAS,MAAM;AAAA,IACnD,SAAS,OAAO;AACd,aAAO,uBAAuB,yBAAyB,yBAAyB,GAAG;AAAA,IACrF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,KAAK;AAAA,IACL,MAAM;AAAA,EACR;AACF;","names":[]}
@@ -191,8 +191,16 @@ class CookieUtils {
191
191
  return maxAge >= minAge && maxAge <= maxAgeLimit;
192
192
  }
193
193
  }
194
+ const DEFAULT_API_HANDLER_OPTIONS = {
195
+ tenantId: void 0,
196
+ cookies: DEFAULT_SESSION_COOKIE_OPTIONS,
197
+ enableCustomToken: false,
198
+ debug: false,
199
+ revokeRefreshTokensOnSignOut: true
200
+ };
194
201
  export {
195
202
  CookieUtils,
203
+ DEFAULT_API_HANDLER_OPTIONS,
196
204
  DEFAULT_COOKIE_REQUEST_CONFIG,
197
205
  DEFAULT_CORS_OPTIONS,
198
206
  DEFAULT_ENDPOINT_CONFIG,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/app-router/admin/types.ts"],"sourcesContent":["import type {\n AuthEndpoint,\n CookieEndpointConfig,\n CookieOpts as CookieOptions,\n CorsOptions,\n EndpointConfig,\n SecurityOptions,\n SessionEndpointConfig,\n SessionSubEndpoint,\n SignInEndpointConfig,\n SignInSubEndpoint,\n TernSecureHandlerOptions,\n TokenCookieConfig\n} from '@tern-secure/types';\nimport { type NextResponse } from 'next/server';\n\nexport const DEFAULT_CORS_OPTIONS: CorsOptions = {\n allowedOrigins: [],\n allowedMethods: ['GET', 'POST'],\n allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'],\n allowCredentials: true,\n maxAge: 86400, // 24 hours\n};\n\nexport const DEFAULT_SESSION_COOKIE_OPTIONS: CookieOptions = {\n httpOnly: true,\n path: '/',\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'strict',\n maxAge: 12 * 60 * 60 * 24, // twelve days\n priority: 'high',\n};\n\nexport const DEFAULT_ID_REFRESH_TOKEN_COOKIE_OPTIONS: CookieOptions = {\n httpOnly: true,\n path: '/',\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'strict',\n maxAge: 12 * 60 * 60 * 24, // twelve days\n priority: 'high',\n};\n\n\nexport const FIXED_TOKEN_CONFIGS = {\n id: {\n path: '/',\n httpOnly: true,\n sameSite: 'lax' as const,\n maxAge: 3600, // 1 hour\n },\n refresh: {\n path: '/',\n httpOnly: true,\n sameSite: 'lax' as const,\n maxAge: 3600 * 24 * 30, // 30 days (changes when user events occur)\n },\n signature: {\n path: '/',\n httpOnly: true,\n sameSite: 'lax' as const,\n maxAge: 3600 * 24 * 7, // 1 week (as needed)\n },\n custom: {\n path: '/',\n httpOnly: true,\n sameSite: 'lax' as const,\n maxAge: 3600 * 24 * 7, // 1 week (as needed)\n },\n} as const;\n\nexport const DEFAULT_SECURITY_OPTIONS: SecurityOptions = {\n requireCSRF: true,\n allowedReferers: [],\n requiredHeaders: {},\n ipWhitelist: [],\n userAgent: {\n block: [],\n allow: [],\n },\n};\n\nexport const DEFAULT_ENDPOINT_CONFIG: EndpointConfig = {\n enabled: true,\n methods: ['GET', 'POST'],\n requireAuth: false,\n security: DEFAULT_SECURITY_OPTIONS,\n};\n\nexport const DEFAULT_COOKIE_REQUEST_CONFIG: CookieEndpointConfig = {\n ...DEFAULT_ENDPOINT_CONFIG,\n subEndpoints: {\n get: {\n enabled: true,\n methods: ['GET'],\n requireAuth: false,\n security: {\n requireCSRF: true,\n allowedReferers: [],\n },\n },\n },\n};\n\nexport const DEFAULT_SESSIONS_CONFIG: SessionEndpointConfig = {\n ...DEFAULT_ENDPOINT_CONFIG,\n subEndpoints: {\n verify: {\n enabled: true,\n methods: ['GET'],\n requireAuth: false,\n security: {\n requireCSRF: true,\n allowedReferers: [],\n },\n },\n createsession: {\n enabled: true,\n methods: ['POST'],\n requireAuth: false,\n security: {\n requireCSRF: true,\n },\n },\n refresh: {\n enabled: true,\n methods: ['POST'],\n requireAuth: true,\n security: {\n requireCSRF: true,\n },\n },\n revoke: {\n enabled: true,\n methods: ['POST'],\n requireAuth: true,\n security: {\n requireCSRF: true,\n },\n },\n },\n};\n\nexport const DEFAULT_SIGNINS_CONFIG: SignInEndpointConfig = {\n ...DEFAULT_ENDPOINT_CONFIG,\n subEndpoints: {\n resetPasswordEmail: {\n enabled: true,\n methods: ['POST'],\n requireAuth: false\n },\n },\n};\n\nexport const DEFAULT_HANDLER_OPTIONS: Required<TernSecureHandlerOptions> & {\n endpoints: Required<NonNullable<TernSecureHandlerOptions['endpoints']>>;\n} = {\n cors: DEFAULT_CORS_OPTIONS,\n cookies: DEFAULT_SESSION_COOKIE_OPTIONS,\n rateLimit: {\n windowMs: 15 * 60 * 1000, // 15 minutes\n maxRequests: 100,\n skipSuccessful: false,\n skipFailedRequests: false,\n },\n security: DEFAULT_SECURITY_OPTIONS,\n endpoints: {\n cookies: DEFAULT_COOKIE_REQUEST_CONFIG,\n sessions: DEFAULT_SESSIONS_CONFIG,\n signIns: DEFAULT_SIGNINS_CONFIG,\n },\n tenantId: '',\n revokeRefreshTokensOnSignOut: true,\n enableCustomToken: false,\n debug: false,\n environment: 'production',\n basePath: '/api/auth',\n};\n\nexport interface ValidationResult {\n error?: NextResponse;\n data?: any;\n}\n\nexport interface ValidationConfig {\n cors?: CorsOptions;\n security?: SecurityOptions;\n endpoint?: {\n name: AuthEndpoint;\n config: EndpointConfig;\n };\n subEndpoint?: {\n name: SessionSubEndpoint;\n config: EndpointConfig;\n };\n requireIdToken?: boolean;\n requireCsrfToken?: boolean;\n}\n\nexport interface ComprehensiveValidationResult {\n isValid: boolean;\n error?: Response;\n corsResponse?: Response;\n sessionData?: {\n body: any;\n idToken?: string;\n csrfToken?: string;\n };\n}\n\nexport type suffix = 'session' | 'id' | 'refresh' | 'signature' | 'custom';\n\nexport class CookieUtils {\n static getCookieName(namePrefix: string, tokenType: suffix): string {\n return `${namePrefix}.${tokenType}`;\n }\n\n static getCookieNames(namePrefix: string) {\n return {\n session: this.getCookieName(namePrefix, 'session'),\n id: this.getCookieName(namePrefix, 'id'),\n refresh: this.getCookieName(namePrefix, 'refresh'),\n signature: this.getCookieName(namePrefix, 'signature'),\n custom: this.getCookieName(namePrefix, 'custom'),\n };\n }\n\n static getSessionConfig(cookieOptions: CookieOptions): TokenCookieConfig {\n return {\n path: cookieOptions.path ?? '/',\n httpOnly: cookieOptions.httpOnly ?? true,\n sameSite: cookieOptions.sameSite ?? 'lax',\n maxAge: cookieOptions.maxAge ?? 3600 * 24 * 7,\n };\n }\n\n static getFixedTokenConfig(\n tokenType: Exclude<suffix, 'session'>,\n ): TokenCookieConfig {\n const fixedConfig = FIXED_TOKEN_CONFIGS[tokenType];\n\n return {\n path: fixedConfig.path,\n httpOnly: fixedConfig.httpOnly,\n sameSite: fixedConfig.sameSite,\n maxAge: fixedConfig.maxAge,\n };\n }\n\n static validateSessionMaxAge(maxAge: number): boolean {\n const minAge = 300; // 5 minutes\n const maxAgeLimit = 3600 * 24 * 14; // 2 weeks\n return maxAge >= minAge && maxAge <= maxAgeLimit;\n }\n}\n\nexport {\n AuthEndpoint,\n CookieOptions,\n CorsOptions,\n SecurityOptions,\n SessionSubEndpoint,\n EndpointConfig,\n SessionEndpointConfig,\n SignInEndpointConfig,\n SignInSubEndpoint,\n TernSecureHandlerOptions,\n};\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../src/app-router/admin/types.ts"],"sourcesContent":["import type { AuthenticateRequestOptions } from '@tern-secure/backend';\nimport type {\n AuthEndpoint,\n AuthSubEndpoint,\n CookieEndpointConfig,\n CookieOpts as CookieOptions,\n CorsOptions,\n EndpointConfig,\n SecurityOptions,\n SessionEndpointConfig,\n SessionSubEndpoint,\n SignInEndpointConfig,\n SignInSubEndpoint,\n TernSecureHandlerOptions,\n TokenCookieConfig\n} from '@tern-secure/types';\nimport { type NextResponse } from 'next/server';\n\n\n\nexport const DEFAULT_CORS_OPTIONS: CorsOptions = {\n allowedOrigins: [],\n allowedMethods: ['GET', 'POST'],\n allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'],\n allowCredentials: true,\n maxAge: 86400, // 24 hours\n};\n\nexport const DEFAULT_SESSION_COOKIE_OPTIONS: CookieOptions = {\n httpOnly: true,\n path: '/',\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'strict',\n maxAge: 12 * 60 * 60 * 24, // twelve days\n priority: 'high',\n};\n\nexport const DEFAULT_ID_REFRESH_TOKEN_COOKIE_OPTIONS: CookieOptions = {\n httpOnly: true,\n path: '/',\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'strict',\n maxAge: 12 * 60 * 60 * 24, // twelve days\n priority: 'high',\n};\n\n\nexport const FIXED_TOKEN_CONFIGS = {\n id: {\n path: '/',\n httpOnly: true,\n sameSite: 'lax' as const,\n maxAge: 3600, // 1 hour\n },\n refresh: {\n path: '/',\n httpOnly: true,\n sameSite: 'lax' as const,\n maxAge: 3600 * 24 * 30, // 30 days (changes when user events occur)\n },\n signature: {\n path: '/',\n httpOnly: true,\n sameSite: 'lax' as const,\n maxAge: 3600 * 24 * 7, // 1 week (as needed)\n },\n custom: {\n path: '/',\n httpOnly: true,\n sameSite: 'lax' as const,\n maxAge: 3600 * 24 * 7, // 1 week (as needed)\n },\n} as const;\n\nexport const DEFAULT_SECURITY_OPTIONS: SecurityOptions = {\n requireCSRF: true,\n allowedReferers: [],\n requiredHeaders: {},\n ipWhitelist: [],\n userAgent: {\n block: [],\n allow: [],\n },\n};\n\nexport const DEFAULT_ENDPOINT_CONFIG: EndpointConfig = {\n enabled: true,\n methods: ['GET', 'POST'],\n requireAuth: false,\n security: DEFAULT_SECURITY_OPTIONS,\n};\n\nexport const DEFAULT_COOKIE_REQUEST_CONFIG: CookieEndpointConfig = {\n ...DEFAULT_ENDPOINT_CONFIG,\n subEndpoints: {\n get: {\n enabled: true,\n methods: ['GET'],\n requireAuth: false,\n security: {\n requireCSRF: true,\n allowedReferers: [],\n },\n },\n },\n};\n\nexport const DEFAULT_SESSIONS_CONFIG: SessionEndpointConfig = {\n ...DEFAULT_ENDPOINT_CONFIG,\n subEndpoints: {\n verify: {\n enabled: true,\n methods: ['GET'],\n requireAuth: false,\n security: {\n requireCSRF: true,\n allowedReferers: [],\n },\n },\n createsession: {\n enabled: true,\n methods: ['POST'],\n requireAuth: false,\n security: {\n requireCSRF: true,\n },\n },\n refresh: {\n enabled: true,\n methods: ['POST'],\n requireAuth: true,\n security: {\n requireCSRF: true,\n },\n },\n revoke: {\n enabled: true,\n methods: ['POST'],\n requireAuth: true,\n security: {\n requireCSRF: true,\n },\n },\n },\n};\n\nexport const DEFAULT_SIGNINS_CONFIG: SignInEndpointConfig = {\n ...DEFAULT_ENDPOINT_CONFIG,\n subEndpoints: {\n resetPasswordEmail: {\n enabled: true,\n methods: ['POST'],\n requireAuth: false\n },\n },\n};\n\nexport const DEFAULT_HANDLER_OPTIONS: Required<TernSecureHandlerOptions> & {\n endpoints: Required<NonNullable<TernSecureHandlerOptions['endpoints']>>;\n} = {\n cors: DEFAULT_CORS_OPTIONS,\n cookies: DEFAULT_SESSION_COOKIE_OPTIONS,\n rateLimit: {\n windowMs: 15 * 60 * 1000, // 15 minutes\n maxRequests: 100,\n skipSuccessful: false,\n skipFailedRequests: false,\n },\n security: DEFAULT_SECURITY_OPTIONS,\n endpoints: {\n cookies: DEFAULT_COOKIE_REQUEST_CONFIG,\n sessions: DEFAULT_SESSIONS_CONFIG,\n signIns: DEFAULT_SIGNINS_CONFIG,\n },\n tenantId: '',\n revokeRefreshTokensOnSignOut: true,\n enableCustomToken: false,\n debug: false,\n environment: 'production',\n basePath: '/api/auth',\n};\n\nexport interface ValidationResult {\n error?: NextResponse;\n data?: any;\n}\n\nexport interface ValidationConfig {\n cors?: CorsOptions;\n security?: SecurityOptions;\n endpoint?: {\n name: AuthEndpoint;\n config: EndpointConfig;\n };\n subEndpoint?: {\n name: SessionSubEndpoint;\n config: EndpointConfig;\n };\n requireIdToken?: boolean;\n requireCsrfToken?: boolean;\n}\n\nexport interface ComprehensiveValidationResult {\n isValid: boolean;\n error?: Response;\n corsResponse?: Response;\n sessionData?: {\n body: any;\n idToken?: string;\n csrfToken?: string;\n };\n}\n\nexport type suffix = 'session' | 'id' | 'refresh' | 'signature' | 'custom';\n\nexport class CookieUtils {\n static getCookieName(namePrefix: string, tokenType: suffix): string {\n return `${namePrefix}.${tokenType}`;\n }\n\n static getCookieNames(namePrefix: string) {\n return {\n session: this.getCookieName(namePrefix, 'session'),\n id: this.getCookieName(namePrefix, 'id'),\n refresh: this.getCookieName(namePrefix, 'refresh'),\n signature: this.getCookieName(namePrefix, 'signature'),\n custom: this.getCookieName(namePrefix, 'custom'),\n };\n }\n\n static getSessionConfig(cookieOptions: CookieOptions): TokenCookieConfig {\n return {\n path: cookieOptions.path ?? '/',\n httpOnly: cookieOptions.httpOnly ?? true,\n sameSite: cookieOptions.sameSite ?? 'lax',\n maxAge: cookieOptions.maxAge ?? 3600 * 24 * 7,\n };\n }\n\n static getFixedTokenConfig(\n tokenType: Exclude<suffix, 'session'>,\n ): TokenCookieConfig {\n const fixedConfig = FIXED_TOKEN_CONFIGS[tokenType];\n\n return {\n path: fixedConfig.path,\n httpOnly: fixedConfig.httpOnly,\n sameSite: fixedConfig.sameSite,\n maxAge: fixedConfig.maxAge,\n };\n }\n\n static validateSessionMaxAge(maxAge: number): boolean {\n const minAge = 300; // 5 minutes\n const maxAgeLimit = 3600 * 24 * 14; // 2 weeks\n return maxAge >= minAge && maxAge <= maxAgeLimit;\n }\n}\n\n/**\n * API Handler Options - Unified with Middleware\n * \n * Since API routes are protected by middleware, they use the same\n * AuthenticateRequestOptions type. No separate config needed.\n * \n * Key fields used by API handlers:\n * - tenantId: Multi-tenant support\n * - enableCustomToken: Whether to create custom token cookie\n * - debug: Debug logging\n * - revokeRefreshTokensOnSignOut: Token revocation behavior\n * \n * Fields handled by middleware (not needed in API):\n * - firebaseConfig: Used for authentication\n * - firebaseAdminConfig: Server-side Firebase admin\n * - checkRevoked: Token revocation checking\n */\nexport type ApiHandlerOptions = AuthenticateRequestOptions & {\n cookies?: CookieOptions;\n};\n\nexport const DEFAULT_API_HANDLER_OPTIONS: Partial<ApiHandlerOptions> = {\n tenantId: undefined,\n cookies: DEFAULT_SESSION_COOKIE_OPTIONS,\n enableCustomToken: false,\n debug: false,\n revokeRefreshTokensOnSignOut: true,\n};\n\nexport {\n AuthEndpoint,\n AuthSubEndpoint,\n CookieOptions,\n CorsOptions,\n SecurityOptions,\n SessionSubEndpoint,\n EndpointConfig,\n SessionEndpointConfig,\n SignInEndpointConfig,\n SignInSubEndpoint,\n TernSecureHandlerOptions,\n};\n"],"mappings":"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","names":[]}
@@ -1,188 +1,110 @@
1
+ import { constants } from "@tern-secure/backend";
2
+ import { cookies } from "next/headers";
1
3
  import { createApiErrorResponse } from "./responses";
2
- class CorsValidator {
3
- static async validate(request, corsOptions) {
4
- const origin = request.headers.get("origin");
5
- const host = request.headers.get("host");
6
- if (!origin || host && origin.includes(host)) {
7
- return null;
8
- }
9
- if (corsOptions.allowedOrigins !== "*") {
10
- const isAllowed = corsOptions.allowedOrigins.some((allowedOrigin) => {
11
- if (allowedOrigin.startsWith("*")) {
12
- const domain = allowedOrigin.slice(1);
13
- return origin?.endsWith(domain);
14
- }
15
- return origin === allowedOrigin;
16
- });
17
- if (!isAllowed) {
18
- return createApiErrorResponse("CORS_ORIGIN_NOT_ALLOWED", "Origin not allowed", 403);
19
- }
20
- }
21
- return null;
4
+ async function validateJsonBody(request) {
5
+ try {
6
+ const body = await request.json();
7
+ return { body };
8
+ } catch (error) {
9
+ return {
10
+ body: null,
11
+ error: createApiErrorResponse("INVALID_REQUEST_FORMAT", "Invalid JSON in request body", 400)
12
+ };
22
13
  }
23
- static createOptionsResponse(corsOptions) {
24
- const response = new Response(null, { status: 204 });
25
- if (corsOptions.allowedOrigins === "*") {
26
- response.headers.set("Access-Control-Allow-Origin", "*");
27
- } else {
28
- response.headers.set("Access-Control-Allow-Origin", corsOptions.allowedOrigins.join(","));
29
- }
30
- response.headers.set(
31
- "Access-Control-Allow-Methods",
32
- corsOptions.allowedMethods?.join(",") || "GET,POST"
14
+ }
15
+ function validateIdToken(idToken) {
16
+ if (!idToken) {
17
+ return createApiErrorResponse(
18
+ "INVALID_TOKEN",
19
+ "ID token is required",
20
+ 400
33
21
  );
34
- response.headers.set(
35
- "Access-Control-Allow-Headers",
36
- corsOptions.allowedHeaders?.join(",") || "Content-Type,Authorization"
22
+ }
23
+ if (idToken.split(".").length !== 3) {
24
+ return createApiErrorResponse(
25
+ "INVALID_TOKEN_FORMAT",
26
+ "ID token must be a valid JWT",
27
+ 400
37
28
  );
38
- if (corsOptions.allowCredentials) {
39
- response.headers.set("Access-Control-Allow-Credentials", "true");
40
- }
41
- if (corsOptions.maxAge) {
42
- response.headers.set("Access-Control-Max-Age", corsOptions.maxAge.toString());
43
- }
44
- return response;
45
29
  }
30
+ return null;
46
31
  }
47
- class SecurityValidator {
48
- static async validate(request, securityOptions) {
49
- const origin = request.headers.get("origin");
50
- const host = request.headers.get("host");
51
- const referer = request.headers.get("referer");
52
- const userAgent = request.headers.get("user-agent") || "";
53
- const csrfResult = this.validateCsrf(request, securityOptions, origin, host, referer);
54
- if (csrfResult) return csrfResult;
55
- const headersResult = this.validateRequiredHeaders(request, securityOptions);
56
- if (headersResult) return headersResult;
57
- const userAgentResult = this.validateUserAgent(userAgent, securityOptions);
58
- if (userAgentResult) return userAgentResult;
59
- return null;
60
- }
61
- static validateCsrf(request, securityOptions, origin, host, referer) {
62
- if (securityOptions.requireCSRF && origin && host && !origin.includes(host)) {
63
- const hasCSRFHeader = request.headers.get("x-requested-with") === "XMLHttpRequest";
64
- const hasValidReferer = referer && host && referer.includes(host);
65
- if (!hasCSRFHeader && !hasValidReferer) {
66
- const isAllowedReferer = securityOptions.allowedReferers?.some(
67
- (allowedRef) => referer?.includes(allowedRef)
68
- );
69
- if (!isAllowedReferer) {
70
- return createApiErrorResponse("CSRF_PROTECTION", "Access denied", 403);
71
- }
72
- }
73
- }
74
- return null;
32
+ async function validateCsrfToken(csrfToken) {
33
+ if (!csrfToken) {
34
+ return createApiErrorResponse("INVALID_CSRF_TOKEN", "CSRF token is required", 400);
75
35
  }
76
- static validateRequiredHeaders(request, securityOptions) {
77
- if (securityOptions.requiredHeaders) {
78
- for (const [headerName, expectedValue] of Object.entries(securityOptions.requiredHeaders)) {
79
- const actualValue = request.headers.get(headerName);
80
- if (actualValue !== expectedValue) {
81
- return createApiErrorResponse(
82
- "INVALID_HEADERS",
83
- "Required header missing or invalid",
84
- 400
85
- );
86
- }
87
- }
88
- }
89
- return null;
36
+ const cookieStore = await cookies();
37
+ const csrfCookieValue = cookieStore.get(constants.Cookies.CsrfToken)?.value;
38
+ if (!csrfCookieValue) {
39
+ return createApiErrorResponse("CSRF_MISSING", "CSRF token cookie not found", 403);
90
40
  }
91
- static validateUserAgent(userAgent, securityOptions) {
92
- if (securityOptions.userAgent?.block?.length) {
93
- const isBlocked = securityOptions.userAgent.block.some(
94
- (blocked) => userAgent.toLowerCase().includes(blocked.toLowerCase())
95
- );
96
- if (isBlocked) {
97
- return createApiErrorResponse("USER_AGENT_BLOCKED", "Access denied", 403);
98
- }
99
- }
100
- if (securityOptions.userAgent?.allow?.length) {
101
- const isAllowed = securityOptions.userAgent.allow.some(
102
- (allowed) => userAgent.toLowerCase().includes(allowed.toLowerCase())
103
- );
104
- if (!isAllowed) {
105
- return createApiErrorResponse("USER_AGENT_NOT_ALLOWED", "Access denied", 403);
106
- }
107
- }
108
- return null;
41
+ if (csrfToken !== csrfCookieValue) {
42
+ return createApiErrorResponse("CSRF_TOKEN_MISMATCH", "CSRF token mismatch", 403);
109
43
  }
44
+ return null;
110
45
  }
111
- class CsrfValidator {
112
- static validate(csrfToken, csrfCookieValue) {
113
- if (!csrfToken) {
114
- return createApiErrorResponse("INVALID_CSRF_TOKEN", "CSRF token is required", 400);
115
- }
116
- if (!csrfCookieValue) {
117
- return createApiErrorResponse("CSRF_COOKIE_MISSING", "CSRF token cookie not found", 403);
118
- }
119
- if (csrfToken !== csrfCookieValue) {
120
- return createApiErrorResponse("CSRF_TOKEN_MISMATCH", "CSRF token mismatch", 403);
121
- }
122
- return null;
46
+ function validateEmail(email) {
47
+ if (!email || typeof email !== "string") {
48
+ return createApiErrorResponse("EMAIL_REQUIRED", "Email is required", 400);
123
49
  }
50
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
51
+ if (!emailRegex.test(email)) {
52
+ return createApiErrorResponse("INVALID_EMAIL", "Invalid email format", 400);
53
+ }
54
+ return null;
124
55
  }
125
- class RouteValidator {
126
- static validatePathStructure(pathSegments) {
127
- if (pathSegments.length < 3) {
128
- return createApiErrorResponse(
129
- "INVALID_ROUTE",
130
- "Invalid route structure. Expected: /api/auth/{endpoint}",
131
- 404
132
- );
133
- }
134
- return null;
56
+ function validatePassword(password) {
57
+ if (!password || typeof password !== "string") {
58
+ return createApiErrorResponse("PASSWORD_REQUIRED", "Password is required", 400);
135
59
  }
136
- static validateEndpoint(_endpoint, endpointConfig, method) {
137
- if (!endpointConfig || !endpointConfig.enabled) {
138
- return createApiErrorResponse("ENDPOINT_NOT_FOUND", "Endpoint not found", 404);
139
- }
140
- if (method !== "OPTIONS" && !endpointConfig.methods.includes(method)) {
141
- return createApiErrorResponse("METHOD_NOT_ALLOWED", "Method not allowed", 405);
142
- }
143
- return null;
60
+ if (password.length < 6) {
61
+ return createApiErrorResponse(
62
+ "PASSWORD_TOO_SHORT",
63
+ "Password must be at least 6 characters",
64
+ 400
65
+ );
144
66
  }
145
- static validateSubEndpoint(subEndpoint, subEndpointConfig, method) {
146
- if (!subEndpoint) {
147
- return createApiErrorResponse("SUB_ENDPOINT_REQUIRED", "Session sub-endpoint required", 400);
148
- }
149
- if (!subEndpointConfig || !subEndpointConfig.enabled) {
150
- return createApiErrorResponse("ENDPOINT_NOT_FOUND", "Endpoint not found", 404);
151
- }
152
- if (!subEndpointConfig.methods?.includes(method)) {
153
- return createApiErrorResponse("METHOD_NOT_ALLOWED", "Method not allowed", 405);
154
- }
155
- return null;
67
+ return null;
68
+ }
69
+ function validateRequiredFields(body, fields) {
70
+ const missingFields = fields.filter((field) => !body[field]);
71
+ if (missingFields.length > 0) {
72
+ return createApiErrorResponse(
73
+ "MISSING_REQUIRED_FIELDS",
74
+ `Missing required fields: ${missingFields.join(", ")}`,
75
+ 400
76
+ );
156
77
  }
78
+ return null;
157
79
  }
158
- class RequestValidator {
159
- static async validateSessionRequest(request) {
160
- try {
161
- const body = await request.json();
162
- return { body, idToken: body.idToken, csrfToken: body.csrfToken };
163
- } catch (error) {
164
- return {
165
- body: null,
166
- error: createApiErrorResponse("INVALID_REQUEST_FORMAT", "Invalid request format", 400)
167
- };
168
- }
80
+ function validateSubEndpointPresent(context, endpointType) {
81
+ if (!context.subEndpoint) {
82
+ return createApiErrorResponse(
83
+ "SUB_ENDPOINT_REQUIRED",
84
+ `${endpointType} sub-endpoint required`,
85
+ 400
86
+ );
169
87
  }
170
- static validateIdToken(idToken) {
171
- if (!idToken) {
172
- return createApiErrorResponse(
173
- "INVALID_TOKEN",
174
- "ID token is required for creating session",
175
- 400
176
- );
177
- }
178
- return null;
88
+ return null;
89
+ }
90
+ async function extractSessionRequestData(request) {
91
+ const { body, error } = await validateJsonBody(request);
92
+ if (error) {
93
+ return { error };
179
94
  }
95
+ return {
96
+ idToken: body.idToken,
97
+ csrfToken: body.csrfToken
98
+ };
180
99
  }
181
100
  export {
182
- CorsValidator,
183
- CsrfValidator,
184
- RequestValidator,
185
- RouteValidator,
186
- SecurityValidator
101
+ extractSessionRequestData,
102
+ validateCsrfToken,
103
+ validateEmail,
104
+ validateIdToken,
105
+ validateJsonBody,
106
+ validatePassword,
107
+ validateRequiredFields,
108
+ validateSubEndpointPresent
187
109
  };
188
110
  //# sourceMappingURL=validators.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/app-router/admin/validators.ts"],"sourcesContent":["import { createApiErrorResponse } from './responses';\nimport type { AuthEndpoint, CorsOptions, SecurityOptions, SessionSubEndpoint } from './types';\n\n/**\n * CORS validation utilities\n */\nexport class CorsValidator {\n static async validate(\n request: Request,\n corsOptions: CorsOptions,\n ): Promise<Response | null> {\n const origin = request.headers.get('origin');\n const host = request.headers.get('host');\n\n // Skip CORS for same-origin requests\n if (!origin || (host && origin.includes(host))) {\n return null;\n }\n\n if (corsOptions.allowedOrigins !== '*') {\n const isAllowed = corsOptions.allowedOrigins.some(allowedOrigin => {\n if (allowedOrigin.startsWith('*')) {\n const domain = allowedOrigin.slice(1);\n return origin?.endsWith(domain);\n }\n return origin === allowedOrigin;\n });\n\n if (!isAllowed) {\n return createApiErrorResponse('CORS_ORIGIN_NOT_ALLOWED', 'Origin not allowed', 403);\n }\n }\n\n return null;\n }\n\n static createOptionsResponse(corsOptions: CorsOptions): Response {\n const response = new Response(null, { status: 204 });\n\n if (corsOptions.allowedOrigins === '*') {\n response.headers.set('Access-Control-Allow-Origin', '*');\n } else {\n response.headers.set('Access-Control-Allow-Origin', corsOptions.allowedOrigins.join(','));\n }\n\n response.headers.set(\n 'Access-Control-Allow-Methods',\n corsOptions.allowedMethods?.join(',') || 'GET,POST',\n );\n response.headers.set(\n 'Access-Control-Allow-Headers',\n corsOptions.allowedHeaders?.join(',') || 'Content-Type,Authorization',\n );\n\n if (corsOptions.allowCredentials) {\n response.headers.set('Access-Control-Allow-Credentials', 'true');\n }\n\n if (corsOptions.maxAge) {\n response.headers.set('Access-Control-Max-Age', corsOptions.maxAge.toString());\n }\n\n return response;\n }\n}\n\n/**\n * Security validation utilities\n */\nexport class SecurityValidator {\n static async validate(\n request: Request,\n securityOptions: SecurityOptions,\n ): Promise<Response | null> {\n const origin = request.headers.get('origin');\n const host = request.headers.get('host');\n const referer = request.headers.get('referer');\n const userAgent = request.headers.get('user-agent') || '';\n\n // CSRF Protection for cross-origin requests\n const csrfResult = this.validateCsrf(request, securityOptions, origin, host, referer);\n if (csrfResult) return csrfResult;\n\n // Required headers validation\n const headersResult = this.validateRequiredHeaders(request, securityOptions);\n if (headersResult) return headersResult;\n\n // User Agent filtering\n const userAgentResult = this.validateUserAgent(userAgent, securityOptions);\n if (userAgentResult) return userAgentResult;\n\n return null;\n }\n\n private static validateCsrf(\n request: Request,\n securityOptions: SecurityOptions,\n origin: string | null,\n host: string | null,\n referer: string | null,\n ): Response | null {\n if (securityOptions.requireCSRF && origin && host && !origin.includes(host)) {\n const hasCSRFHeader = request.headers.get('x-requested-with') === 'XMLHttpRequest';\n const hasValidReferer = referer && host && referer.includes(host);\n\n if (!hasCSRFHeader && !hasValidReferer) {\n const isAllowedReferer = securityOptions.allowedReferers?.some((allowedRef: string) =>\n referer?.includes(allowedRef),\n );\n\n if (!isAllowedReferer) {\n return createApiErrorResponse('CSRF_PROTECTION', 'Access denied', 403);\n }\n }\n }\n return null;\n }\n\n private static validateRequiredHeaders(\n request: Request,\n securityOptions: SecurityOptions,\n ): Response | null {\n if (securityOptions.requiredHeaders) {\n for (const [headerName, expectedValue] of Object.entries(securityOptions.requiredHeaders)) {\n const actualValue = request.headers.get(headerName);\n if (actualValue !== expectedValue) {\n return createApiErrorResponse(\n 'INVALID_HEADERS',\n 'Required header missing or invalid',\n 400,\n );\n }\n }\n }\n return null;\n }\n\n private static validateUserAgent(\n userAgent: string,\n securityOptions: SecurityOptions,\n ): Response | null {\n // User Agent blocking\n if (securityOptions.userAgent?.block?.length) {\n const isBlocked = securityOptions.userAgent.block.some((blocked: string) =>\n userAgent.toLowerCase().includes(blocked.toLowerCase()),\n );\n\n if (isBlocked) {\n return createApiErrorResponse('USER_AGENT_BLOCKED', 'Access denied', 403);\n }\n }\n\n // User Agent allowlist\n if (securityOptions.userAgent?.allow?.length) {\n const isAllowed = securityOptions.userAgent.allow.some((allowed: string) =>\n userAgent.toLowerCase().includes(allowed.toLowerCase()),\n );\n\n if (!isAllowed) {\n return createApiErrorResponse('USER_AGENT_NOT_ALLOWED', 'Access denied', 403);\n }\n }\n\n return null;\n }\n}\n\n/**\n * CSRF token validation utilities\n */\nexport class CsrfValidator {\n static validate(csrfToken: string, csrfCookieValue: string | undefined): Response | null {\n if (!csrfToken) {\n return createApiErrorResponse('INVALID_CSRF_TOKEN', 'CSRF token is required', 400);\n }\n\n if (!csrfCookieValue) {\n return createApiErrorResponse('CSRF_COOKIE_MISSING', 'CSRF token cookie not found', 403);\n }\n\n if (csrfToken !== csrfCookieValue) {\n return createApiErrorResponse('CSRF_TOKEN_MISMATCH', 'CSRF token mismatch', 403);\n }\n\n return null;\n }\n}\n\n/**\n * Route validation utilities\n */\nexport class RouteValidator {\n static validatePathStructure(pathSegments: string[]): Response | null {\n if (pathSegments.length < 3) {\n return createApiErrorResponse(\n 'INVALID_ROUTE',\n 'Invalid route structure. Expected: /api/auth/{endpoint}',\n 404,\n );\n }\n return null;\n }\n\n static validateEndpoint(\n _endpoint: AuthEndpoint,\n endpointConfig: any,\n method: string,\n ): Response | null {\n if (!endpointConfig || !endpointConfig.enabled) {\n return createApiErrorResponse('ENDPOINT_NOT_FOUND', 'Endpoint not found', 404);\n }\n\n if (method !== 'OPTIONS' && !endpointConfig.methods.includes(method as any)) {\n return createApiErrorResponse('METHOD_NOT_ALLOWED', 'Method not allowed', 405);\n }\n\n return null;\n }\n\n static validateSubEndpoint(\n subEndpoint: SessionSubEndpoint | undefined,\n subEndpointConfig: any,\n method: string,\n ): Response | null {\n if (!subEndpoint) {\n return createApiErrorResponse('SUB_ENDPOINT_REQUIRED', 'Session sub-endpoint required', 400);\n }\n\n if (!subEndpointConfig || !subEndpointConfig.enabled) {\n return createApiErrorResponse('ENDPOINT_NOT_FOUND', 'Endpoint not found', 404);\n }\n\n if (!subEndpointConfig.methods?.includes(method as any)) {\n return createApiErrorResponse('METHOD_NOT_ALLOWED', 'Method not allowed', 405);\n }\n\n return null;\n }\n}\n\n/**\n * Request body validation utilities\n */\nexport class RequestValidator {\n static async validateSessionRequest(request: Request): Promise<{\n body: any;\n idToken?: string;\n csrfToken?: string;\n error?: Response;\n }> {\n try {\n const body = await request.json();\n return { body, idToken: body.idToken, csrfToken: body.csrfToken };\n } catch (error) {\n return {\n body: null,\n error: createApiErrorResponse('INVALID_REQUEST_FORMAT', 'Invalid request format', 400),\n };\n }\n }\n\n static validateIdToken(idToken: string | undefined): Response | null {\n if (!idToken) {\n return createApiErrorResponse(\n 'INVALID_TOKEN',\n 'ID token is required for creating session',\n 400,\n );\n }\n return null;\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../src/app-router/admin/validators.ts"],"sourcesContent":["import type { RequestProcessorContext } from '@tern-secure/backend';\nimport { constants } from '@tern-secure/backend'\nimport { cookies } from 'next/headers';\n\nimport { createApiErrorResponse } from './responses';\n\n/**\n * Lightweight validators for API route handlers\n * Note: Middleware already handles CORS, security, and CSRF validation\n * These validators only handle endpoint-specific validation\n */\n\n/**\n * Validates that the request body is valid JSON\n */\nexport async function validateJsonBody(request: Request): Promise<{\n body: any;\n error?: Response;\n}> {\n try {\n const body = await request.json();\n return { body };\n } catch (error) {\n return {\n body: null,\n error: createApiErrorResponse('INVALID_REQUEST_FORMAT', 'Invalid JSON in request body', 400),\n };\n }\n}\n\n/**\n * Validates that an ID token is present and has correct JWT structure\n */\nexport function validateIdToken(idToken: string | undefined): Response | null {\n if (!idToken) {\n return createApiErrorResponse(\n 'INVALID_TOKEN',\n 'ID token is required',\n 400,\n );\n }\n \n if (idToken.split('.').length !== 3) {\n return createApiErrorResponse(\n 'INVALID_TOKEN_FORMAT',\n 'ID token must be a valid JWT',\n 400,\n );\n }\n \n return null;\n}\n\n/**\n * Validates CSRF token matches the cookie value\n * Note: This is only used for specific endpoints that need double-submit CSRF\n */\nexport async function validateCsrfToken(\n csrfToken: string | undefined,\n): Promise<Response | null> {\n if (!csrfToken) {\n return createApiErrorResponse('INVALID_CSRF_TOKEN', 'CSRF token is required', 400);\n }\n\n const cookieStore = await cookies();\n const csrfCookieValue = cookieStore.get(constants.Cookies.CsrfToken)?.value;\n\n if (!csrfCookieValue) {\n return createApiErrorResponse('CSRF_MISSING', 'CSRF token cookie not found', 403);\n }\n\n if (csrfToken !== csrfCookieValue) {\n return createApiErrorResponse('CSRF_TOKEN_MISMATCH', 'CSRF token mismatch', 403);\n }\n\n return null;\n}\n\n/**\n * Validates email format (basic validation)\n */\nexport function validateEmail(email: string | undefined): Response | null {\n if (!email || typeof email !== 'string') {\n return createApiErrorResponse('EMAIL_REQUIRED', 'Email is required', 400);\n }\n\n // Basic email validation\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n if (!emailRegex.test(email)) {\n return createApiErrorResponse('INVALID_EMAIL', 'Invalid email format', 400);\n }\n\n return null;\n}\n\n/**\n * Validates password meets minimum requirements\n */\nexport function validatePassword(password: string | undefined): Response | null {\n if (!password || typeof password !== 'string') {\n return createApiErrorResponse('PASSWORD_REQUIRED', 'Password is required', 400);\n }\n\n if (password.length < 6) {\n return createApiErrorResponse(\n 'PASSWORD_TOO_SHORT',\n 'Password must be at least 6 characters',\n 400,\n );\n }\n\n return null;\n}\n\n/**\n * Validates required fields are present in request body\n */\nexport function validateRequiredFields(\n body: any,\n fields: string[],\n): Response | null {\n const missingFields = fields.filter(field => !body[field]);\n \n if (missingFields.length > 0) {\n return createApiErrorResponse(\n 'MISSING_REQUIRED_FIELDS',\n `Missing required fields: ${missingFields.join(', ')}`,\n 400,\n );\n }\n\n return null;\n}\n\n/**\n * Validates that a sub-endpoint exists in the URL\n */\nexport function validateSubEndpointPresent(\n context: RequestProcessorContext,\n endpointType: string,\n): Response | null {\n if (!context.subEndpoint) {\n return createApiErrorResponse(\n 'SUB_ENDPOINT_REQUIRED',\n `${endpointType} sub-endpoint required`,\n 400,\n );\n }\n \n return null;\n}\n\n/**\n * Helper to extract and validate session request data\n */\nexport async function extractSessionRequestData(request: Request): Promise<{\n idToken?: string;\n csrfToken?: string;\n error?: Response;\n}> {\n const { body, error } = await validateJsonBody(request);\n \n if (error) {\n return { error };\n }\n\n return {\n idToken: body.idToken,\n csrfToken: body.csrfToken,\n };\n}\n"],"mappings":"AACA,SAAS,iBAAiB;AAC1B,SAAS,eAAe;AAExB,SAAS,8BAA8B;AAWvC,eAAsB,iBAAiB,SAGpC;AACD,MAAI;AACF,UAAM,OAAO,MAAM,QAAQ,KAAK;AAChC,WAAO,EAAE,KAAK;AAAA,EAChB,SAAS,OAAO;AACd,WAAO;AAAA,MACL,MAAM;AAAA,MACN,OAAO,uBAAuB,0BAA0B,gCAAgC,GAAG;AAAA,IAC7F;AAAA,EACF;AACF;AAKO,SAAS,gBAAgB,SAA8C;AAC5E,MAAI,CAAC,SAAS;AACZ,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,MAAI,QAAQ,MAAM,GAAG,EAAE,WAAW,GAAG;AACnC,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAMA,eAAsB,kBACpB,WAC0B;AAC1B,MAAI,CAAC,WAAW;AACd,WAAO,uBAAuB,sBAAsB,0BAA0B,GAAG;AAAA,EACnF;AAEA,QAAM,cAAc,MAAM,QAAQ;AAClC,QAAM,kBAAkB,YAAY,IAAI,UAAU,QAAQ,SAAS,GAAG;AAEtE,MAAI,CAAC,iBAAiB;AACpB,WAAO,uBAAuB,gBAAgB,+BAA+B,GAAG;AAAA,EAClF;AAEA,MAAI,cAAc,iBAAiB;AACjC,WAAO,uBAAuB,uBAAuB,uBAAuB,GAAG;AAAA,EACjF;AAEA,SAAO;AACT;AAKO,SAAS,cAAc,OAA4C;AACxE,MAAI,CAAC,SAAS,OAAO,UAAU,UAAU;AACvC,WAAO,uBAAuB,kBAAkB,qBAAqB,GAAG;AAAA,EAC1E;AAGA,QAAM,aAAa;AACnB,MAAI,CAAC,WAAW,KAAK,KAAK,GAAG;AAC3B,WAAO,uBAAuB,iBAAiB,wBAAwB,GAAG;AAAA,EAC5E;AAEA,SAAO;AACT;AAKO,SAAS,iBAAiB,UAA+C;AAC9E,MAAI,CAAC,YAAY,OAAO,aAAa,UAAU;AAC7C,WAAO,uBAAuB,qBAAqB,wBAAwB,GAAG;AAAA,EAChF;AAEA,MAAI,SAAS,SAAS,GAAG;AACvB,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAKO,SAAS,uBACd,MACA,QACiB;AACjB,QAAM,gBAAgB,OAAO,OAAO,WAAS,CAAC,KAAK,KAAK,CAAC;AAEzD,MAAI,cAAc,SAAS,GAAG;AAC5B,WAAO;AAAA,MACL;AAAA,MACA,4BAA4B,cAAc,KAAK,IAAI,CAAC;AAAA,MACpD;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAKO,SAAS,2BACd,SACA,cACiB;AACjB,MAAI,CAAC,QAAQ,aAAa;AACxB,WAAO;AAAA,MACL;AAAA,MACA,GAAG,YAAY;AAAA,MACf;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAKA,eAAsB,0BAA0B,SAI7C;AACD,QAAM,EAAE,MAAM,MAAM,IAAI,MAAM,iBAAiB,OAAO;AAEtD,MAAI,OAAO;AACT,WAAO,EAAE,MAAM;AAAA,EACjB;AAEA,SAAO;AAAA,IACL,SAAS,KAAK;AAAA,IACd,WAAW,KAAK;AAAA,EAClB;AACF;","names":[]}