node-server-engine 1.13.0 → 2.0.0

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 (35) hide show
  1. package/README.md +1 -0
  2. package/dist/entities/ElasticSearch/ElasticSearch.js +2 -1
  3. package/dist/entities/ElasticSearch/ElasticSearch.js.map +1 -1
  4. package/dist/entities/PubSub/PubSub.js +13 -6
  5. package/dist/entities/PubSub/PubSub.js.map +1 -1
  6. package/dist/entities/Sequelize/Sequelize.js +21 -1
  7. package/dist/entities/Sequelize/Sequelize.js.map +1 -1
  8. package/dist/entities/Server/Server.d.ts +2 -0
  9. package/dist/entities/Server/Server.js +15 -1
  10. package/dist/entities/Server/Server.js.map +1 -1
  11. package/dist/index.js +2 -1
  12. package/dist/index.js.map +1 -1
  13. package/dist/middleware/authJwt/authJwt.js +3 -5
  14. package/dist/middleware/authJwt/authJwt.js.map +1 -1
  15. package/dist/middleware/authStatic/authStatic.js +12 -8
  16. package/dist/middleware/authStatic/authStatic.js.map +1 -1
  17. package/dist/middleware/authTls/authTls.js +2 -1
  18. package/dist/middleware/authTls/authTls.js.map +1 -1
  19. package/dist/middleware/multiPartFileUploader/multiPartFileUploader.js +50 -3
  20. package/dist/middleware/multiPartFileUploader/multiPartFileUploader.js.map +1 -1
  21. package/dist/utils/dbMigration/dbMigration.js +2 -1
  22. package/dist/utils/dbMigration/dbMigration.js.map +1 -1
  23. package/dist/utils/geminiFileUpload/geminiFileUpload.d.ts +10 -1
  24. package/dist/utils/geminiFileUpload/geminiFileUpload.js +58 -46
  25. package/dist/utils/geminiFileUpload/geminiFileUpload.js.map +1 -1
  26. package/dist/utils/jwt/jwt.js +0 -3
  27. package/dist/utils/jwt/jwt.js.map +1 -1
  28. package/dist/utils/report/output.d.ts +2 -0
  29. package/dist/utils/report/output.js +27 -0
  30. package/dist/utils/report/output.js.map +1 -1
  31. package/dist/utils/sendEmail/sendEmail.js +5 -4
  32. package/dist/utils/sendEmail/sendEmail.js.map +1 -1
  33. package/dist/utils/verificationToken/verificationToken.js +16 -9
  34. package/dist/utils/verificationToken/verificationToken.js.map +1 -1
  35. package/package.json +59 -51
@@ -1,57 +1,82 @@
1
1
  "use strict";
2
2
 
3
- var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
3
  Object.defineProperty(exports, "__esModule", {
5
4
  value: true
6
5
  });
6
+ exports.GEMINI_POLL_INTERVAL_MS = void 0;
7
7
  exports.geminiFileUpload = geminiFileUpload;
8
- var _server = require("@google/generative-ai/server");
9
- var _fs = _interopRequireDefault(require("fs"));
10
- var _path = _interopRequireDefault(require("path"));
8
+ exports.getGeminiClient = getGeminiClient;
9
+ var _genai = require("@google/genai");
11
10
  var _promises = require("timers/promises");
12
- var _tmp = _interopRequireDefault(require("tmp"));
13
11
  var _report = require("../report");
14
12
  const namespace = 'engine:utils:geminiFileUpload';
