{"version":3,"file":"verify-two-factor.mjs","names":[],"sources":["../../../src/plugins/two-factor/verify-two-factor.ts"],"sourcesContent":["import type { GenericEndpointContext } from \"@better-auth/core\";\nimport { APIError } from \"@better-auth/core/error\";\nimport { createHMAC } from \"@better-auth/utils/hmac\";\nimport { getSessionFromCtx } from \"../../api\";\nimport { expireCookie, setSessionCookie } from \"../../cookies\";\nimport { generateRandomString } from \"../../crypto/random\";\nimport { parseUserOutput } from \"../../db/schema\";\nimport {\n\tTRUST_DEVICE_COOKIE_MAX_AGE,\n\tTRUST_DEVICE_COOKIE_NAME,\n\tTWO_FACTOR_COOKIE_NAME,\n} from \"./constant\";\nimport { TWO_FACTOR_ERROR_CODES } from \"./error-code\";\nimport type { UserWithTwoFactor } from \"./types\";\n\nexport async function verifyTwoFactor(ctx: GenericEndpointContext) {\n\tconst invalid = (errorKey: keyof typeof TWO_FACTOR_ERROR_CODES) => {\n\t\tthrow APIError.from(\"UNAUTHORIZED\", TWO_FACTOR_ERROR_CODES[errorKey]);\n\t};\n\n\tconst session = await getSessionFromCtx(ctx);\n\tif (!session) {\n\t\tconst twoFactorCookie = ctx.context.createAuthCookie(\n\t\t\tTWO_FACTOR_COOKIE_NAME,\n\t\t);\n\t\tconst signedTwoFactorCookie = await ctx.getSignedCookie(\n\t\t\ttwoFactorCookie.name,\n\t\t\tctx.context.secret,\n\t\t);\n\t\tif (!signedTwoFactorCookie) {\n\t\t\tthrow APIError.from(\n\t\t\t\t\"UNAUTHORIZED\",\n\t\t\t\tTWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE,\n\t\t\t);\n\t\t}\n\t\tconst verificationToken =\n\t\t\tawait ctx.context.internalAdapter.findVerificationValue(\n\t\t\t\tsignedTwoFactorCookie,\n\t\t\t);\n\t\tif (!verificationToken) {\n\t\t\tthrow APIError.from(\n\t\t\t\t\"UNAUTHORIZED\",\n\t\t\t\tTWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE,\n\t\t\t);\n\t\t}\n\t\tconst user = (await ctx.context.internalAdapter.findUserById(\n\t\t\tverificationToken.value,\n\t\t)) as UserWithTwoFactor;\n\t\tif (!user) {\n\t\t\tthrow APIError.from(\n\t\t\t\t\"UNAUTHORIZED\",\n\t\t\t\tTWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE,\n\t\t\t);\n\t\t}\n\t\tconst dontRememberMe = await ctx.getSignedCookie(\n\t\t\tctx.context.authCookies.dontRememberToken.name,\n\t\t\tctx.context.secret,\n\t\t);\n\t\treturn {\n\t\t\tvalid: async (ctx: GenericEndpointContext) => {\n\t\t\t\tconst session = await ctx.context.internalAdapter.createSession(\n\t\t\t\t\tverificationToken.value,\n\t\t\t\t\t!!dontRememberMe,\n\t\t\t\t);\n\t\t\t\tif (!session) {\n\t\t\t\t\tthrow APIError.from(\"INTERNAL_SERVER_ERROR\", {\n\t\t\t\t\t\tmessage: \"failed to create session\",\n\t\t\t\t\t\tcode: \"FAILED_TO_CREATE_SESSION\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\t// Delete the verification token from the database after successful verification\n\t\t\t\tawait ctx.context.internalAdapter.deleteVerificationByIdentifier(\n\t\t\t\t\tsignedTwoFactorCookie,\n\t\t\t\t);\n\t\t\t\tawait setSessionCookie(ctx, {\n\t\t\t\t\tsession,\n\t\t\t\t\tuser,\n\t\t\t\t});\n\t\t\t\t// Always clear the two factor cookie after successful verification\n\t\t\t\texpireCookie(ctx, twoFactorCookie);\n\t\t\t\tif (ctx.body.trustDevice) {\n\t\t\t\t\tconst plugin = ctx.context.getPlugin(\"two-factor\");\n\t\t\t\t\tconst trustDeviceMaxAge = plugin!.options?.trustDeviceMaxAge;\n\t\t\t\t\tconst maxAge = trustDeviceMaxAge ?? TRUST_DEVICE_COOKIE_MAX_AGE;\n\t\t\t\t\tconst trustDeviceCookie = ctx.context.createAuthCookie(\n\t\t\t\t\t\tTRUST_DEVICE_COOKIE_NAME,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmaxAge,\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t\t/**\n\t\t\t\t\t * Create a random identifier for the trust device record.\n\t\t\t\t\t * Store it in the verification table with an expiration\n\t\t\t\t\t * so the server can validate and revoke it.\n\t\t\t\t\t */\n\t\t\t\t\tconst trustIdentifier = `trust-device-${generateRandomString(32)}`;\n\t\t\t\t\tconst token = await createHMAC(\"SHA-256\", \"base64urlnopad\").sign(\n\t\t\t\t\t\tctx.context.secret,\n\t\t\t\t\t\t`${user.id}!${trustIdentifier}`,\n\t\t\t\t\t);\n\t\t\t\t\tawait ctx.context.internalAdapter.createVerificationValue({\n\t\t\t\t\t\tvalue: user.id,\n\t\t\t\t\t\tidentifier: trustIdentifier,\n\t\t\t\t\t\texpiresAt: new Date(Date.now() + maxAge * 1000),\n\t\t\t\t\t});\n\t\t\t\t\tawait ctx.setSignedCookie(\n\t\t\t\t\t\ttrustDeviceCookie.name,\n\t\t\t\t\t\t`${token}!${trustIdentifier}`,\n\t\t\t\t\t\tctx.context.secret,\n\t\t\t\t\t\ttrustDeviceCookie.attributes,\n\t\t\t\t\t);\n\t\t\t\t\t// delete the dont remember me cookie\n\t\t\t\t\texpireCookie(ctx, ctx.context.authCookies.dontRememberToken);\n\t\t\t\t}\n\t\t\t\treturn ctx.json({\n\t\t\t\t\ttoken: session.token,\n\t\t\t\t\tuser: parseUserOutput(ctx.context.options, user),\n\t\t\t\t});\n\t\t\t},\n\t\t\tinvalid,\n\t\t\tsession: {\n\t\t\t\tsession: null,\n\t\t\t\tuser,\n\t\t\t},\n\t\t\tkey: signedTwoFactorCookie,\n\t\t};\n\t}\n\treturn {\n\t\tvalid: async (ctx: GenericEndpointContext) => {\n\t\t\treturn ctx.json({\n\t\t\t\ttoken: session.session.token,\n\t\t\t\tuser: parseUserOutput(ctx.context.options, session.user),\n\t\t\t});\n\t\t},\n\t\tinvalid,\n\t\tsession,\n\t\tkey: `${session.user.id}!${session.session.id}`,\n\t};\n}\n"],"mappings":";;;;;;;;;;;AAeA,eAAsB,gBAAgB,KAA6B;CAClE,MAAM,WAAW,aAAkD;AAClE,QAAM,SAAS,KAAK,gBAAgB,uBAAuB,UAAU;;CAGtE,MAAM,UAAU,MAAM,kBAAkB,IAAI;AAC5C,KAAI,CAAC,SAAS;EACb,MAAM,kBAAkB,IAAI,QAAQ,iBACnC,uBACA;EACD,MAAM,wBAAwB,MAAM,IAAI,gBACvC,gBAAgB,MAChB,IAAI,QAAQ,OACZ;AACD,MAAI,CAAC,sBACJ,OAAM,SAAS,KACd,gBACA,uBAAuB,0BACvB;EAEF,MAAM,oBACL,MAAM,IAAI,QAAQ,gBAAgB,sBACjC,sBACA;AACF,MAAI,CAAC,kBACJ,OAAM,SAAS,KACd,gBACA,uBAAuB,0BACvB;EAEF,MAAM,OAAQ,MAAM,IAAI,QAAQ,gBAAgB,aAC/C,kBAAkB,MAClB;AACD,MAAI,CAAC,KACJ,OAAM,SAAS,KACd,gBACA,uBAAuB,0BACvB;EAEF,MAAM,iBAAiB,MAAM,IAAI,gBAChC,IAAI,QAAQ,YAAY,kBAAkB,MAC1C,IAAI,QAAQ,OACZ;AACD,SAAO;GACN,OAAO,OAAO,QAAgC;IAC7C,MAAM,UAAU,MAAM,IAAI,QAAQ,gBAAgB,cACjD,kBAAkB,OAClB,CAAC,CAAC,eACF;AACD,QAAI,CAAC,QACJ,OAAM,SAAS,KAAK,yBAAyB;KAC5C,SAAS;KACT,MAAM;KACN,CAAC;AAGH,UAAM,IAAI,QAAQ,gBAAgB,+BACjC,sBACA;AACD,UAAM,iBAAiB,KAAK;KAC3B;KACA;KACA,CAAC;AAEF,iBAAa,KAAK,gBAAgB;AAClC,QAAI,IAAI,KAAK,aAAa;KAGzB,MAAM,SAFS,IAAI,QAAQ,UAAU,aAAa,CAChB,SAAS,qBACP;KACpC,MAAM,oBAAoB,IAAI,QAAQ,iBACrC,0BACA,EACC,QACA,CACD;;;;;;KAMD,MAAM,kBAAkB,gBAAgB,qBAAqB,GAAG;KAChE,MAAM,QAAQ,MAAM,WAAW,WAAW,iBAAiB,CAAC,KAC3D,IAAI,QAAQ,QACZ,GAAG,KAAK,GAAG,GAAG,kBACd;AACD,WAAM,IAAI,QAAQ,gBAAgB,wBAAwB;MACzD,OAAO,KAAK;MACZ,YAAY;MACZ,WAAW,IAAI,KAAK,KAAK,KAAK,GAAG,SAAS,IAAK;MAC/C,CAAC;AACF,WAAM,IAAI,gBACT,kBAAkB,MAClB,GAAG,MAAM,GAAG,mBACZ,IAAI,QAAQ,QACZ,kBAAkB,WAClB;AAED,kBAAa,KAAK,IAAI,QAAQ,YAAY,kBAAkB;;AAE7D,WAAO,IAAI,KAAK;KACf,OAAO,QAAQ;KACf,MAAM,gBAAgB,IAAI,QAAQ,SAAS,KAAK;KAChD,CAAC;;GAEH;GACA,SAAS;IACR,SAAS;IACT;IACA;GACD,KAAK;GACL;;AAEF,QAAO;EACN,OAAO,OAAO,QAAgC;AAC7C,UAAO,IAAI,KAAK;IACf,OAAO,QAAQ,QAAQ;IACvB,MAAM,gBAAgB,IAAI,QAAQ,SAAS,QAAQ,KAAK;IACxD,CAAC;;EAEH;EACA;EACA,KAAK,GAAG,QAAQ,KAAK,GAAG,GAAG,QAAQ,QAAQ;EAC3C"}