{"version":3,"file":"otp-token.mjs","names":[],"sources":["../../../src/plugins/email-otp/otp-token.ts"],"sourcesContent":["import type { GenericEndpointContext } from \"@better-auth/core\";\nimport {\n\tconstantTimeEqual,\n\tsymmetricDecrypt,\n\tsymmetricEncrypt,\n} from \"../../crypto\";\nimport { getDate } from \"../../utils/date\";\nimport type { EmailOTPOptions, RequiredEmailOTPOptions } from \"./types\";\nimport { defaultKeyHasher, splitAtLastColon } from \"./utils\";\n\nexport async function storeOTP(\n\tctx: GenericEndpointContext,\n\topts: EmailOTPOptions,\n\totp: string,\n) {\n\tif (opts.storeOTP === \"encrypted\") {\n\t\treturn await symmetricEncrypt({\n\t\t\tkey: ctx.context.secretConfig,\n\t\t\tdata: otp,\n\t\t});\n\t}\n\tif (opts.storeOTP === \"hashed\") {\n\t\treturn await defaultKeyHasher(otp);\n\t}\n\tif (typeof opts.storeOTP === \"object\" && \"hash\" in opts.storeOTP) {\n\t\treturn await opts.storeOTP.hash(otp);\n\t}\n\tif (typeof opts.storeOTP === \"object\" && \"encrypt\" in opts.storeOTP) {\n\t\treturn await opts.storeOTP.encrypt(otp);\n\t}\n\n\treturn otp;\n}\n\nexport async function verifyStoredOTP(\n\tctx: GenericEndpointContext,\n\topts: EmailOTPOptions,\n\tstoredOtp: string,\n\totp: string,\n): Promise<boolean> {\n\tif (opts.storeOTP === \"encrypted\") {\n\t\tconst decryptedOtp = await symmetricDecrypt({\n\t\t\tkey: ctx.context.secretConfig,\n\t\t\tdata: storedOtp,\n\t\t});\n\t\treturn constantTimeEqual(decryptedOtp, otp);\n\t}\n\tif (opts.storeOTP === \"hashed\") {\n\t\tconst hashedOtp = await defaultKeyHasher(otp);\n\t\treturn constantTimeEqual(hashedOtp, storedOtp);\n\t}\n\tif (typeof opts.storeOTP === \"object\" && \"hash\" in opts.storeOTP) {\n\t\tconst hashedOtp = await opts.storeOTP.hash(otp);\n\t\treturn constantTimeEqual(hashedOtp, storedOtp);\n\t}\n\tif (typeof opts.storeOTP === \"object\" && \"decrypt\" in opts.storeOTP) {\n\t\tconst decryptedOtp = await opts.storeOTP.decrypt(storedOtp);\n\t\treturn constantTimeEqual(decryptedOtp, otp);\n\t}\n\n\treturn constantTimeEqual(otp, storedOtp);\n}\n\n/**\n * Retrieves the plain-text OTP from a stored value.\n * Returns `null` if the OTP is hashed and cannot be recovered.\n */\nasync function retrieveOTP(\n\tctx: GenericEndpointContext,\n\topts: EmailOTPOptions,\n\tstoredOtp: string,\n): Promise<string | null> {\n\tif (opts.storeOTP === \"plain\" || opts.storeOTP === undefined) {\n\t\treturn storedOtp;\n\t}\n\tif (opts.storeOTP === \"encrypted\") {\n\t\treturn await symmetricDecrypt({\n\t\t\tkey: ctx.context.secretConfig,\n\t\t\tdata: storedOtp,\n\t\t});\n\t}\n\tif (typeof opts.storeOTP === \"object\" && \"decrypt\" in opts.storeOTP) {\n\t\treturn await opts.storeOTP.decrypt(storedOtp);\n\t}\n\t// hashed or custom hash -> cannot recover\n\treturn null;\n}\n\n/**\n * Tries to reuse an existing unexpired OTP.\n * Returns the plain-text OTP if reusable, `null` otherwise.\n */\nexport async function tryReuseOTP(\n\tctx: GenericEndpointContext,\n\topts: RequiredEmailOTPOptions,\n\tidentifier: string,\n): Promise<string | null> {\n\tconst existing =\n\t\tawait ctx.context.internalAdapter.findVerificationValue(identifier);\n\tif (!existing || existing.expiresAt < new Date()) return null;\n\n\tconst [storedOtpValue, attempts] = splitAtLastColon(existing.value);\n\tconst allowedAttempts = opts.allowedAttempts || 3;\n\tif (attempts && parseInt(attempts) >= allowedAttempts) return null;\n\n\tconst plainOtp = await retrieveOTP(ctx, opts, storedOtpValue);\n\tif (!plainOtp) return null;\n\n\tawait ctx.context.internalAdapter.updateVerificationByIdentifier(identifier, {\n\t\texpiresAt: getDate(opts.expiresIn, \"sec\"),\n\t});\n\n\treturn plainOtp;\n}\n"],"mappings":";;;;;;AAUA,eAAsB,SACrB,KACA,MACA,KACC;AACD,KAAI,KAAK,aAAa,YACrB,QAAO,MAAM,iBAAiB;EAC7B,KAAK,IAAI,QAAQ;EACjB,MAAM;EACN,CAAC;AAEH,KAAI,KAAK,aAAa,SACrB,QAAO,MAAM,iBAAiB,IAAI;AAEnC,KAAI,OAAO,KAAK,aAAa,YAAY,UAAU,KAAK,SACvD,QAAO,MAAM,KAAK,SAAS,KAAK,IAAI;AAErC,KAAI,OAAO,KAAK,aAAa,YAAY,aAAa,KAAK,SAC1D,QAAO,MAAM,KAAK,SAAS,QAAQ,IAAI;AAGxC,QAAO;;AAGR,eAAsB,gBACrB,KACA,MACA,WACA,KACmB;AACnB,KAAI,KAAK,aAAa,YAKrB,QAAO,kBAJc,MAAM,iBAAiB;EAC3C,KAAK,IAAI,QAAQ;EACjB,MAAM;EACN,CAAC,EACqC,IAAI;AAE5C,KAAI,KAAK,aAAa,SAErB,QAAO,kBADW,MAAM,iBAAiB,IAAI,EACT,UAAU;AAE/C,KAAI,OAAO,KAAK,aAAa,YAAY,UAAU,KAAK,SAEvD,QAAO,kBADW,MAAM,KAAK,SAAS,KAAK,IAAI,EACX,UAAU;AAE/C,KAAI,OAAO,KAAK,aAAa,YAAY,aAAa,KAAK,SAE1D,QAAO,kBADc,MAAM,KAAK,SAAS,QAAQ,UAAU,EACpB,IAAI;AAG5C,QAAO,kBAAkB,KAAK,UAAU;;;;;;AAOzC,eAAe,YACd,KACA,MACA,WACyB;AACzB,KAAI,KAAK,aAAa,WAAW,KAAK,aAAa,OAClD,QAAO;AAER,KAAI,KAAK,aAAa,YACrB,QAAO,MAAM,iBAAiB;EAC7B,KAAK,IAAI,QAAQ;EACjB,MAAM;EACN,CAAC;AAEH,KAAI,OAAO,KAAK,aAAa,YAAY,aAAa,KAAK,SAC1D,QAAO,MAAM,KAAK,SAAS,QAAQ,UAAU;AAG9C,QAAO;;;;;;AAOR,eAAsB,YACrB,KACA,MACA,YACyB;CACzB,MAAM,WACL,MAAM,IAAI,QAAQ,gBAAgB,sBAAsB,WAAW;AACpE,KAAI,CAAC,YAAY,SAAS,4BAAY,IAAI,MAAM,CAAE,QAAO;CAEzD,MAAM,CAAC,gBAAgB,YAAY,iBAAiB,SAAS,MAAM;CACnE,MAAM,kBAAkB,KAAK,mBAAmB;AAChD,KAAI,YAAY,SAAS,SAAS,IAAI,gBAAiB,QAAO;CAE9D,MAAM,WAAW,MAAM,YAAY,KAAK,MAAM,eAAe;AAC7D,KAAI,CAAC,SAAU,QAAO;AAEtB,OAAM,IAAI,QAAQ,gBAAgB,+BAA+B,YAAY,EAC5E,WAAW,QAAQ,KAAK,WAAW,MAAM,EACzC,CAAC;AAEF,QAAO"}