15
- const fileManager = new _server.GoogleAIFileManager(process.env.GOOGLE_AI_KEY || '');
16
- async function geminiFileUpload(buffer, mimeType, originalName) {
17
- let tempFile;
18
- try {
19
- // 1. Create Temp File
20
- tempFile = _tmp.default.fileSync({
21
- postfix: _path.default.extname(originalName)
13
+
14
+ /** Delay between two checks of the file processing state */
15
+ const GEMINI_POLL_INTERVAL_MS = exports.GEMINI_POLL_INTERVAL_MS = 10_000;
16
+ /** Maximum time to wait for Gemini to process an uploaded file */
17
+ const GEMINI_PROCESSING_TIMEOUT_MS = Number(process.env.GEMINI_PROCESSING_TIMEOUT_MS) || 10 * 60 * 1000;
18
+ let client;
19
+ let clientKey;
20
+
21
+ /**
22
+ * Get the Gemini client
23
+ * The client is created lazily so that GOOGLE_AI_KEY can be loaded at startup (e.g. by the Secret Manager)
24
+ */
25
+ function getGeminiClient() {
26
+ const apiKey = process.env.GOOGLE_AI_KEY ?? '';
27
+ if (!client || clientKey !== apiKey) {
28
+ client = new _genai.GoogleGenAI({
29
+ apiKey
22
30
  });
23
- _fs.default.writeFileSync(tempFile.name, buffer);
31
+ clientKey = apiKey;
32
+ }
33
+ return client;
34
+ }
35
+
36
+ /** Upload a file to Gemini and wait until it is ready to be used in prompts */
37
+ async function geminiFileUpload(buffer, mimeType, originalName, pollIntervalMs = GEMINI_POLL_INTERVAL_MS) {
38
+ try {
39
+ const {
40
+ files
41
+ } = getGeminiClient();
24
42
 
25
- // 2. Upload File
26
- const uploadResult = await fileManager.uploadFile(tempFile.name, {
27
- mimeType,
28
- displayName: originalName
43
+ // 1. Upload the file straight from memory
44
+ const file = await files.upload({
45
+ file: new Blob([new Uint8Array(buffer)], {
46
+ type: mimeType
47
+ }),
48
+ config: {
49
+ mimeType,
50
+ displayName: originalName
51
+ }
29
52
  });
30
- if (!uploadResult || !uploadResult.file) {
53
+ if (!file?.name) {
31
54
  throw new Error('Upload failed: No file returned');
32
55
  }
33
- const file = uploadResult.file;
34
56
  (0, _report.reportDebug)({
35
57
  namespace,
36
- message: 'Uploaded file successfully',
58
+ message: 'Uploaded file successfully, waiting for file processing',
37
59
  data: {
38
60
  displayName: file.displayName,
39
61
  name: file.name
40
62
  }
41
63
  });
42
- (0, _report.reportDebug)({
43
- namespace,
44
- message: 'Waiting for file processing...'
45
- });
46
64
 
47
- // 3. Poll Until File is Processed
48
- let geminiFile = await fileManager.getFile(file.name);
49
- while (geminiFile?.state === 'PROCESSING') {
50
- process.stdout.write('.');
51
- await (0, _promises.setTimeout)(10000);
52
- geminiFile = await fileManager.getFile(file.name);
65
+ // 2. Poll until the file is processed, with an upper bound on the wait
66
+ const deadline = Date.now() + GEMINI_PROCESSING_TIMEOUT_MS;
67
+ let geminiFile = await files.get({
68
+ name: file.name
69
+ });
70
+ while (geminiFile?.state === _genai.FileState.PROCESSING) {
71
+ if (Date.now() > deadline) {
72
+ throw new Error(`Timed out waiting for Gemini to process ${file.name}`);
73
+ }
74
+ await (0, _promises.setTimeout)(pollIntervalMs);
75
+ geminiFile = await files.get({
76
+ name: file.name
77
+ });
53
78
  }
54
- if (!geminiFile || geminiFile.state !== 'ACTIVE') {
79
+ if (geminiFile?.state !== _genai.FileState.ACTIVE) {
55
80
  const errorMessage = `File processing failed for ${file.name}`;
56
81
  (0, _report.reportError)(errorMessage);
57
82
  return {
@@ -66,8 +91,8 @@ async function geminiFileUpload(buffer, mimeType, originalName) {
66
91
  return {
67
92
  success: true,
68
93
  originalname: file.name,
69
- fileUri: file.uri,
70
- mimeType: file.mimeType
94
+ fileUri: geminiFile.uri ?? file.uri ?? '',
95
+ mimeType: geminiFile.mimeType ?? file.mimeType ?? mimeType
71
96
  };
72
97
  } catch (error) {
73
98
  (0, _report.reportError)({
@@ -79,19 +104,6 @@ async function geminiFileUpload(buffer, mimeType, originalName) {
79
104
  success: false,
80
105
  error
81
106
  };
82
- } finally {
83
- // Ensure cleanup of temp file
84
- if (tempFile) {
85
- try {
86
- tempFile.removeCallback();
87
- } catch (cleanupError) {
88
- (0, _report.reportError)({
89
- namespace,
90
- message: 'Error cleaning up temp file',
91
- error: cleanupError
92
- });
93
- }
94
- }
95
107
  }
96
108
  }
97
109
  //# sourceMappingURL=geminiFileUpload.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"geminiFileUpload.js","names":["_server","require","_fs","_interopRequireDefault","_path","_promises","_tmp","_report","namespace","fileManager","GoogleAIFileManager","process","env","GOOGLE_AI_KEY","geminiFileUpload","buffer","mimeType","originalName","tempFile","tmp","fileSync","postfix","path","extname","fs","writeFileSync","name","uploadResult","uploadFile","displayName","file","Error","reportDebug","message","data","geminiFile","getFile","state","stdout","write","setTimeoutPromise","errorMessage","reportError","success","error","originalname","fileUri","uri","removeCallback","cleanupError"],"sources":["../../../src/utils/geminiFileUpload/geminiFileUpload.ts"],"sourcesContent":["import { GoogleAIFileManager } from '@google/generative-ai/server';\nimport fs from 'fs';\nimport path from 'path';\nimport { setTimeout as setTimeoutPromise } from 'timers/promises';\nimport tmp from 'tmp';\nimport { reportError, reportDebug } from 'utils/report';\n\nconst namespace = 'engine:utils:geminiFileUpload';\n\nconst fileManager = new GoogleAIFileManager(process.env.GOOGLE_AI_KEY || '');\n\nexport async function geminiFileUpload(\n buffer: Buffer,\n mimeType: string,\n originalName: string\n): Promise<\n | { success: true; originalname: string; fileUri: string; mimeType: string }\n | { success: false; error: unknown }\n> {\n let tempFile;\n try {\n // 1. Create Temp File\n tempFile = tmp.fileSync({ postfix: path.extname(originalName) });\n fs.writeFileSync(tempFile.name, buffer);\n\n // 2. Upload File\n const uploadResult = await fileManager.uploadFile(tempFile.name, {\n mimeType,\n displayName: originalName\n });\n\n if (!uploadResult || !uploadResult.file) {\n throw new Error('Upload failed: No file returned');\n }\n\n const file = uploadResult.file;\n reportDebug({\n namespace,\n message: 'Uploaded file successfully',\n data: { displayName: file.displayName, name: file.name }\n });\n\n reportDebug({\n namespace,\n message: 'Waiting for file processing...'\n });\n\n // 3. Poll Until File is Processed\n let geminiFile = await fileManager.getFile(file.name);\n while (geminiFile?.state === 'PROCESSING') {\n process.stdout.write('.');\n await setTimeoutPromise(10000);\n geminiFile = await fileManager.getFile(file.name);\n }\n\n if (!geminiFile || geminiFile.state !== 'ACTIVE') {\n const errorMessage = `File processing failed for ${file.name}`;\n reportError(errorMessage);\n return { success: false, error: new Error(errorMessage) };\n }\n\n reportDebug({\n namespace,\n message: 'File is ready'\n });\n\n return {\n success: true,\n originalname: file.name,\n fileUri: file.uri,\n mimeType: file.mimeType\n };\n } catch (error) {\n reportError({\n namespace,\n message: 'Error during Gemini file upload and processing',\n error\n });\n return { success: false, error };\n } finally {\n // Ensure cleanup of temp file\n if (tempFile) {\n try {\n tempFile.removeCallback();\n } catch (cleanupError) {\n reportError({\n namespace,\n message: 'Error cleaning up temp file',\n error: cleanupError\n });\n }\n }\n }\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"geminiFileUpload.js","names":["_genai","require","_promises","_report","namespace","GEMINI_POLL_INTERVAL_MS","exports","GEMINI_PROCESSING_TIMEOUT_MS","Number","process","env","client","clientKey","getGeminiClient","apiKey","GOOGLE_AI_KEY","GoogleGenAI","geminiFileUpload","buffer","mimeType","originalName","pollIntervalMs","files","file","upload","Blob","Uint8Array","type","config","displayName","name","Error","reportDebug","message","data","deadline","Date","now","geminiFile","get","state","FileState","PROCESSING","setTimeoutPromise","ACTIVE","errorMessage","reportError","success","error","originalname","fileUri","uri"],"sources":["../../../src/utils/geminiFileUpload/geminiFileUpload.ts"],"sourcesContent":["import { GoogleGenAI, File as GeminiFile, FileState } from '@google/genai';\nimport { setTimeout as setTimeoutPromise } from 'timers/promises';\nimport { reportError, reportDebug } from 'utils/report';\n\nconst namespace = 'engine:utils:geminiFileUpload';\n\n/** Delay between two checks of the file processing state */\nexport const GEMINI_POLL_INTERVAL_MS = 10_000;\n/** Maximum time to wait for Gemini to process an uploaded file */\nconst GEMINI_PROCESSING_TIMEOUT_MS =\n Number(process.env.GEMINI_PROCESSING_TIMEOUT_MS) || 10 * 60 * 1000;\n\nlet client: GoogleGenAI | undefined;\nlet clientKey: string | undefined;\n\n/**\n * Get the Gemini client\n * The client is created lazily so that GOOGLE_AI_KEY can be loaded at startup (e.g. by the Secret Manager)\n */\nexport function getGeminiClient(): GoogleGenAI {\n const apiKey = process.env.GOOGLE_AI_KEY ?? '';\n if (!client || clientKey !== apiKey) {\n client = new GoogleGenAI({ apiKey });\n clientKey = apiKey;\n }\n return client;\n}\n\n/** Upload a file to Gemini and wait until it is ready to be used in prompts */\nexport async function geminiFileUpload(\n buffer: Buffer,\n mimeType: string,\n originalName: string,\n pollIntervalMs: number = GEMINI_POLL_INTERVAL_MS\n): Promise<\n | { success: true; originalname: string; fileUri: string; mimeType: string }\n | { success: false; error: unknown }\n> {\n try {\n const { files } = getGeminiClient();\n\n // 1. Upload the file straight from memory\n const file = await files.upload({\n file: new Blob([new Uint8Array(buffer)], { type: mimeType }),\n config: { mimeType, displayName: originalName }\n });\n\n if (!file?.name) {\n throw new Error('Upload failed: No file returned');\n }\n\n reportDebug({\n namespace,\n message: 'Uploaded file successfully, waiting for file processing',\n data: { displayName: file.displayName, name: file.name }\n });\n\n // 2. Poll until the file is processed, with an upper bound on the wait\n const deadline = Date.now() + GEMINI_PROCESSING_TIMEOUT_MS;\n let geminiFile: GeminiFile | undefined = await files.get({\n name: file.name\n });\n while (geminiFile?.state === FileState.PROCESSING) {\n if (Date.now() > deadline) {\n throw new Error(`Timed out waiting for Gemini to process ${file.name}`);\n }\n await setTimeoutPromise(pollIntervalMs);\n geminiFile = await files.get({ name: file.name });\n }\n\n if (geminiFile?.state !== FileState.ACTIVE) {\n const errorMessage = `File processing failed for ${file.name}`;\n reportError(errorMessage);\n return { success: false, error: new Error(errorMessage) };\n }\n\n reportDebug({ namespace, message: 'File is ready' });\n\n return {\n success: true,\n originalname: file.name,\n fileUri: geminiFile.uri ?? file.uri ?? '',\n mimeType: geminiFile.mimeType ?? file.mimeType ?? mimeType\n };\n } catch (error) {\n reportError({\n namespace,\n message: 'Error during Gemini file upload and processing',\n error\n });\n return { success: false, error };\n }\n}\n"],"mappings":"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","ignoreList":[]}
@@ -150,9 +150,6 @@ async function jwtVerify(token, issuer = _const.TokenIssuer.AUTH_SERVICE) {
150
150
  errorCode: 'unauthorized',
151
151
  statusCode: 401,
152
152
  message: 'Invalid token supplied',
153
- data: {
154
- token
155
- },
156
153
  error: error instanceof Error ? error : undefined
157
154
  }));
158
155
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"jwt.js","names":["_jsonwebtoken","require","_fs","_interopRequireDefault","_const","_EngineError","_KeySet","_WebError","_checkEnvironment","_envAssert","_report","keySet","exports","namespace","initKeySets","process","env","CHART","ECDSA_PUBLIC_KEY","EngineError","message","KeySet","file","AUTH_SERVICE_URL","internal","jwksPath","JWKS_PATH","promise","init","bind","Promise","all","shutdownKeySets","shutdown","jwtGetKey","header","callback","NODE_ENV","JWT_SECRET","kid","data","reportDebug","key","getKey","error","Error","WebError","statusCode","errorCode","jwtVerify","token","issuer","TokenIssuer","AUTH_SERVICE","algorithms","assertEnvironment","envAssert","isString","ACCESS_TOKEN_AUDIENCE","ACCESS_TOKEN_ISSUER","ACCESS_TOKEN_EXPIRATION_TIME","options","audience","resolve","reject","verify","result","undefined","generateJwtToken","user","signOptions","algorithm","expiresIn","parseInt","keyid","Object","keys","getKeys","privateKey","PRIVATE_KEY","PRIVATE_KEY_PATH","fs","readFileSync","String","sign"],"sources":["../../../src/utils/jwt/jwt.ts"],"sourcesContent":["import {\n verify,\n JwtHeader,\n SigningKeyCallback,\n Algorithm,\n VerifyOptions,\n sign\n} from 'jsonwebtoken';\nimport fs from 'fs';\nimport { UserTokenPayload } from './jwt.types';\nimport { TokenIssuer } from 'const';\nimport { EngineError } from 'entities/EngineError';\nimport { KeySet } from 'entities/KeySet';\nimport { WebError } from 'entities/WebError';\nimport { assertEnvironment } from 'utils/checkEnvironment';\nimport { envAssert } from 'utils/envAssert';\nimport { reportDebug } from 'utils/report';\n\nexport let keySet: KeySet | undefined;\n\nconst namespace = 'engine:utils:jwt';\n\n/** Initiate key fetching mechanism */\nexport async function initKeySets(): Promise<void> {\n // Create the KeySet instance\n // The auth-service fetches its key from its disk and not from a network request\n // This prevents fetch failures the first time the auth-service is started\n if (process.env.CHART === 'auth-service') {\n if (!process.env.ECDSA_PUBLIC_KEY)\n throw new EngineError({\n message: 'Environment variable \"ECDSA_PUBLIC_KEY\" is not defined'\n });\n keySet = new KeySet(process.env.ECDSA_PUBLIC_KEY, { file: true });\n } else {\n if (process.env.AUTH_SERVICE_URL)\n keySet = new KeySet(\n `${process.env.AUTH_SERVICE_URL}/.well-known/jwks.json`,\n { internal: true }\n );\n else {\n // Use JWKS_PATH if set (e.g., by Secret Manager), otherwise fall back to local path\n const jwksPath = process.env.JWKS_PATH || './keys/jwks.json';\n keySet = new KeySet(jwksPath, { internal: true, file: true });\n }\n\n if (!keySet)\n throw new EngineError({\n message:\n 'Environment variable \"AUTH_SERVICE_URL\" is not defined or JWKS not found'\n });\n }\n const promise = [keySet.init.bind(keySet)()];\n\n await Promise.all(promise);\n}\n\n/** Stop fetching keys regularly */\nexport function shutdownKeySets(): void {\n if (keySet) keySet.shutdown.bind(keySet)();\n}\n\n/** Get the JWT public key */\nfunction jwtGetKey(header: JwtHeader, callback: SigningKeyCallback): void {\n // In test environment we return the symmetric signature secret\n if (process.env.NODE_ENV === 'test') {\n callback(null, process.env.JWT_SECRET);\n return;\n }\n // Check that KeySet has been initialized\n if (!keySet) {\n callback(new EngineError({ message: 'KeySet has not been initialized' }));\n return;\n }\n // Check that the token has a key ID\n if (!header.kid) {\n callback(\n new EngineError({\n message:\n 'Token has not key ID in header, impossible to match with known key',\n data: header\n })\n );\n return;\n }\n\n // In test environment we use a symmetrical secret bases signature\n if (process.env.NODE_ENV === 'test') {\n callback(null, process.env.JWT_SECRET);\n return;\n }\n reportDebug({\n namespace,\n message: `Looking for key with ID \"${header.kid}\"`\n });\n // Go through each key set to check if the token's key is in one of them\n let key;\n try {\n key = keySet.getKey(header.kid);\n } catch (error: Error | null | unknown) {\n callback(error instanceof Error ? error : null);\n return;\n }\n\n if (key) {\n reportDebug({ namespace, message: `Found key with ID \"${header.kid}\"` });\n callback(null, key);\n return;\n }\n callback(\n new WebError({\n statusCode: 401,\n errorCode: 'unauthorized',\n message: 'Could not find the tokens key in any key set',\n data: { kid: header.kid }\n })\n );\n}\n\n/** Verify an user Json Web Token */\nexport async function jwtVerify(\n token: string,\n issuer: TokenIssuer = TokenIssuer.AUTH_SERVICE\n): Promise<UserTokenPayload> {\n let algorithms = ['ES256' as Algorithm];\n if (process.env.NODE_ENV === 'test') {\n assertEnvironment({ JWT_SECRET: envAssert.isString() });\n algorithms = ['HS256' as Algorithm];\n }\n assertEnvironment({\n ACCESS_TOKEN_AUDIENCE: envAssert.isString(),\n ACCESS_TOKEN_ISSUER: envAssert.isString(),\n ACCESS_TOKEN_EXPIRATION_TIME: envAssert.isString()\n });\n let options: VerifyOptions | undefined;\n\n switch (issuer) {\n case TokenIssuer.AUTH_SERVICE:\n options = {\n audience: [process.env.ACCESS_TOKEN_AUDIENCE as string],\n issuer: [process.env.ACCESS_TOKEN_ISSUER as string],\n algorithms\n };\n break;\n default:\n throw new EngineError({\n message: `Invalid token issuer specified \"${issuer as string}\"`\n });\n }\n\n return new Promise<UserTokenPayload>((resolve, reject) => {\n verify(token, jwtGetKey, options, (error, result) => {\n if (error) {\n reject(\n new WebError({\n errorCode: 'unauthorized',\n statusCode: 401,\n message: 'Invalid token supplied',\n data: { token },\n error: error instanceof Error ? error : undefined\n })\n );\n return;\n }\n resolve(result as UserTokenPayload);\n });\n });\n}\n\nexport function generateJwtToken(user: unknown) {\n const signOptions = {\n algorithm: 'ES256' as const, // or 'ES256' as Algorithm\n expiresIn: process.env.ACCESS_TOKEN_EXPIRATION_TIME\n ? parseInt(process.env.ACCESS_TOKEN_EXPIRATION_TIME, 10)\n : 3600,\n issuer: process.env.ACCESS_TOKEN_ISSUER,\n audience: process.env.ACCESS_TOKEN_AUDIENCE,\n keyid: Object.keys(keySet?.getKeys() ?? {})[0]\n };\n\n // Get private key - can be either the key content directly or a file path\n let privateKey: string;\n \n if (process.env.PRIVATE_KEY) {\n // Direct key content in environment variable\n privateKey = process.env.PRIVATE_KEY;\n } else if (process.env.PRIVATE_KEY_PATH) {\n // File path in environment variable - read the file\n try {\n privateKey = fs.readFileSync(process.env.PRIVATE_KEY_PATH, 'utf8');\n } catch (error) {\n throw new EngineError({\n message: `Failed to read private key from path: ${process.env.PRIVATE_KEY_PATH}`,\n data: { error: error instanceof Error ? error.message : String(error) }\n });\n }\n } else {\n throw new EngineError({ \n message: 'Neither PRIVATE_KEY nor PRIVATE_KEY_PATH environment variable is set' \n });\n }\n\n const token = sign({ user }, privateKey, signOptions);\n\n return token;\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"jwt.js","names":["_jsonwebtoken","require","_fs","_interopRequireDefault","_const","_EngineError","_KeySet","_WebError","_checkEnvironment","_envAssert","_report","keySet","exports","namespace","initKeySets","process","env","CHART","ECDSA_PUBLIC_KEY","EngineError","message","KeySet","file","AUTH_SERVICE_URL","internal","jwksPath","JWKS_PATH","promise","init","bind","Promise","all","shutdownKeySets","shutdown","jwtGetKey","header","callback","NODE_ENV","JWT_SECRET","kid","data","reportDebug","key","getKey","error","Error","WebError","statusCode","errorCode","jwtVerify","token","issuer","TokenIssuer","AUTH_SERVICE","algorithms","assertEnvironment","envAssert","isString","ACCESS_TOKEN_AUDIENCE","ACCESS_TOKEN_ISSUER","ACCESS_TOKEN_EXPIRATION_TIME","options","audience","resolve","reject","verify","result","undefined","generateJwtToken","user","signOptions","algorithm","expiresIn","parseInt","keyid","Object","keys","getKeys","privateKey","PRIVATE_KEY","PRIVATE_KEY_PATH","fs","readFileSync","String","sign"],"sources":["../../../src/utils/jwt/jwt.ts"],"sourcesContent":["import {\n verify,\n JwtHeader,\n SigningKeyCallback,\n Algorithm,\n VerifyOptions,\n sign\n} from 'jsonwebtoken';\nimport fs from 'fs';\nimport { UserTokenPayload } from './jwt.types';\nimport { TokenIssuer } from 'const';\nimport { EngineError } from 'entities/EngineError';\nimport { KeySet } from 'entities/KeySet';\nimport { WebError } from 'entities/WebError';\nimport { assertEnvironment } from 'utils/checkEnvironment';\nimport { envAssert } from 'utils/envAssert';\nimport { reportDebug } from 'utils/report';\n\nexport let keySet: KeySet | undefined;\n\nconst namespace = 'engine:utils:jwt';\n\n/** Initiate key fetching mechanism */\nexport async function initKeySets(): Promise<void> {\n // Create the KeySet instance\n // The auth-service fetches its key from its disk and not from a network request\n // This prevents fetch failures the first time the auth-service is started\n if (process.env.CHART === 'auth-service') {\n if (!process.env.ECDSA_PUBLIC_KEY)\n throw new EngineError({\n message: 'Environment variable \"ECDSA_PUBLIC_KEY\" is not defined'\n });\n keySet = new KeySet(process.env.ECDSA_PUBLIC_KEY, { file: true });\n } else {\n if (process.env.AUTH_SERVICE_URL)\n keySet = new KeySet(\n `${process.env.AUTH_SERVICE_URL}/.well-known/jwks.json`,\n { internal: true }\n );\n else {\n // Use JWKS_PATH if set (e.g., by Secret Manager), otherwise fall back to local path\n const jwksPath = process.env.JWKS_PATH || './keys/jwks.json';\n keySet = new KeySet(jwksPath, { internal: true, file: true });\n }\n\n if (!keySet)\n throw new EngineError({\n message:\n 'Environment variable \"AUTH_SERVICE_URL\" is not defined or JWKS not found'\n });\n }\n const promise = [keySet.init.bind(keySet)()];\n\n await Promise.all(promise);\n}\n\n/** Stop fetching keys regularly */\nexport function shutdownKeySets(): void {\n if (keySet) keySet.shutdown.bind(keySet)();\n}\n\n/** Get the JWT public key */\nfunction jwtGetKey(header: JwtHeader, callback: SigningKeyCallback): void {\n // In test environment we return the symmetric signature secret\n if (process.env.NODE_ENV === 'test') {\n callback(null, process.env.JWT_SECRET);\n return;\n }\n // Check that KeySet has been initialized\n if (!keySet) {\n callback(new EngineError({ message: 'KeySet has not been initialized' }));\n return;\n }\n // Check that the token has a key ID\n if (!header.kid) {\n callback(\n new EngineError({\n message:\n 'Token has not key ID in header, impossible to match with known key',\n data: header\n })\n );\n return;\n }\n\n // In test environment we use a symmetrical secret bases signature\n if (process.env.NODE_ENV === 'test') {\n callback(null, process.env.JWT_SECRET);\n return;\n }\n reportDebug({\n namespace,\n message: `Looking for key with ID \"${header.kid}\"`\n });\n // Go through each key set to check if the token's key is in one of them\n let key;\n try {\n key = keySet.getKey(header.kid);\n } catch (error: Error | null | unknown) {\n callback(error instanceof Error ? error : null);\n return;\n }\n\n if (key) {\n reportDebug({ namespace, message: `Found key with ID \"${header.kid}\"` });\n callback(null, key);\n return;\n }\n callback(\n new WebError({\n statusCode: 401,\n errorCode: 'unauthorized',\n message: 'Could not find the tokens key in any key set',\n data: { kid: header.kid }\n })\n );\n}\n\n/** Verify an user Json Web Token */\nexport async function jwtVerify(\n token: string,\n issuer: TokenIssuer = TokenIssuer.AUTH_SERVICE\n): Promise<UserTokenPayload> {\n let algorithms = ['ES256' as Algorithm];\n if (process.env.NODE_ENV === 'test') {\n assertEnvironment({ JWT_SECRET: envAssert.isString() });\n algorithms = ['HS256' as Algorithm];\n }\n assertEnvironment({\n ACCESS_TOKEN_AUDIENCE: envAssert.isString(),\n ACCESS_TOKEN_ISSUER: envAssert.isString(),\n ACCESS_TOKEN_EXPIRATION_TIME: envAssert.isString()\n });\n let options: VerifyOptions | undefined;\n\n switch (issuer) {\n case TokenIssuer.AUTH_SERVICE:\n options = {\n audience: [process.env.ACCESS_TOKEN_AUDIENCE as string],\n issuer: [process.env.ACCESS_TOKEN_ISSUER as string],\n algorithms\n };\n break;\n default:\n throw new EngineError({\n message: `Invalid token issuer specified \"${issuer as string}\"`\n });\n }\n\n return new Promise<UserTokenPayload>((resolve, reject) => {\n verify(token, jwtGetKey, options, (error, result) => {\n if (error) {\n reject(\n new WebError({\n errorCode: 'unauthorized',\n statusCode: 401,\n message: 'Invalid token supplied',\n error: error instanceof Error ? error : undefined\n })\n );\n return;\n }\n resolve(result as UserTokenPayload);\n });\n });\n}\n\nexport function generateJwtToken(user: unknown) {\n const signOptions = {\n algorithm: 'ES256' as const, // or 'ES256' as Algorithm\n expiresIn: process.env.ACCESS_TOKEN_EXPIRATION_TIME\n ? parseInt(process.env.ACCESS_TOKEN_EXPIRATION_TIME, 10)\n : 3600,\n issuer: process.env.ACCESS_TOKEN_ISSUER,\n audience: process.env.ACCESS_TOKEN_AUDIENCE,\n keyid: Object.keys(keySet?.getKeys() ?? {})[0]\n };\n\n // Get private key - can be either the key content directly or a file path\n let privateKey: string;\n \n if (process.env.PRIVATE_KEY) {\n // Direct key content in environment variable\n privateKey = process.env.PRIVATE_KEY;\n } else if (process.env.PRIVATE_KEY_PATH) {\n // File path in environment variable - read the file\n try {\n privateKey = fs.readFileSync(process.env.PRIVATE_KEY_PATH, 'utf8');\n } catch (error) {\n throw new EngineError({\n message: `Failed to read private key from path: ${process.env.PRIVATE_KEY_PATH}`,\n data: { error: error instanceof Error ? error.message : String(error) }\n });\n }\n } else {\n throw new EngineError({ \n message: 'Neither PRIVATE_KEY nor PRIVATE_KEY_PATH environment variable is set' \n });\n }\n\n const token = sign({ user }, privateKey, signOptions);\n\n return token;\n}\n"],"mappings":"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","ignoreList":[]}
@@ -10,5 +10,7 @@ export declare function formatTime(date?: Date): string;
10
10
  */
11
11
  export declare function formatLocalLog(severity: LogSeverity | string, message: string): void;
12
12
  export declare function formatLocalLog(entry: FullLogEntry): string;
13
+ /** Returns a copy of the value with sensitive fields masked */
14
+ export declare function redactSensitive(value: unknown, depth?: number, seen?: WeakSet<object>): unknown;
13
15
  /** Add metadata to the log entry and print it */
14
16
  export declare function output(data: LogEntry): FullLogEntry;
@@ -8,6 +8,7 @@ exports.INSTANCE_ID = void 0;
8
8
  exports.formatLocalLog = formatLocalLog;
9
9
  exports.formatTime = formatTime;
10
10
  exports.output = output;
11
+ exports.redactSensitive = redactSensitive;
11
12
  var _crypto = require("crypto");
12
13
  var _callsite = _interopRequireDefault(require("callsite"));
13
14
  var _jsonStringifySafe = _interopRequireDefault(require("json-stringify-safe"));
@@ -136,8 +137,34 @@ function isLocalDevelopment() {
136
137
  return process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'dev' || !process.env.NODE_ENV && !process.env.KUBERNETES_POD_NAME && !process.env.CLOUD_RUN_JOB;
137
138
  }
138
139
 
140
+ /** Keys whose values must never be written to the logs */
141
+ const SENSITIVE_KEY_PATTERN = /^(authorization|proxy[-_]?authorization|cookie|set[-_]?cookie|password|passwd|pass|secret|client[-_]?secret|token|access[-_]?token|refresh[-_]?token|id[-_]?token|api[-_]?key|private[-_]?key|credentials?|otp)$/i;
142
+ const REDACTED = '[REDACTED]';
143
+ const MAX_REDACT_DEPTH = 10;
144
+
145
+ /** Returns a copy of the value with sensitive fields masked */
146
+ function redactSensitive(value, depth = 0, seen = new WeakSet()) {
147
+ if (!value || typeof value !== 'object' || depth > MAX_REDACT_DEPTH) return value;
148
+ if (seen.has(value)) return value;
149
+ seen.add(value);
150
+ if (Array.isArray(value)) return value.map(item => redactSensitive(item, depth + 1, seen));
151
+ // Only walk plain objects, leave class instances (Errors, Buffers, Dates...) untouched
152
+ const prototype = Object.getPrototypeOf(value);
153
+ if (prototype !== Object.prototype && prototype !== null) return value;
154
+ const result = {};
155
+ for (const [key, entry] of Object.entries(value)) {
156
+ result[key] = SENSITIVE_KEY_PATTERN.test(key) ? REDACTED : redactSensitive(entry, depth + 1, seen);
157
+ }
158
+ return result;
159
+ }
160
+
139
161
  /** Add metadata to the log entry and print it */
140
162
  function output(data) {
163
+ if (data.data !== undefined) data = {
164
+ ...data,
165
+ data: redactSensitive(data.data)
166
+ };
167
+
141
168
  // Gather useful information about where the logger was called from
142
169
  const call = (0, _callsite.default)()[2];
143
170
 
@@ -1 +1 @@
1
- {"version":3,"file":"output.js","names":["_crypto","require","_callsite","_interopRequireDefault","_jsonStringifySafe","_const","INSTANCE_ID","exports","randomUUID","colors","reset","bright","dim","red","green","yellow","blue","magenta","cyan","white","gray","getSeverityColor","severity","LogSeverity","DEBUG","INFO","WARNING","ERROR","CRITICAL","formatTime","date","now","Date","toTimeString","split","formatLocalLog","severityOrEntry","message","undefined","time","severityColor","useColors","process","env","NODE_ENV","output","padEnd","console","log","entry","showDetailedLogs","DETAILED_LOGS","parts","push","join","errorType","errorCode","statusCode","errorParts","statusColor","length","context","httpRequest","req","method","url","stack_trace","data","Object","keys","JSON","stringify","fileName","lineNumber","isLocalDevelopment","LOG_FORMAT","KUBERNETES_POD_NAME","CLOUD_RUN_JOB","call","callsite","fullEntry","getLineNumber","getFileName","replace","serviceContext","service","CHART","HOSTNAME","version","BUILD_ID","environment","instance","SILENCE_REPORT","dir","depth","formattedLog","error"],"sources":["../../../src/utils/report/output.ts"],"sourcesContent":["import { randomUUID } from 'crypto';\nimport callsite from 'callsite';\nimport stringify from 'json-stringify-safe';\nimport { LogEntry, FullLogEntry } from './output.types';\nimport { LogSeverity } from 'const';\n\n// NOTE: Server instance id (helpful for distributed logs)\nexport const INSTANCE_ID = randomUUID();\n\n// ANSI color codes for terminal output\nconst colors = {\n reset: '\\x1b[0m',\n bright: '\\x1b[1m',\n dim: '\\x1b[2m',\n red: '\\x1b[31m',\n green: '\\x1b[32m',\n yellow: '\\x1b[33m',\n blue: '\\x1b[34m',\n magenta: '\\x1b[35m',\n cyan: '\\x1b[36m',\n white: '\\x1b[37m',\n gray: '\\x1b[90m'\n};\n\n/**\n * Get color for log severity level\n */\nfunction getSeverityColor(severity?: string): string {\n switch (severity) {\n case LogSeverity.DEBUG:\n return colors.cyan;\n case LogSeverity.INFO:\n return colors.green;\n case LogSeverity.WARNING:\n return colors.yellow;\n case LogSeverity.ERROR:\n return colors.red;\n case LogSeverity.CRITICAL:\n return `${colors.bright}${colors.red}`;\n default:\n return colors.white;\n }\n}\n\n/**\n * Format timestamp as HH:MM:SS\n */\nexport function formatTime(date?: Date): string {\n const now = date || new Date();\n return now.toTimeString().split(' ')[0];\n}\n\n/**\n * Format log entry for human-readable local development output\n */\nexport function formatLocalLog(severity: LogSeverity | string, message: string): void;\nexport function formatLocalLog(entry: FullLogEntry): string;\nexport function formatLocalLog(severityOrEntry: LogSeverity | string | FullLogEntry, message?: string): string | void {\n // Handle overloaded signature for testing\n if (typeof severityOrEntry === 'string' && message !== undefined) {\n const time = formatTime();\n const severityColor = getSeverityColor(severityOrEntry);\n const useColors = process.env.NODE_ENV !== 'production';\n \n const output = useColors \n ? `${colors.gray}[${time}]${colors.reset} ${severityColor}${severityOrEntry.padEnd(8)}${colors.reset} ${message}`\n : `[${time}] ${severityOrEntry.padEnd(8)} ${message}`;\n \n console.log(output);\n return;\n }\n \n // Original function implementation\n const entry = severityOrEntry as FullLogEntry;\n const time = formatTime();\n const severity = entry.severity || 'INFO';\n const severityColor = getSeverityColor(severity);\n const showDetailedLogs = process.env.DETAILED_LOGS === 'true';\n \n const parts: string[] = [];\n \n // Timestamp and severity\n parts.push(\n `${colors.gray}[${time}]${colors.reset} ${severityColor}${severity.padEnd(8)}${colors.reset}`\n );\n \n // Message\n if (entry.message) {\n parts.push(`${entry.message}`);\n }\n \n let output = parts.join(' ');\n \n // Error details (compact format)\n if (entry.errorType || entry.errorCode || entry.statusCode) {\n const errorParts: string[] = [];\n if (entry.errorCode) errorParts.push(`${colors.red}${entry.errorCode}${colors.reset}`);\n if (entry.statusCode) {\n const statusColor = entry.statusCode >= 500 ? colors.red : entry.statusCode >= 400 ? colors.yellow : colors.green;\n errorParts.push(`${statusColor}${entry.statusCode}${colors.reset}`);\n }\n if (errorParts.length > 0) {\n output += ` ${colors.dim}[${errorParts.join(' ')}]${colors.reset}`;\n }\n }\n \n // HTTP request context (compact)\n if (entry.context?.httpRequest) {\n const req = entry.context.httpRequest;\n output += ` ${colors.dim}${req.method} ${req.url}${colors.reset}`;\n }\n \n // Stack trace (only in detailed mode)\n if (showDetailedLogs && entry.stack_trace) {\n output += `\\n${colors.gray}Stack Trace:${colors.reset}\\n${colors.dim}${entry.stack_trace}${colors.reset}`;\n }\n \n // Additional data (only in detailed mode or if no message)\n if (showDetailedLogs && entry.data && Object.keys(entry.data).length > 0) {\n output += `\\n${colors.cyan}Data:${colors.reset}\\n${colors.dim}${JSON.stringify(entry.data, null, 2)}${colors.reset}`;\n }\n \n // File location (only in detailed mode)\n if (showDetailedLogs && entry.fileName) {\n output += `\\n ${colors.dim}${entry.fileName}:${entry.lineNumber || '?'}${colors.reset}`;\n }\n \n return output;\n}\n\n/**\n * Check if running in local development environment\n */\nfunction isLocalDevelopment(): boolean {\n // Allow forcing local format via environment variable\n if (process.env.LOG_FORMAT === 'local') return true;\n if (process.env.LOG_FORMAT === 'json') return false;\n \n return (\n process.env.NODE_ENV === 'development' ||\n process.env.NODE_ENV === 'dev' ||\n (!process.env.NODE_ENV && !process.env.KUBERNETES_POD_NAME && !process.env.CLOUD_RUN_JOB)\n );\n}\n\n/** Add metadata to the log entry and print it */\nexport function output(data: LogEntry): FullLogEntry {\n // Gather useful information about where the logger was called from\n const call = callsite()[2];\n\n // Append additional metadata to the log entry\n const fullEntry: FullLogEntry = {\n ...data,\n lineNumber: call.getLineNumber(),\n fileName: call\n .getFileName()\n .replace(/.*node_modules\\//, '')\n .replace(/.*src\\//, 'src/'),\n serviceContext: {\n service: process.env.CHART ?? process.env.HOSTNAME,\n version: process.env.BUILD_ID,\n environment: process.env.NODE_ENV,\n instance: process.env.KUBERNETES_POD_NAME ?? INSTANCE_ID\n }\n };\n\n // Ignore when silenced\n if (process.env.SILENCE_REPORT) return fullEntry;\n\n if (process.env.NODE_ENV === 'test') {\n // In test mode pretty print with full depth\n console.dir(fullEntry, { depth: 5 });\n } else if (isLocalDevelopment()) {\n // Local development: use formatted, colorized output\n const formattedLog = formatLocalLog(fullEntry);\n if (data.severity === LogSeverity.ERROR || data.severity === LogSeverity.CRITICAL || (data.statusCode && data.statusCode >= 500)) {\n console.error(formattedLog);\n } else {\n console.log(formattedLog);\n }\n } else {\n // Production/GCP: use JSON format for structured logging\n const entry = stringify(fullEntry);\n if (data.statusCode && data.statusCode >= 500) {\n console.error(entry);\n } else {\n console.log(entry);\n }\n }\n \n return fullEntry;\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"output.js","names":["_crypto","require","_callsite","_interopRequireDefault","_jsonStringifySafe","_const","INSTANCE_ID","exports","randomUUID","colors","reset","bright","dim","red","green","yellow","blue","magenta","cyan","white","gray","getSeverityColor","severity","LogSeverity","DEBUG","INFO","WARNING","ERROR","CRITICAL","formatTime","date","now","Date","toTimeString","split","formatLocalLog","severityOrEntry","message","undefined","time","severityColor","useColors","process","env","NODE_ENV","output","padEnd","console","log","entry","showDetailedLogs","DETAILED_LOGS","parts","push","join","errorType","errorCode","statusCode","errorParts","statusColor","length","context","httpRequest","req","method","url","stack_trace","data","Object","keys","JSON","stringify","fileName","lineNumber","isLocalDevelopment","LOG_FORMAT","KUBERNETES_POD_NAME","CLOUD_RUN_JOB","SENSITIVE_KEY_PATTERN","REDACTED","MAX_REDACT_DEPTH","redactSensitive","value","depth","seen","WeakSet","has","add","Array","isArray","map","item","prototype","getPrototypeOf","result","key","entries","test","call","callsite","fullEntry","getLineNumber","getFileName","replace","serviceContext","service","CHART","HOSTNAME","version","BUILD_ID","environment","instance","SILENCE_REPORT","dir","formattedLog","error"],"sources":["../../../src/utils/report/output.ts"],"sourcesContent":["import { randomUUID } from 'crypto';\nimport callsite from 'callsite';\nimport stringify from 'json-stringify-safe';\nimport { LogEntry, FullLogEntry } from './output.types';\nimport { LogSeverity } from 'const';\n\n// NOTE: Server instance id (helpful for distributed logs)\nexport const INSTANCE_ID = randomUUID();\n\n// ANSI color codes for terminal output\nconst colors = {\n reset: '\\x1b[0m',\n bright: '\\x1b[1m',\n dim: '\\x1b[2m',\n red: '\\x1b[31m',\n green: '\\x1b[32m',\n yellow: '\\x1b[33m',\n blue: '\\x1b[34m',\n magenta: '\\x1b[35m',\n cyan: '\\x1b[36m',\n white: '\\x1b[37m',\n gray: '\\x1b[90m'\n};\n\n/**\n * Get color for log severity level\n */\nfunction getSeverityColor(severity?: string): string {\n switch (severity) {\n case LogSeverity.DEBUG:\n return colors.cyan;\n case LogSeverity.INFO:\n return colors.green;\n case LogSeverity.WARNING:\n return colors.yellow;\n case LogSeverity.ERROR:\n return colors.red;\n case LogSeverity.CRITICAL:\n return `${colors.bright}${colors.red}`;\n default:\n return colors.white;\n }\n}\n\n/**\n * Format timestamp as HH:MM:SS\n */\nexport function formatTime(date?: Date): string {\n const now = date || new Date();\n return now.toTimeString().split(' ')[0];\n}\n\n/**\n * Format log entry for human-readable local development output\n */\nexport function formatLocalLog(severity: LogSeverity | string, message: string): void;\nexport function formatLocalLog(entry: FullLogEntry): string;\nexport function formatLocalLog(severityOrEntry: LogSeverity | string | FullLogEntry, message?: string): string | void {\n // Handle overloaded signature for testing\n if (typeof severityOrEntry === 'string' && message !== undefined) {\n const time = formatTime();\n const severityColor = getSeverityColor(severityOrEntry);\n const useColors = process.env.NODE_ENV !== 'production';\n \n const output = useColors \n ? `${colors.gray}[${time}]${colors.reset} ${severityColor}${severityOrEntry.padEnd(8)}${colors.reset} ${message}`\n : `[${time}] ${severityOrEntry.padEnd(8)} ${message}`;\n \n console.log(output);\n return;\n }\n \n // Original function implementation\n const entry = severityOrEntry as FullLogEntry;\n const time = formatTime();\n const severity = entry.severity || 'INFO';\n const severityColor = getSeverityColor(severity);\n const showDetailedLogs = process.env.DETAILED_LOGS === 'true';\n \n const parts: string[] = [];\n \n // Timestamp and severity\n parts.push(\n `${colors.gray}[${time}]${colors.reset} ${severityColor}${severity.padEnd(8)}${colors.reset}`\n );\n \n // Message\n if (entry.message) {\n parts.push(`${entry.message}`);\n }\n \n let output = parts.join(' ');\n \n // Error details (compact format)\n if (entry.errorType || entry.errorCode || entry.statusCode) {\n const errorParts: string[] = [];\n if (entry.errorCode) errorParts.push(`${colors.red}${entry.errorCode}${colors.reset}`);\n if (entry.statusCode) {\n const statusColor = entry.statusCode >= 500 ? colors.red : entry.statusCode >= 400 ? colors.yellow : colors.green;\n errorParts.push(`${statusColor}${entry.statusCode}${colors.reset}`);\n }\n if (errorParts.length > 0) {\n output += ` ${colors.dim}[${errorParts.join(' ')}]${colors.reset}`;\n }\n }\n \n // HTTP request context (compact)\n if (entry.context?.httpRequest) {\n const req = entry.context.httpRequest;\n output += ` ${colors.dim}${req.method} ${req.url}${colors.reset}`;\n }\n \n // Stack trace (only in detailed mode)\n if (showDetailedLogs && entry.stack_trace) {\n output += `\\n${colors.gray}Stack Trace:${colors.reset}\\n${colors.dim}${entry.stack_trace}${colors.reset}`;\n }\n \n // Additional data (only in detailed mode or if no message)\n if (showDetailedLogs && entry.data && Object.keys(entry.data).length > 0) {\n output += `\\n${colors.cyan}Data:${colors.reset}\\n${colors.dim}${JSON.stringify(entry.data, null, 2)}${colors.reset}`;\n }\n \n // File location (only in detailed mode)\n if (showDetailedLogs && entry.fileName) {\n output += `\\n ${colors.dim}${entry.fileName}:${entry.lineNumber || '?'}${colors.reset}`;\n }\n \n return output;\n}\n\n/**\n * Check if running in local development environment\n */\nfunction isLocalDevelopment(): boolean {\n // Allow forcing local format via environment variable\n if (process.env.LOG_FORMAT === 'local') return true;\n if (process.env.LOG_FORMAT === 'json') return false;\n \n return (\n process.env.NODE_ENV === 'development' ||\n process.env.NODE_ENV === 'dev' ||\n (!process.env.NODE_ENV && !process.env.KUBERNETES_POD_NAME && !process.env.CLOUD_RUN_JOB)\n );\n}\n\n/** Keys whose values must never be written to the logs */\nconst SENSITIVE_KEY_PATTERN =\n /^(authorization|proxy[-_]?authorization|cookie|set[-_]?cookie|password|passwd|pass|secret|client[-_]?secret|token|access[-_]?token|refresh[-_]?token|id[-_]?token|api[-_]?key|private[-_]?key|credentials?|otp)$/i;\nconst REDACTED = '[REDACTED]';\nconst MAX_REDACT_DEPTH = 10;\n\n/** Returns a copy of the value with sensitive fields masked */\nexport function redactSensitive(\n value: unknown,\n depth = 0,\n seen: WeakSet<object> = new WeakSet()\n): unknown {\n if (!value || typeof value !== 'object' || depth > MAX_REDACT_DEPTH)\n return value;\n if (seen.has(value)) return value;\n seen.add(value);\n if (Array.isArray(value))\n return value.map((item) => redactSensitive(item, depth + 1, seen));\n // Only walk plain objects, leave class instances (Errors, Buffers, Dates...) untouched\n const prototype = Object.getPrototypeOf(value);\n if (prototype !== Object.prototype && prototype !== null) return value;\n const result: Record<string, unknown> = {};\n for (const [key, entry] of Object.entries(value)) {\n result[key] = SENSITIVE_KEY_PATTERN.test(key)\n ? REDACTED\n : redactSensitive(entry, depth + 1, seen);\n }\n return result;\n}\n\n/** Add metadata to the log entry and print it */\nexport function output(data: LogEntry): FullLogEntry {\n if (data.data !== undefined)\n data = { ...data, data: redactSensitive(data.data) };\n\n // Gather useful information about where the logger was called from\n const call = callsite()[2];\n\n // Append additional metadata to the log entry\n const fullEntry: FullLogEntry = {\n ...data,\n lineNumber: call.getLineNumber(),\n fileName: call\n .getFileName()\n .replace(/.*node_modules\\//, '')\n .replace(/.*src\\//, 'src/'),\n serviceContext: {\n service: process.env.CHART ?? process.env.HOSTNAME,\n version: process.env.BUILD_ID,\n environment: process.env.NODE_ENV,\n instance: process.env.KUBERNETES_POD_NAME ?? INSTANCE_ID\n }\n };\n\n // Ignore when silenced\n if (process.env.SILENCE_REPORT) return fullEntry;\n\n if (process.env.NODE_ENV === 'test') {\n // In test mode pretty print with full depth\n console.dir(fullEntry, { depth: 5 });\n } else if (isLocalDevelopment()) {\n // Local development: use formatted, colorized output\n const formattedLog = formatLocalLog(fullEntry);\n if (data.severity === LogSeverity.ERROR || data.severity === LogSeverity.CRITICAL || (data.statusCode && data.statusCode >= 500)) {\n console.error(formattedLog);\n } else {\n console.log(formattedLog);\n }\n } else {\n // Production/GCP: use JSON format for structured logging\n const entry = stringify(fullEntry);\n if (data.statusCode && data.statusCode >= 500) {\n console.error(entry);\n } else {\n console.log(entry);\n }\n }\n \n return fullEntry;\n}\n"],"mappings":";;;;;;;;;;;AAAA,IAAAA,OAAA,GAAAC,OAAA;AACA,IAAAC,SAAA,GAAAC,sBAAA,CAAAF,OAAA;AACA,IAAAG,kBAAA,GAAAD,sBAAA,CAAAF,OAAA;AAEA,IAAAI,MAAA,GAAAJ,OAAA;AAEA;AACO,MAAMK,WAAW,GAAAC,OAAA,CAAAD,WAAA,GAAG,IAAAE,kBAAU,EAAC,CAAC;;AAEvC;AACA,MAAMC,MAAM,GAAG;EACbC,KAAK,EAAE,SAAS;EAChBC,MAAM,EAAE,SAAS;EACjBC,GAAG,EAAE,SAAS;EACdC,GAAG,EAAE,UAAU;EACfC,KAAK,EAAE,UAAU;EACjBC,MAAM,EAAE,UAAU;EAClBC,IAAI,EAAE,UAAU;EAChBC,OAAO,EAAE,UAAU;EACnBC,IAAI,EAAE,UAAU;EAChBC,KAAK,EAAE,UAAU;EACjBC,IAAI,EAAE;AACR,CAAC;;AAED;AACA;AACA;AACA,SAASC,gBAAgBA,CAACC,QAAiB,EAAU;EACnD,QAAQA,QAAQ;IACd,KAAKC,kBAAW,CAACC,KAAK;MACpB,OAAOf,MAAM,CAACS,IAAI;IACpB,KAAKK,kBAAW,CAACE,IAAI;MACnB,OAAOhB,MAAM,CAACK,KAAK;IACrB,KAAKS,kBAAW,CAACG,OAAO;MACtB,OAAOjB,MAAM,CAACM,MAAM;IACtB,KAAKQ,kBAAW,CAACI,KAAK;MACpB,OAAOlB,MAAM,CAACI,GAAG;IACnB,KAAKU,kBAAW,CAACK,QAAQ;MACvB,OAAO,GAAGnB,MAAM,CAACE,MAAM,GAAGF,MAAM,CAACI,GAAG,EAAE;IACxC;MACE,OAAOJ,MAAM,CAACU,KAAK;EACvB;AACF;;AAEA;AACA;AACA;AACO,SAASU,UAAUA,CAACC,IAAW,EAAU;EAC9C,MAAMC,GAAG,GAAGD,IAAI,IAAI,IAAIE,IAAI,CAAC,CAAC;EAC9B,OAAOD,GAAG,CAACE,YAAY,CAAC,CAAC,CAACC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACzC;;AAEA;AACA;AACA;;AAGO,SAASC,cAAcA,CAACC,eAAoD,EAAEC,OAAgB,EAAiB;EACpH;EACA,IAAI,OAAOD,eAAe,KAAK,QAAQ,IAAIC,OAAO,KAAKC,SAAS,EAAE;IAChE,MAAMC,IAAI,GAAGV,UAAU,CAAC,CAAC;IACzB,MAAMW,aAAa,GAAGnB,gBAAgB,CAACe,eAAe,CAAC;IACvD,MAAMK,SAAS,GAAGC,OAAO,CAACC,GAAG,CAACC,QAAQ,KAAK,YAAY;IAEvD,MAAMC,MAAM,GAAGJ,SAAS,GACpB,GAAGhC,MAAM,CAACW,IAAI,IAAImB,IAAI,IAAI9B,MAAM,CAACC,KAAK,IAAI8B,aAAa,GAAGJ,eAAe,CAACU,MAAM,CAAC,CAAC,CAAC,GAAGrC,MAAM,CAACC,KAAK,IAAI2B,OAAO,EAAE,GAC/G,IAAIE,IAAI,KAAKH,eAAe,CAACU,MAAM,CAAC,CAAC,CAAC,IAAIT,OAAO,EAAE;IAEvDU,OAAO,CAACC,GAAG,CAACH,MAAM,CAAC;IACnB;EACF;;EAEA;EACA,MAAMI,KAAK,GAAGb,eAA+B;EAC7C,MAAMG,IAAI,GAAGV,UAAU,CAAC,CAAC;EACzB,MAAMP,QAAQ,GAAG2B,KAAK,CAAC3B,QAAQ,IAAI,MAAM;EACzC,MAAMkB,aAAa,GAAGnB,gBAAgB,CAACC,QAAQ,CAAC;EAChD,MAAM4B,gBAAgB,GAAGR,OAAO,CAACC,GAAG,CAACQ,aAAa,KAAK,MAAM;EAE7D,MAAMC,KAAe,GAAG,EAAE;;EAE1B;EACAA,KAAK,CAACC,IAAI,CACR,GAAG5C,MAAM,CAACW,IAAI,IAAImB,IAAI,IAAI9B,MAAM,CAACC,KAAK,IAAI8B,aAAa,GAAGlB,QAAQ,CAACwB,MAAM,CAAC,CAAC,CAAC,GAAGrC,MAAM,CAACC,KAAK,EAC7F,CAAC;;EAED;EACA,IAAIuC,KAAK,CAACZ,OAAO,EAAE;IACjBe,KAAK,CAACC,IAAI,CAAC,GAAGJ,KAAK,CAACZ,OAAO,EAAE,CAAC;EAChC;EAEA,IAAIQ,MAAM,GAAGO,KAAK,CAACE,IAAI,CAAC,GAAG,CAAC;;EAE5B;EACA,IAAIL,KAAK,CAACM,SAAS,IAAIN,KAAK,CAACO,SAAS,IAAIP,KAAK,CAACQ,UAAU,EAAE;IAC1D,MAAMC,UAAoB,GAAG,EAAE;IAC/B,IAAIT,KAAK,CAACO,SAAS,EAAEE,UAAU,CAACL,IAAI,CAAC,GAAG5C,MAAM,CAACI,GAAG,GAAGoC,KAAK,CAACO,SAAS,GAAG/C,MAAM,CAACC,KAAK,EAAE,CAAC;IACtF,IAAIuC,KAAK,CAACQ,UAAU,EAAE;MACpB,MAAME,WAAW,GAAGV,KAAK,CAACQ,UAAU,IAAI,GAAG,GAAGhD,MAAM,CAACI,GAAG,GAAGoC,KAAK,CAACQ,UAAU,IAAI,GAAG,GAAGhD,MAAM,CAACM,MAAM,GAAGN,MAAM,CAACK,KAAK;MACjH4C,UAAU,CAACL,IAAI,CAAC,GAAGM,WAAW,GAAGV,KAAK,CAACQ,UAAU,GAAGhD,MAAM,CAACC,KAAK,EAAE,CAAC;IACrE;IACA,IAAIgD,UAAU,CAACE,MAAM,GAAG,CAAC,EAAE;MACzBf,MAAM,IAAI,IAAIpC,MAAM,CAACG,GAAG,IAAI8C,UAAU,CAACJ,IAAI,CAAC,GAAG,CAAC,IAAI7C,MAAM,CAACC,KAAK,EAAE;IACpE;EACF;;EAEA;EACA,IAAIuC,KAAK,CAACY,OAAO,EAAEC,WAAW,EAAE;IAC9B,MAAMC,GAAG,GAAGd,KAAK,CAACY,OAAO,CAACC,WAAW;IACrCjB,MAAM,IAAI,IAAIpC,MAAM,CAACG,GAAG,GAAGmD,GAAG,CAACC,MAAM,IAAID,GAAG,CAACE,GAAG,GAAGxD,MAAM,CAACC,KAAK,EAAE;EACnE;;EAEA;EACA,IAAIwC,gBAAgB,IAAID,KAAK,CAACiB,WAAW,EAAE;IACzCrB,MAAM,IAAI,KAAKpC,MAAM,CAACW,IAAI,eAAeX,MAAM,CAACC,KAAK,KAAKD,MAAM,CAACG,GAAG,GAAGqC,KAAK,CAACiB,WAAW,GAAGzD,MAAM,CAACC,KAAK,EAAE;EAC3G;;EAEA;EACA,IAAIwC,gBAAgB,IAAID,KAAK,CAACkB,IAAI,IAAIC,MAAM,CAACC,IAAI,CAACpB,KAAK,CAACkB,IAAI,CAAC,CAACP,MAAM,GAAG,CAAC,EAAE;IACxEf,MAAM,IAAI,KAAKpC,MAAM,CAACS,IAAI,QAAQT,MAAM,CAACC,KAAK,KAAKD,MAAM,CAACG,GAAG,GAAG0D,IAAI,CAACC,SAAS,CAACtB,KAAK,CAACkB,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG1D,MAAM,CAACC,KAAK,EAAE;EACtH;;EAEA;EACA,IAAIwC,gBAAgB,IAAID,KAAK,CAACuB,QAAQ,EAAE;IACtC3B,MAAM,IAAI,OAAOpC,MAAM,CAACG,GAAG,GAAGqC,KAAK,CAACuB,QAAQ,IAAIvB,KAAK,CAACwB,UAAU,IAAI,GAAG,GAAGhE,MAAM,CAACC,KAAK,EAAE;EAC1F;EAEA,OAAOmC,MAAM;AACf;;AAEA;AACA;AACA;AACA,SAAS6B,kBAAkBA,CAAA,EAAY;EACrC;EACA,IAAIhC,OAAO,CAACC,GAAG,CAACgC,UAAU,KAAK,OAAO,EAAE,OAAO,IAAI;EACnD,IAAIjC,OAAO,CAACC,GAAG,CAACgC,UAAU,KAAK,MAAM,EAAE,OAAO,KAAK;EAEnD,OACEjC,OAAO,CAACC,GAAG,CAACC,QAAQ,KAAK,aAAa,IACtCF,OAAO,CAACC,GAAG,CAACC,QAAQ,KAAK,KAAK,IAC7B,CAACF,OAAO,CAACC,GAAG,CAACC,QAAQ,IAAI,CAACF,OAAO,CAACC,GAAG,CAACiC,mBAAmB,IAAI,CAAClC,OAAO,CAACC,GAAG,CAACkC,aAAc;AAE7F;;AAEA;AACA,MAAMC,qBAAqB,GACzB,mNAAmN;AACrN,MAAMC,QAAQ,GAAG,YAAY;AAC7B,MAAMC,gBAAgB,GAAG,EAAE;;AAE3B;AACO,SAASC,eAAeA,CAC7BC,KAAc,EACdC,KAAK,GAAG,CAAC,EACTC,IAAqB,GAAG,IAAIC,OAAO,CAAC,CAAC,EAC5B;EACT,IAAI,CAACH,KAAK,IAAI,OAAOA,KAAK,KAAK,QAAQ,IAAIC,KAAK,GAAGH,gBAAgB,EACjE,OAAOE,KAAK;EACd,IAAIE,IAAI,CAACE,GAAG,CAACJ,KAAK,CAAC,EAAE,OAAOA,KAAK;EACjCE,IAAI,CAACG,GAAG,CAACL,KAAK,CAAC;EACf,IAAIM,KAAK,CAACC,OAAO,CAACP,KAAK,CAAC,EACtB,OAAOA,KAAK,CAACQ,GAAG,CAAEC,IAAI,IAAKV,eAAe,CAACU,IAAI,EAAER,KAAK,GAAG,CAAC,EAAEC,IAAI,CAAC,CAAC;EACpE;EACA,MAAMQ,SAAS,GAAGxB,MAAM,CAACyB,cAAc,CAACX,KAAK,CAAC;EAC9C,IAAIU,SAAS,KAAKxB,MAAM,CAACwB,SAAS,IAAIA,SAAS,KAAK,IAAI,EAAE,OAAOV,KAAK;EACtE,MAAMY,MAA+B,GAAG,CAAC,CAAC;EAC1C,KAAK,MAAM,CAACC,GAAG,EAAE9C,KAAK,CAAC,IAAImB,MAAM,CAAC4B,OAAO,CAACd,KAAK,CAAC,EAAE;IAChDY,MAAM,CAACC,GAAG,CAAC,GAAGjB,qBAAqB,CAACmB,IAAI,CAACF,GAAG,CAAC,GACzChB,QAAQ,GACRE,eAAe,CAAChC,KAAK,EAAEkC,KAAK,GAAG,CAAC,EAAEC,IAAI,CAAC;EAC7C;EACA,OAAOU,MAAM;AACf;;AAEA;AACO,SAASjD,MAAMA,CAACsB,IAAc,EAAgB;EACnD,IAAIA,IAAI,CAACA,IAAI,KAAK7B,SAAS,EACzB6B,IAAI,GAAG;IAAE,GAAGA,IAAI;IAAEA,IAAI,EAAEc,eAAe,CAACd,IAAI,CAACA,IAAI;EAAE,CAAC;;EAEtD;EACA,MAAM+B,IAAI,GAAG,IAAAC,iBAAQ,EAAC,CAAC,CAAC,CAAC,CAAC;;EAE1B;EACA,MAAMC,SAAuB,GAAG;IAC9B,GAAGjC,IAAI;IACPM,UAAU,EAAEyB,IAAI,CAACG,aAAa,CAAC,CAAC;IAChC7B,QAAQ,EAAE0B,IAAI,CACXI,WAAW,CAAC,CAAC,CACbC,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAC/BA,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC;IAC7BC,cAAc,EAAE;MACdC,OAAO,EAAE/D,OAAO,CAACC,GAAG,CAAC+D,KAAK,IAAIhE,OAAO,CAACC,GAAG,CAACgE,QAAQ;MAClDC,OAAO,EAAElE,OAAO,CAACC,GAAG,CAACkE,QAAQ;MAC7BC,WAAW,EAAEpE,OAAO,CAACC,GAAG,CAACC,QAAQ;MACjCmE,QAAQ,EAAErE,OAAO,CAACC,GAAG,CAACiC,mBAAmB,IAAItE;IAC/C;EACF,CAAC;;EAED;EACA,IAAIoC,OAAO,CAACC,GAAG,CAACqE,cAAc,EAAE,OAAOZ,SAAS;EAEhD,IAAI1D,OAAO,CAACC,GAAG,CAACC,QAAQ,KAAK,MAAM,EAAE;IACnC;IACAG,OAAO,CAACkE,GAAG,CAACb,SAAS,EAAE;MAAEjB,KAAK,EAAE;IAAE,CAAC,CAAC;EACtC,CAAC,MAAM,IAAIT,kBAAkB,CAAC,CAAC,EAAE;IAC/B;IACA,MAAMwC,YAAY,GAAG/E,cAAc,CAACiE,SAAS,CAAC;IAC9C,IAAIjC,IAAI,CAAC7C,QAAQ,KAAKC,kBAAW,CAACI,KAAK,IAAIwC,IAAI,CAAC7C,QAAQ,KAAKC,kBAAW,CAACK,QAAQ,IAAKuC,IAAI,CAACV,UAAU,IAAIU,IAAI,CAACV,UAAU,IAAI,GAAI,EAAE;MAChIV,OAAO,CAACoE,KAAK,CAACD,YAAY,CAAC;IAC7B,CAAC,MAAM;MACLnE,OAAO,CAACC,GAAG,CAACkE,YAAY,CAAC;IAC3B;EACF,CAAC,MAAM;IACL;IACA,MAAMjE,KAAK,GAAG,IAAAsB,0BAAS,EAAC6B,SAAS,CAAC;IAClC,IAAIjC,IAAI,CAACV,UAAU,IAAIU,IAAI,CAACV,UAAU,IAAI,GAAG,EAAE;MAC7CV,OAAO,CAACoE,KAAK,CAAClE,KAAK,CAAC;IACtB,CAAC,MAAM;MACLF,OAAO,CAACC,GAAG,CAACC,KAAK,CAAC;IACpB;EACF;EAEA,OAAOmD,SAAS;AAClB","ignoreList":[]}
@@ -23,8 +23,9 @@ const sendEmail = async emailOptions => {
23
23
  pass: process.env.SMTP_PASS
24
24
  },
25
25
  tls: {
26
- ciphers: 'SSLv3',
27
- rejectUnauthorized: false
26
+ // Certificates are verified by default to protect the SMTP credentials and content
27
+ // Set SMTP_TLS_REJECT_UNAUTHORIZED=false only for servers with self-signed certificates
28
+ rejectUnauthorized: process.env.SMTP_TLS_REJECT_UNAUTHORIZED !== 'false'
28
29
  },
29
30
  requireTLS: true
30
31
  });
@@ -49,9 +50,9 @@ const sendEmail = async emailOptions => {
49
50
 
50
51
  // Determine email status
51
52
  let status = 'sent';
52
- if (info.response.includes('queued')) {
53
+ if (info.response?.includes('queued')) {
53
54
  status = 'queued';
54
- } else if (info.response.includes('delivered')) {
55
+ } else if (info.response?.includes('delivered')) {
55
56
  status = 'delivered';
56
57
  }
57
58
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"sendEmail.js","names":["_nodemailer","_interopRequireDefault","require","sendEmail","emailOptions","transporter","nodemailer","createTransport","host","process","env","SMTP_HOST","port","parseInt","SMTP_PORT","secure","auth","user","SMTP_USER","pass","SMTP_PASS","tls","ciphers","rejectUnauthorized","requireTLS","mailOptions","from","to","cc","bcc","subject","text","html","attachments","replyTo","headers","priority","info","sendMail","status","response","includes","messageId","error","message","exports"],"sources":["../../../src/utils/sendEmail/sendEmail.ts"],"sourcesContent":["import { EmailOptions, EmailResult, EmailStatus } from './sendEmail.types';\nimport nodemailer, {\n Transporter,\n SendMailOptions,\n SentMessageInfo\n} from 'nodemailer';\n\n/**\n * Sends an email using Nodemailer.\n * @param emailOptions - Configuration options for sending the email.\n * @returns A Promise that resolves with an EmailResult indicating success or failure.\n */\nexport const sendEmail = async (\n emailOptions: EmailOptions\n): Promise<EmailResult> => {\n try {\n // Create a transport with SMTP configuration\n const transporter: Transporter = nodemailer.createTransport({\n host: process.env.SMTP_HOST,\n port: parseInt(process.env.SMTP_PORT || '587', 10),\n secure: process.env.SMTP_PORT === '465' ? true : false,\n auth: {\n user: process.env.SMTP_USER,\n pass: process.env.SMTP_PASS\n },\n tls: {\n ciphers: 'SSLv3',\n rejectUnauthorized: false\n },\n requireTLS: true\n });\n\n // Construct email options\n const mailOptions: SendMailOptions = {\n from: emailOptions.from,\n to: emailOptions.to,\n cc: emailOptions.cc,\n bcc: emailOptions.bcc,\n subject: emailOptions.subject,\n text: emailOptions.text,\n html: emailOptions.html,\n attachments: emailOptions.attachments,\n replyTo: emailOptions.replyTo,\n headers: emailOptions.headers,\n priority: emailOptions.priority\n };\n\n // Send email\n const info: SentMessageInfo = await transporter.sendMail(mailOptions);\n\n // Determine email status\n let status: EmailStatus = 'sent';\n if (info.response.includes('queued')) {\n status = 'queued';\n } else if (info.response.includes('delivered')) {\n status = 'delivered';\n }\n\n return { status, messageId: info.messageId };\n } catch (error) {\n return { status: 'failed', error: (error as Error).message };\n }\n};\n"],"mappings":";;;;;;;AACA,IAAAA,WAAA,GAAAC,sBAAA,CAAAC,OAAA;AAMA;AACA;AACA;AACA;AACA;AACO,MAAMC,SAAS,GAAG,MACvBC,YAA0B,IACD;EACzB,IAAI;IACF;IACA,MAAMC,WAAwB,GAAGC,mBAAU,CAACC,eAAe,CAAC;MAC1DC,IAAI,EAAEC,OAAO,CAACC,GAAG,CAACC,SAAS;MAC3BC,IAAI,EAAEC,QAAQ,CAACJ,OAAO,CAACC,GAAG,CAACI,SAAS,IAAI,KAAK,EAAE,EAAE,CAAC;MAClDC,MAAM,EAAEN,OAAO,CAACC,GAAG,CAACI,SAAS,KAAK,KAAK,GAAG,IAAI,GAAG,KAAK;MACtDE,IAAI,EAAE;QACJC,IAAI,EAAER,OAAO,CAACC,GAAG,CAACQ,SAAS;QAC3BC,IAAI,EAAEV,OAAO,CAACC,GAAG,CAACU;MACpB,CAAC;MACDC,GAAG,EAAE;QACHC,OAAO,EAAE,OAAO;QAChBC,kBAAkB,EAAE;MACtB,CAAC;MACDC,UAAU,EAAE;IACd,CAAC,CAAC;;IAEF;IACA,MAAMC,WAA4B,GAAG;MACnCC,IAAI,EAAEtB,YAAY,CAACsB,IAAI;MACvBC,EAAE,EAAEvB,YAAY,CAACuB,EAAE;MACnBC,EAAE,EAAExB,YAAY,CAACwB,EAAE;MACnBC,GAAG,EAAEzB,YAAY,CAACyB,GAAG;MACrBC,OAAO,EAAE1B,YAAY,CAAC0B,OAAO;MAC7BC,IAAI,EAAE3B,YAAY,CAAC2B,IAAI;MACvBC,IAAI,EAAE5B,YAAY,CAAC4B,IAAI;MACvBC,WAAW,EAAE7B,YAAY,CAAC6B,WAAW;MACrCC,OAAO,EAAE9B,YAAY,CAAC8B,OAAO;MAC7BC,OAAO,EAAE/B,YAAY,CAAC+B,OAAO;MAC7BC,QAAQ,EAAEhC,YAAY,CAACgC;IACzB,CAAC;;IAED;IACA,MAAMC,IAAqB,GAAG,MAAMhC,WAAW,CAACiC,QAAQ,CAACb,WAAW,CAAC;;IAErE;IACA,IAAIc,MAAmB,GAAG,MAAM;IAChC,IAAIF,IAAI,CAACG,QAAQ,CAACC,QAAQ,CAAC,QAAQ,CAAC,EAAE;MACpCF,MAAM,GAAG,QAAQ;IACnB,CAAC,MAAM,IAAIF,IAAI,CAACG,QAAQ,CAACC,QAAQ,CAAC,WAAW,CAAC,EAAE;MAC9CF,MAAM,GAAG,WAAW;IACtB;IAEA,OAAO;MAAEA,MAAM;MAAEG,SAAS,EAAEL,IAAI,CAACK;IAAU,CAAC;EAC9C,CAAC,CAAC,OAAOC,KAAK,EAAE;IACd,OAAO;MAAEJ,MAAM,EAAE,QAAQ;MAAEI,KAAK,EAAGA,KAAK,CAAWC;IAAQ,CAAC;EAC9D;AACF,CAAC;AAACC,OAAA,CAAA1C,SAAA,GAAAA,SAAA","ignoreList":[]}
1
+ {"version":3,"file":"sendEmail.js","names":["_nodemailer","_interopRequireDefault","require","sendEmail","emailOptions","transporter","nodemailer","createTransport","host","process","env","SMTP_HOST","port","parseInt","SMTP_PORT","secure","auth","user","SMTP_USER","pass","SMTP_PASS","tls","rejectUnauthorized","SMTP_TLS_REJECT_UNAUTHORIZED","requireTLS","mailOptions","from","to","cc","bcc","subject","text","html","attachments","replyTo","headers","priority","info","sendMail","status","response","includes","messageId","error","message","exports"],"sources":["../../../src/utils/sendEmail/sendEmail.ts"],"sourcesContent":["import { EmailOptions, EmailResult, EmailStatus } from './sendEmail.types';\nimport nodemailer, {\n Transporter,\n SendMailOptions,\n SentMessageInfo\n} from 'nodemailer';\n\n/**\n * Sends an email using Nodemailer.\n * @param emailOptions - Configuration options for sending the email.\n * @returns A Promise that resolves with an EmailResult indicating success or failure.\n */\nexport const sendEmail = async (\n emailOptions: EmailOptions\n): Promise<EmailResult> => {\n try {\n // Create a transport with SMTP configuration\n const transporter: Transporter = nodemailer.createTransport({\n host: process.env.SMTP_HOST,\n port: parseInt(process.env.SMTP_PORT || '587', 10),\n secure: process.env.SMTP_PORT === '465' ? true : false,\n auth: {\n user: process.env.SMTP_USER,\n pass: process.env.SMTP_PASS\n },\n tls: {\n // Certificates are verified by default to protect the SMTP credentials and content\n // Set SMTP_TLS_REJECT_UNAUTHORIZED=false only for servers with self-signed certificates\n rejectUnauthorized: process.env.SMTP_TLS_REJECT_UNAUTHORIZED !== 'false'\n },\n requireTLS: true\n });\n\n // Construct email options\n const mailOptions: SendMailOptions = {\n from: emailOptions.from,\n to: emailOptions.to,\n cc: emailOptions.cc,\n bcc: emailOptions.bcc,\n subject: emailOptions.subject,\n text: emailOptions.text,\n html: emailOptions.html,\n attachments: emailOptions.attachments,\n replyTo: emailOptions.replyTo,\n headers: emailOptions.headers,\n priority: emailOptions.priority\n };\n\n // Send email\n const info: SentMessageInfo = await transporter.sendMail(mailOptions);\n\n // Determine email status\n let status: EmailStatus = 'sent';\n if (info.response?.includes('queued')) {\n status = 'queued';\n } else if (info.response?.includes('delivered')) {\n status = 'delivered';\n }\n\n return { status, messageId: info.messageId };\n } catch (error) {\n return { status: 'failed', error: (error as Error).message };\n }\n};\n"],"mappings":";;;;;;;AACA,IAAAA,WAAA,GAAAC,sBAAA,CAAAC,OAAA;AAMA;AACA;AACA;AACA;AACA;AACO,MAAMC,SAAS,GAAG,MACvBC,YAA0B,IACD;EACzB,IAAI;IACF;IACA,MAAMC,WAAwB,GAAGC,mBAAU,CAACC,eAAe,CAAC;MAC1DC,IAAI,EAAEC,OAAO,CAACC,GAAG,CAACC,SAAS;MAC3BC,IAAI,EAAEC,QAAQ,CAACJ,OAAO,CAACC,GAAG,CAACI,SAAS,IAAI,KAAK,EAAE,EAAE,CAAC;MAClDC,MAAM,EAAEN,OAAO,CAACC,GAAG,CAACI,SAAS,KAAK,KAAK,GAAG,IAAI,GAAG,KAAK;MACtDE,IAAI,EAAE;QACJC,IAAI,EAAER,OAAO,CAACC,GAAG,CAACQ,SAAS;QAC3BC,IAAI,EAAEV,OAAO,CAACC,GAAG,CAACU;MACpB,CAAC;MACDC,GAAG,EAAE;QACH;QACA;QACAC,kBAAkB,EAAEb,OAAO,CAACC,GAAG,CAACa,4BAA4B,KAAK;MACnE,CAAC;MACDC,UAAU,EAAE;IACd,CAAC,CAAC;;IAEF;IACA,MAAMC,WAA4B,GAAG;MACnCC,IAAI,EAAEtB,YAAY,CAACsB,IAAI;MACvBC,EAAE,EAAEvB,YAAY,CAACuB,EAAE;MACnBC,EAAE,EAAExB,YAAY,CAACwB,EAAE;MACnBC,GAAG,EAAEzB,YAAY,CAACyB,GAAG;MACrBC,OAAO,EAAE1B,YAAY,CAAC0B,OAAO;MAC7BC,IAAI,EAAE3B,YAAY,CAAC2B,IAAI;MACvBC,IAAI,EAAE5B,YAAY,CAAC4B,IAAI;MACvBC,WAAW,EAAE7B,YAAY,CAAC6B,WAAW;MACrCC,OAAO,EAAE9B,YAAY,CAAC8B,OAAO;MAC7BC,OAAO,EAAE/B,YAAY,CAAC+B,OAAO;MAC7BC,QAAQ,EAAEhC,YAAY,CAACgC;IACzB,CAAC;;IAED;IACA,MAAMC,IAAqB,GAAG,MAAMhC,WAAW,CAACiC,QAAQ,CAACb,WAAW,CAAC;;IAErE;IACA,IAAIc,MAAmB,GAAG,MAAM;IAChC,IAAIF,IAAI,CAACG,QAAQ,EAAEC,QAAQ,CAAC,QAAQ,CAAC,EAAE;MACrCF,MAAM,GAAG,QAAQ;IACnB,CAAC,MAAM,IAAIF,IAAI,CAACG,QAAQ,EAAEC,QAAQ,CAAC,WAAW,CAAC,EAAE;MAC/CF,MAAM,GAAG,WAAW;IACtB;IAEA,OAAO;MAAEA,MAAM;MAAEG,SAAS,EAAEL,IAAI,CAACK;IAAU,CAAC;EAC9C,CAAC,CAAC,OAAOC,KAAK,EAAE;IACd,OAAO;MAAEJ,MAAM,EAAE,QAAQ;MAAEI,KAAK,EAAGA,KAAK,CAAWC;IAAQ,CAAC;EAC9D;AACF,CAAC;AAACC,OAAA,CAAA1C,SAAA,GAAAA,SAAA","ignoreList":[]}
@@ -65,6 +65,13 @@ function getOtpSecret() {
65
65
  function hashOtp(otp, secret) {
66
66
  return (0, _crypto.createHmac)('sha256', secret).update(otp).digest('hex');
67
67
  }
68
+
69
+ /** Constant time string comparison */
70
+ function safeEqual(a, b) {
71
+ const bufferA = Buffer.from(a);
72
+ const bufferB = Buffer.from(b);
73
+ return bufferA.length === bufferB.length && (0, _crypto.timingSafeEqual)(bufferA, bufferB);
74
+ }
68
75
  function generateOtp(length) {
69
76
  let otp = '';
70
77
  for (let index = 0; index < length; index += 1) {
@@ -129,13 +136,16 @@ function createVerificationToken(options) {
129
136
  }
130
137
  const verificationToken = exports.verificationToken = createVerificationToken;
131
138
  function verifyVerificationToken(token, options = {}) {
132
- // Default test verification bypass for test@review.com with OTP 000000
133
- if (options.subject === 'test@review.com' && options.otp === '000000') {
139
+ // Optional verification bypass for a single review account (e.g. app store review)
140
+ // It is disabled unless both VERIFICATION_BYPASS_SUBJECT and VERIFICATION_BYPASS_OTP are explicitly set
141
+ const bypassSubject = process.env.VERIFICATION_BYPASS_SUBJECT;
142
+ const bypassOtp = process.env.VERIFICATION_BYPASS_OTP;
143
+ if (bypassSubject && bypassOtp && options.subject === bypassSubject && options.otp !== undefined && safeEqual(options.otp, bypassOtp)) {
134
144
  return {
135
- act: options.action || 'TEST_VERIFICATION',
136
- otp: hashOtp('000000', getOtpSecret()),
145
+ act: options.action ? normalizeAction(options.action) : 'TEST_VERIFICATION',
146
+ otp: hashOtp(bypassOtp, getOtpSecret()),
137
147
  jti: 'test-verification-jti',
138
- sub: 'test@review.com',
148
+ sub: bypassSubject,
139
149
  iat: Math.floor(Date.now() / 1000),
140
150
  exp: Math.floor(Date.now() / 1000) + DEFAULT_EXPIRES_IN_SECONDS,
141
151
  iss: buildIssuer(options.issuer)
@@ -150,9 +160,6 @@ function verifyVerificationToken(token, options = {}) {
150
160
  errorCode: 'unauthorized',
151
161
  statusCode: 401,
152
162
  message: 'Invalid verification token supplied',
153
- data: {
154
- token
155
- },
156
163
  error: error instanceof Error ? error : undefined
157
164
  });
158
165
  }
@@ -191,7 +198,7 @@ function verifyVerificationToken(token, options = {}) {
191
198
  if (options.otp) {
192
199
  const otpSecret = getOtpSecret();
193
200
  const expectedHash = hashOtp(options.otp, otpSecret);
194
- if (payload.otp !== expectedHash) {
201
+ if (!safeEqual(payload.otp, expectedHash)) {
195
202
  throw new _WebError.WebError({
196
203
  errorCode: 'verification_failed',
197
204
  statusCode: 403,
@@ -1 +1 @@
1
- {"version":3,"file":"verificationToken.js","names":["_crypto","require","_jsonwebtoken","_EngineError","_WebError","_checkEnvironment","_envAssert","DEFAULT_OTP_LENGTH","DEFAULT_EXPIRES_IN_SECONDS","DEFAULT_ISSUER","normalizeAction","action","EngineError","message","data","trim","toUpperCase","resolveOtpLength","length","undefined","Number","isInteger","resolveExpiresInSeconds","expiresInSeconds","getTokenSecret","secret","process","env","VERIFICATION_TOKEN_SECRET","assertEnvironment","envAssert","isString","getOtpSecret","VERIFICATION_TOKEN_OTP_SECRET","hashOtp","otp","createHmac","update","digest","generateOtp","index","randomInt","toString","buildIssuer","issuer","VERIFICATION_TOKEN_ISSUER","buildSignOptions","options","signOptions","algorithm","expiresIn","audience","VERIFICATION_TOKEN_AUDIENCE","normalizeAudience","Array","isArray","slice","buildVerifyOptions","verifyOptions","algorithms","normalizedAudience","createVerificationToken","otpLength","tokenSecret","otpSecret","payload","act","jti","randomUUID","sub","subject","token","sign","expiresAt","Date","now","verificationToken","exports","verifyVerificationToken","iat","Math","floor","exp","iss","verify","error","WebError","errorCode","statusCode","Error","expected","actual","expectedHash"],"sources":["../../../src/utils/verificationToken/verificationToken.ts"],"sourcesContent":["import { randomInt, randomUUID, createHmac } from 'crypto';\nimport { sign, verify, SignOptions, VerifyOptions } from 'jsonwebtoken';\nimport { EngineError } from 'entities/EngineError';\nimport { WebError } from 'entities/WebError';\nimport { assertEnvironment } from 'utils/checkEnvironment';\nimport { envAssert } from 'utils/envAssert';\nimport {\n VerificationTokenPayload,\n VerificationTokenOptions,\n VerificationTokenResult,\n VerifyVerificationTokenOptions\n} from './verificationToken.types';\n\nconst DEFAULT_OTP_LENGTH = 6;\nconst DEFAULT_EXPIRES_IN_SECONDS = 5 * 60;\nconst DEFAULT_ISSUER = 'node-server-engine';\n\nfunction normalizeAction(action: string): string {\n if (!action || typeof action !== 'string') {\n throw new EngineError({\n message: 'Verification token action must be a non-empty string',\n data: { action }\n });\n }\n return action.trim().toUpperCase();\n}\n\nfunction resolveOtpLength(length?: number): number {\n if (length === undefined) return DEFAULT_OTP_LENGTH;\n if (!Number.isInteger(length) || length < 4 || length > 10) {\n throw new EngineError({\n message: 'OTP length must be an integer between 4 and 10',\n data: { length }\n });\n }\n return length;\n}\n\nfunction resolveExpiresInSeconds(expiresInSeconds?: number): number {\n if (expiresInSeconds === undefined) return DEFAULT_EXPIRES_IN_SECONDS;\n if (!Number.isInteger(expiresInSeconds) || expiresInSeconds < 30) {\n throw new EngineError({\n message: 'Verification token expiration must be at least 30 seconds',\n data: { expiresInSeconds }\n });\n }\n return expiresInSeconds;\n}\n\nfunction getTokenSecret(): string {\n const secret = process.env.VERIFICATION_TOKEN_SECRET;\n if (!secret) {\n assertEnvironment({ VERIFICATION_TOKEN_SECRET: envAssert.isString() });\n }\n return secret as string;\n}\n\nfunction getOtpSecret(): string {\n return process.env.VERIFICATION_TOKEN_OTP_SECRET ?? getTokenSecret();\n}\n\nfunction hashOtp(otp: string, secret: string): string {\n return createHmac('sha256', secret).update(otp).digest('hex');\n}\n\nfunction generateOtp(length: number): string {\n let otp = '';\n for (let index = 0; index < length; index += 1) {\n otp += randomInt(0, 10).toString();\n }\n return otp;\n}\n\nfunction buildIssuer(issuer?: string): string {\n return issuer ?? process.env.VERIFICATION_TOKEN_ISSUER ?? DEFAULT_ISSUER;\n}\n\nfunction buildSignOptions(options: VerificationTokenOptions): SignOptions {\n const signOptions: SignOptions = {\n algorithm: 'HS256',\n expiresIn: resolveExpiresInSeconds(options.expiresInSeconds),\n issuer: buildIssuer(options.issuer)\n };\n if (options.audience) {\n signOptions.audience = options.audience;\n } else if (process.env.VERIFICATION_TOKEN_AUDIENCE) {\n signOptions.audience = process.env.VERIFICATION_TOKEN_AUDIENCE;\n }\n return signOptions;\n}\n\nfunction normalizeAudience(\n audience?: string | string[]\n): VerifyOptions['audience'] | undefined {\n if (!audience) return undefined;\n if (Array.isArray(audience)) {\n if (audience.length === 0) return undefined;\n return [audience[0], ...audience.slice(1)];\n }\n return audience;\n}\n\nfunction buildVerifyOptions(options: VerifyVerificationTokenOptions): VerifyOptions {\n const verifyOptions: VerifyOptions = {\n algorithms: ['HS256'],\n issuer: buildIssuer(options.issuer)\n };\n const normalizedAudience = normalizeAudience(\n options.audience ?? process.env.VERIFICATION_TOKEN_AUDIENCE\n );\n if (normalizedAudience) verifyOptions.audience = normalizedAudience;\n return verifyOptions;\n}\n\nexport function createVerificationToken(\n options: VerificationTokenOptions\n): VerificationTokenResult {\n const action = normalizeAction(options.action);\n const otpLength = resolveOtpLength(options.otpLength);\n const tokenSecret = getTokenSecret();\n const otpSecret = getOtpSecret();\n const otp = generateOtp(otpLength);\n const signOptions = buildSignOptions(options);\n const payload: VerificationTokenPayload = {\n act: action,\n otp: hashOtp(otp, otpSecret),\n jti: randomUUID(),\n sub: options.subject\n };\n\n const token = sign(payload, tokenSecret, signOptions);\n const expiresInSeconds = signOptions.expiresIn as number;\n\n return {\n token,\n otp,\n expiresAt: Date.now() + expiresInSeconds * 1000,\n action\n };\n}\n\nexport const verificationToken = createVerificationToken;\n\nexport function verifyVerificationToken(\n token: string,\n options: VerifyVerificationTokenOptions = {}\n): VerificationTokenPayload {\n // Default test verification bypass for test@review.com with OTP 000000\n if (options.subject === 'test@review.com' && options.otp === '000000') {\n return {\n act: options.action || 'TEST_VERIFICATION',\n otp: hashOtp('000000', getOtpSecret()),\n jti: 'test-verification-jti',\n sub: 'test@review.com',\n iat: Math.floor(Date.now() / 1000),\n exp: Math.floor(Date.now() / 1000) + DEFAULT_EXPIRES_IN_SECONDS,\n iss: buildIssuer(options.issuer)\n } as VerificationTokenPayload;\n }\n\n const tokenSecret = getTokenSecret();\n let payload: VerificationTokenPayload;\n\n try {\n payload = verify(token, tokenSecret, buildVerifyOptions(options)) as VerificationTokenPayload;\n } catch (error: unknown) {\n throw new WebError({\n errorCode: 'unauthorized',\n statusCode: 401,\n message: 'Invalid verification token supplied',\n data: { token },\n error: error instanceof Error ? error : undefined\n });\n }\n\n if (!payload?.act || !payload?.otp || !payload?.jti) {\n throw new WebError({\n errorCode: 'unauthorized',\n statusCode: 401,\n message: 'Malformed verification token payload'\n });\n }\n\n if (options.action) {\n const action = normalizeAction(options.action);\n if (payload.act !== action) {\n throw new WebError({\n errorCode: 'verification_failed',\n statusCode: 403,\n message: 'Verification action mismatch',\n data: { expected: action, actual: payload.act }\n });\n }\n }\n\n if (options.subject && payload.sub !== options.subject) {\n throw new WebError({\n errorCode: 'verification_failed',\n statusCode: 403,\n message: 'Verification subject mismatch',\n data: { expected: options.subject, actual: payload.sub }\n });\n }\n\n if (options.otp) {\n const otpSecret = getOtpSecret();\n const expectedHash = hashOtp(options.otp, otpSecret);\n if (payload.otp !== expectedHash) {\n throw new WebError({\n errorCode: 'verification_failed',\n statusCode: 403,\n message: 'Verification OTP mismatch'\n });\n }\n }\n\n return payload;\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"verificationToken.js","names":["_crypto","require","_jsonwebtoken","_EngineError","_WebError","_checkEnvironment","_envAssert","DEFAULT_OTP_LENGTH","DEFAULT_EXPIRES_IN_SECONDS","DEFAULT_ISSUER","normalizeAction","action","EngineError","message","data","trim","toUpperCase","resolveOtpLength","length","undefined","Number","isInteger","resolveExpiresInSeconds","expiresInSeconds","getTokenSecret","secret","process","env","VERIFICATION_TOKEN_SECRET","assertEnvironment","envAssert","isString","getOtpSecret","VERIFICATION_TOKEN_OTP_SECRET","hashOtp","otp","createHmac","update","digest","safeEqual","a","b","bufferA","Buffer","from","bufferB","timingSafeEqual","generateOtp","index","randomInt","toString","buildIssuer","issuer","VERIFICATION_TOKEN_ISSUER","buildSignOptions","options","signOptions","algorithm","expiresIn","audience","VERIFICATION_TOKEN_AUDIENCE","normalizeAudience","Array","isArray","slice","buildVerifyOptions","verifyOptions","algorithms","normalizedAudience","createVerificationToken","otpLength","tokenSecret","otpSecret","payload","act","jti","randomUUID","sub","subject","token","sign","expiresAt","Date","now","verificationToken","exports","verifyVerificationToken","bypassSubject","VERIFICATION_BYPASS_SUBJECT","bypassOtp","VERIFICATION_BYPASS_OTP","iat","Math","floor","exp","iss","verify","error","WebError","errorCode","statusCode","Error","expected","actual","expectedHash"],"sources":["../../../src/utils/verificationToken/verificationToken.ts"],"sourcesContent":["import { randomInt, randomUUID, createHmac, timingSafeEqual } from 'crypto';\nimport { sign, verify, SignOptions, VerifyOptions } from 'jsonwebtoken';\nimport { EngineError } from 'entities/EngineError';\nimport { WebError } from 'entities/WebError';\nimport { assertEnvironment } from 'utils/checkEnvironment';\nimport { envAssert } from 'utils/envAssert';\nimport {\n VerificationTokenPayload,\n VerificationTokenOptions,\n VerificationTokenResult,\n VerifyVerificationTokenOptions\n} from './verificationToken.types';\n\nconst DEFAULT_OTP_LENGTH = 6;\nconst DEFAULT_EXPIRES_IN_SECONDS = 5 * 60;\nconst DEFAULT_ISSUER = 'node-server-engine';\n\nfunction normalizeAction(action: string): string {\n if (!action || typeof action !== 'string') {\n throw new EngineError({\n message: 'Verification token action must be a non-empty string',\n data: { action }\n });\n }\n return action.trim().toUpperCase();\n}\n\nfunction resolveOtpLength(length?: number): number {\n if (length === undefined) return DEFAULT_OTP_LENGTH;\n if (!Number.isInteger(length) || length < 4 || length > 10) {\n throw new EngineError({\n message: 'OTP length must be an integer between 4 and 10',\n data: { length }\n });\n }\n return length;\n}\n\nfunction resolveExpiresInSeconds(expiresInSeconds?: number): number {\n if (expiresInSeconds === undefined) return DEFAULT_EXPIRES_IN_SECONDS;\n if (!Number.isInteger(expiresInSeconds) || expiresInSeconds < 30) {\n throw new EngineError({\n message: 'Verification token expiration must be at least 30 seconds',\n data: { expiresInSeconds }\n });\n }\n return expiresInSeconds;\n}\n\nfunction getTokenSecret(): string {\n const secret = process.env.VERIFICATION_TOKEN_SECRET;\n if (!secret) {\n assertEnvironment({ VERIFICATION_TOKEN_SECRET: envAssert.isString() });\n }\n return secret as string;\n}\n\nfunction getOtpSecret(): string {\n return process.env.VERIFICATION_TOKEN_OTP_SECRET ?? getTokenSecret();\n}\n\nfunction hashOtp(otp: string, secret: string): string {\n return createHmac('sha256', secret).update(otp).digest('hex');\n}\n\n/** Constant time string comparison */\nfunction safeEqual(a: string, b: string): boolean {\n const bufferA = Buffer.from(a);\n const bufferB = Buffer.from(b);\n return bufferA.length === bufferB.length && timingSafeEqual(bufferA, bufferB);\n}\n\nfunction generateOtp(length: number): string {\n let otp = '';\n for (let index = 0; index < length; index += 1) {\n otp += randomInt(0, 10).toString();\n }\n return otp;\n}\n\nfunction buildIssuer(issuer?: string): string {\n return issuer ?? process.env.VERIFICATION_TOKEN_ISSUER ?? DEFAULT_ISSUER;\n}\n\nfunction buildSignOptions(options: VerificationTokenOptions): SignOptions {\n const signOptions: SignOptions = {\n algorithm: 'HS256',\n expiresIn: resolveExpiresInSeconds(options.expiresInSeconds),\n issuer: buildIssuer(options.issuer)\n };\n if (options.audience) {\n signOptions.audience = options.audience;\n } else if (process.env.VERIFICATION_TOKEN_AUDIENCE) {\n signOptions.audience = process.env.VERIFICATION_TOKEN_AUDIENCE;\n }\n return signOptions;\n}\n\nfunction normalizeAudience(\n audience?: string | string[]\n): VerifyOptions['audience'] | undefined {\n if (!audience) return undefined;\n if (Array.isArray(audience)) {\n if (audience.length === 0) return undefined;\n return [audience[0], ...audience.slice(1)];\n }\n return audience;\n}\n\nfunction buildVerifyOptions(options: VerifyVerificationTokenOptions): VerifyOptions {\n const verifyOptions: VerifyOptions = {\n algorithms: ['HS256'],\n issuer: buildIssuer(options.issuer)\n };\n const normalizedAudience = normalizeAudience(\n options.audience ?? process.env.VERIFICATION_TOKEN_AUDIENCE\n );\n if (normalizedAudience) verifyOptions.audience = normalizedAudience;\n return verifyOptions;\n}\n\nexport function createVerificationToken(\n options: VerificationTokenOptions\n): VerificationTokenResult {\n const action = normalizeAction(options.action);\n const otpLength = resolveOtpLength(options.otpLength);\n const tokenSecret = getTokenSecret();\n const otpSecret = getOtpSecret();\n const otp = generateOtp(otpLength);\n const signOptions = buildSignOptions(options);\n const payload: VerificationTokenPayload = {\n act: action,\n otp: hashOtp(otp, otpSecret),\n jti: randomUUID(),\n sub: options.subject\n };\n\n const token = sign(payload, tokenSecret, signOptions);\n const expiresInSeconds = signOptions.expiresIn as number;\n\n return {\n token,\n otp,\n expiresAt: Date.now() + expiresInSeconds * 1000,\n action\n };\n}\n\nexport const verificationToken = createVerificationToken;\n\nexport function verifyVerificationToken(\n token: string,\n options: VerifyVerificationTokenOptions = {}\n): VerificationTokenPayload {\n // Optional verification bypass for a single review account (e.g. app store review)\n // It is disabled unless both VERIFICATION_BYPASS_SUBJECT and VERIFICATION_BYPASS_OTP are explicitly set\n const bypassSubject = process.env.VERIFICATION_BYPASS_SUBJECT;\n const bypassOtp = process.env.VERIFICATION_BYPASS_OTP;\n if (\n bypassSubject &&\n bypassOtp &&\n options.subject === bypassSubject &&\n options.otp !== undefined &&\n safeEqual(options.otp, bypassOtp)\n ) {\n return {\n act: options.action ? normalizeAction(options.action) : 'TEST_VERIFICATION',\n otp: hashOtp(bypassOtp, getOtpSecret()),\n jti: 'test-verification-jti',\n sub: bypassSubject,\n iat: Math.floor(Date.now() / 1000),\n exp: Math.floor(Date.now() / 1000) + DEFAULT_EXPIRES_IN_SECONDS,\n iss: buildIssuer(options.issuer)\n } as VerificationTokenPayload;\n }\n\n const tokenSecret = getTokenSecret();\n let payload: VerificationTokenPayload;\n\n try {\n payload = verify(token, tokenSecret, buildVerifyOptions(options)) as VerificationTokenPayload;\n } catch (error: unknown) {\n throw new WebError({\n errorCode: 'unauthorized',\n statusCode: 401,\n message: 'Invalid verification token supplied',\n error: error instanceof Error ? error : undefined\n });\n }\n\n if (!payload?.act || !payload?.otp || !payload?.jti) {\n throw new WebError({\n errorCode: 'unauthorized',\n statusCode: 401,\n message: 'Malformed verification token payload'\n });\n }\n\n if (options.action) {\n const action = normalizeAction(options.action);\n if (payload.act !== action) {\n throw new WebError({\n errorCode: 'verification_failed',\n statusCode: 403,\n message: 'Verification action mismatch',\n data: { expected: action, actual: payload.act }\n });\n }\n }\n\n if (options.subject && payload.sub !== options.subject) {\n throw new WebError({\n errorCode: 'verification_failed',\n statusCode: 403,\n message: 'Verification subject mismatch',\n data: { expected: options.subject, actual: payload.sub }\n });\n }\n\n if (options.otp) {\n const otpSecret = getOtpSecret();\n const expectedHash = hashOtp(options.otp, otpSecret);\n if (!safeEqual(payload.otp, expectedHash)) {\n throw new WebError({\n errorCode: 'verification_failed',\n statusCode: 403,\n message: 'Verification OTP mismatch'\n });\n }\n }\n\n return payload;\n}\n"],"mappings":"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","ignoreList":[]}