better-auth 1.4.9 → 1.5.0-beta.1
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.
- package/dist/api/index.d.mts +396 -395
- package/dist/api/index.mjs +6 -4
- package/dist/api/index.mjs.map +1 -1
- package/dist/api/middlewares/origin-check.d.mts +3 -3
- package/dist/api/middlewares/origin-check.mjs +14 -4
- package/dist/api/middlewares/origin-check.mjs.map +1 -1
- package/dist/api/routes/account.d.mts +11 -11
- package/dist/api/routes/account.mjs +59 -30
- package/dist/api/routes/account.mjs.map +1 -1
- package/dist/api/routes/callback.d.mts +2 -2
- package/dist/api/routes/email-verification.d.mts +4 -4
- package/dist/api/routes/email-verification.mjs +14 -14
- package/dist/api/routes/email-verification.mjs.map +1 -1
- package/dist/api/routes/error.d.mts +2 -2
- package/dist/api/routes/ok.d.mts +2 -2
- package/dist/api/routes/reset-password.d.mts +5 -5
- package/dist/api/routes/reset-password.mjs +9 -7
- package/dist/api/routes/reset-password.mjs.map +1 -1
- package/dist/api/routes/session.d.mts +14 -14
- package/dist/api/routes/session.mjs +31 -11
- package/dist/api/routes/session.mjs.map +1 -1
- package/dist/api/routes/sign-in.d.mts +3 -3
- package/dist/api/routes/sign-in.mjs +22 -17
- package/dist/api/routes/sign-in.mjs.map +1 -1
- package/dist/api/routes/sign-out.d.mts +2 -2
- package/dist/api/routes/sign-up.d.mts +2 -2
- package/dist/api/routes/sign-up.mjs +15 -12
- package/dist/api/routes/sign-up.mjs.map +1 -1
- package/dist/api/routes/update-user.d.mts +13 -13
- package/dist/api/routes/update-user.mjs +29 -24
- package/dist/api/routes/update-user.mjs.map +1 -1
- package/dist/api/to-auth-endpoints.mjs +7 -6
- package/dist/api/to-auth-endpoints.mjs.map +1 -1
- package/dist/client/lynx/index.d.mts +15 -15
- package/dist/client/plugins/index.d.mts +12 -2
- package/dist/client/plugins/index.mjs +11 -1
- package/dist/client/react/index.d.mts +13 -13
- package/dist/client/solid/index.d.mts +13 -13
- package/dist/client/svelte/index.d.mts +15 -15
- package/dist/client/types.d.mts +4 -1
- package/dist/client/vue/index.d.mts +13 -13
- package/dist/context/create-context.mjs +2 -2
- package/dist/context/create-context.mjs.map +1 -1
- package/dist/context/helpers.mjs +2 -2
- package/dist/context/helpers.mjs.map +1 -1
- package/dist/db/field.d.mts +6 -6
- package/dist/db/schema.mjs +14 -5
- package/dist/db/schema.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/integrations/next-js.d.mts +4 -4
- package/dist/integrations/svelte-kit.d.mts +2 -2
- package/dist/integrations/tanstack-start.d.mts +4 -4
- package/dist/oauth2/link-account.mjs +3 -2
- package/dist/oauth2/link-account.mjs.map +1 -1
- package/dist/oauth2/state.mjs +3 -3
- package/dist/oauth2/state.mjs.map +1 -1
- package/dist/plugins/admin/admin.d.mts +200 -137
- package/dist/plugins/admin/admin.mjs +3 -4
- package/dist/plugins/admin/admin.mjs.map +1 -1
- package/dist/plugins/admin/client.d.mts +87 -0
- package/dist/plugins/admin/client.mjs +3 -1
- package/dist/plugins/admin/client.mjs.map +1 -1
- package/dist/plugins/admin/error-codes.d.mts +90 -0
- package/dist/plugins/admin/error-codes.mjs.map +1 -1
- package/dist/plugins/admin/routes.mjs +40 -46
- package/dist/plugins/admin/routes.mjs.map +1 -1
- package/dist/plugins/anonymous/client.d.mts +19 -0
- package/dist/plugins/anonymous/client.mjs +4 -1
- package/dist/plugins/anonymous/client.mjs.map +1 -1
- package/dist/plugins/anonymous/error-codes.d.mts +22 -0
- package/dist/plugins/anonymous/index.d.mts +21 -9
- package/dist/plugins/anonymous/index.mjs +5 -5
- package/dist/plugins/anonymous/index.mjs.map +1 -1
- package/dist/plugins/api-key/client.d.mts +103 -0
- package/dist/plugins/api-key/client.mjs +4 -1
- package/dist/plugins/api-key/client.mjs.map +1 -1
- package/dist/plugins/api-key/error-codes.d.mts +106 -0
- package/dist/plugins/api-key/error-codes.mjs +34 -0
- package/dist/plugins/api-key/error-codes.mjs.map +1 -0
- package/dist/plugins/api-key/index.d.mts +181 -112
- package/dist/plugins/api-key/index.mjs +7 -34
- package/dist/plugins/api-key/index.mjs.map +1 -1
- package/dist/plugins/api-key/rate-limit.mjs +3 -2
- package/dist/plugins/api-key/rate-limit.mjs.map +1 -1
- package/dist/plugins/api-key/routes/create-api-key.mjs +19 -17
- package/dist/plugins/api-key/routes/create-api-key.mjs.map +1 -1
- package/dist/plugins/api-key/routes/delete-api-key.mjs +7 -5
- package/dist/plugins/api-key/routes/delete-api-key.mjs.map +1 -1
- package/dist/plugins/api-key/routes/get-api-key.mjs +5 -3
- package/dist/plugins/api-key/routes/get-api-key.mjs.map +1 -1
- package/dist/plugins/api-key/routes/update-api-key.mjs +18 -16
- package/dist/plugins/api-key/routes/update-api-key.mjs.map +1 -1
- package/dist/plugins/api-key/routes/verify-api-key.mjs +16 -35
- package/dist/plugins/api-key/routes/verify-api-key.mjs.map +1 -1
- package/dist/plugins/bearer/index.d.mts +3 -3
- package/dist/plugins/captcha/index.d.mts +2 -2
- package/dist/plugins/captcha/index.mjs +3 -3
- package/dist/plugins/captcha/index.mjs.map +1 -1
- package/dist/plugins/captcha/verify-handlers/captchafox.mjs +2 -2
- package/dist/plugins/captcha/verify-handlers/captchafox.mjs.map +1 -1
- package/dist/plugins/captcha/verify-handlers/cloudflare-turnstile.mjs +2 -2
- package/dist/plugins/captcha/verify-handlers/cloudflare-turnstile.mjs.map +1 -1
- package/dist/plugins/captcha/verify-handlers/google-recaptcha.mjs +2 -2
- package/dist/plugins/captcha/verify-handlers/google-recaptcha.mjs.map +1 -1
- package/dist/plugins/captcha/verify-handlers/h-captcha.mjs +2 -2
- package/dist/plugins/captcha/verify-handlers/h-captcha.mjs.map +1 -1
- package/dist/plugins/custom-session/index.d.mts +5 -5
- package/dist/plugins/device-authorization/index.d.mts +54 -18
- package/dist/plugins/device-authorization/routes.mjs +18 -18
- package/dist/plugins/device-authorization/routes.mjs.map +1 -1
- package/dist/plugins/email-otp/client.d.mts +15 -0
- package/dist/plugins/email-otp/client.mjs +4 -1
- package/dist/plugins/email-otp/client.mjs.map +1 -1
- package/dist/plugins/email-otp/error-codes.d.mts +18 -0
- package/dist/plugins/email-otp/error-codes.mjs +12 -0
- package/dist/plugins/email-otp/error-codes.mjs.map +1 -0
- package/dist/plugins/email-otp/index.d.mts +64 -55
- package/dist/plugins/email-otp/index.mjs +4 -3
- package/dist/plugins/email-otp/index.mjs.map +1 -1
- package/dist/plugins/email-otp/routes.mjs +30 -35
- package/dist/plugins/email-otp/routes.mjs.map +1 -1
- package/dist/plugins/generic-oauth/client.d.mts +27 -0
- package/dist/plugins/generic-oauth/client.mjs +4 -1
- package/dist/plugins/generic-oauth/client.mjs.map +1 -1
- package/dist/plugins/generic-oauth/error-codes.d.mts +30 -0
- package/dist/plugins/generic-oauth/index.d.mts +55 -37
- package/dist/plugins/generic-oauth/index.mjs +4 -4
- package/dist/plugins/generic-oauth/index.mjs.map +1 -1
- package/dist/plugins/generic-oauth/routes.mjs +11 -12
- package/dist/plugins/generic-oauth/routes.mjs.map +1 -1
- package/dist/plugins/haveibeenpwned/index.d.mts +7 -4
- package/dist/plugins/haveibeenpwned/index.mjs +5 -4
- package/dist/plugins/haveibeenpwned/index.mjs.map +1 -1
- package/dist/plugins/index.d.mts +4 -2
- package/dist/plugins/index.mjs +6 -4
- package/dist/plugins/jwt/index.d.mts +9 -9
- package/dist/plugins/jwt/index.mjs +2 -2
- package/dist/plugins/jwt/index.mjs.map +1 -1
- package/dist/plugins/last-login-method/index.d.mts +4 -4
- package/dist/plugins/magic-link/index.d.mts +4 -4
- package/dist/plugins/mcp/authorize.mjs +1 -1
- package/dist/plugins/mcp/authorize.mjs.map +1 -1
- package/dist/plugins/mcp/index.d.mts +10 -10
- package/dist/plugins/multi-session/client.d.mts +10 -14
- package/dist/plugins/multi-session/client.mjs +5 -2
- package/dist/plugins/multi-session/client.mjs.map +1 -1
- package/dist/plugins/multi-session/error-codes.d.mts +10 -0
- package/dist/plugins/multi-session/error-codes.mjs +8 -0
- package/dist/plugins/multi-session/error-codes.mjs.map +1 -0
- package/dist/plugins/multi-session/index.d.mts +18 -14
- package/dist/plugins/multi-session/index.mjs +6 -7
- package/dist/plugins/multi-session/index.mjs.map +1 -1
- package/dist/plugins/oauth-proxy/index.d.mts +8 -8
- package/dist/plugins/oidc-provider/authorize.mjs +1 -1
- package/dist/plugins/oidc-provider/authorize.mjs.map +1 -1
- package/dist/plugins/oidc-provider/error.mjs +1 -1
- package/dist/plugins/oidc-provider/error.mjs.map +1 -1
- package/dist/plugins/oidc-provider/index.d.mts +15 -15
- package/dist/plugins/one-tap/client.d.mts +2 -2
- package/dist/plugins/one-tap/index.d.mts +2 -2
- package/dist/plugins/one-time-token/index.d.mts +5 -5
- package/dist/plugins/open-api/index.d.mts +3 -3
- package/dist/plugins/organization/client.d.mts +229 -2
- package/dist/plugins/organization/client.mjs +3 -1
- package/dist/plugins/organization/client.mjs.map +1 -1
- package/dist/plugins/organization/error-codes.d.mts +224 -56
- package/dist/plugins/organization/organization.d.mts +7 -7
- package/dist/plugins/organization/organization.mjs +4 -4
- package/dist/plugins/organization/organization.mjs.map +1 -1
- package/dist/plugins/organization/routes/crud-access-control.d.mts +22 -22
- package/dist/plugins/organization/routes/crud-access-control.mjs +40 -39
- package/dist/plugins/organization/routes/crud-access-control.mjs.map +1 -1
- package/dist/plugins/organization/routes/crud-invites.d.mts +58 -58
- package/dist/plugins/organization/routes/crud-invites.mjs +42 -40
- package/dist/plugins/organization/routes/crud-invites.mjs.map +1 -1
- package/dist/plugins/organization/routes/crud-members.d.mts +67 -67
- package/dist/plugins/organization/routes/crud-members.mjs +41 -54
- package/dist/plugins/organization/routes/crud-members.mjs.map +1 -1
- package/dist/plugins/organization/routes/crud-org.d.mts +51 -51
- package/dist/plugins/organization/routes/crud-org.mjs +28 -25
- package/dist/plugins/organization/routes/crud-org.mjs.map +1 -1
- package/dist/plugins/organization/routes/crud-team.d.mts +77 -77
- package/dist/plugins/organization/routes/crud-team.mjs +41 -47
- package/dist/plugins/organization/routes/crud-team.mjs.map +1 -1
- package/dist/plugins/phone-number/client.d.mts +51 -0
- package/dist/plugins/phone-number/client.mjs +4 -1
- package/dist/plugins/phone-number/client.mjs.map +1 -1
- package/dist/plugins/phone-number/error-codes.d.mts +54 -0
- package/dist/plugins/phone-number/index.d.mts +81 -45
- package/dist/plugins/phone-number/index.mjs +2 -2
- package/dist/plugins/phone-number/index.mjs.map +1 -1
- package/dist/plugins/phone-number/routes.mjs +27 -28
- package/dist/plugins/phone-number/routes.mjs.map +1 -1
- package/dist/plugins/siwe/index.d.mts +3 -3
- package/dist/plugins/siwe/index.mjs +7 -6
- package/dist/plugins/siwe/index.mjs.map +1 -1
- package/dist/plugins/two-factor/backup-codes/index.mjs +7 -7
- package/dist/plugins/two-factor/backup-codes/index.mjs.map +1 -1
- package/dist/plugins/two-factor/client.d.mts +39 -0
- package/dist/plugins/two-factor/client.mjs +4 -1
- package/dist/plugins/two-factor/client.mjs.map +1 -1
- package/dist/plugins/two-factor/error-code.d.mts +36 -9
- package/dist/plugins/two-factor/index.d.mts +54 -27
- package/dist/plugins/two-factor/index.mjs +4 -5
- package/dist/plugins/two-factor/index.mjs.map +1 -1
- package/dist/plugins/two-factor/otp/index.mjs +8 -6
- package/dist/plugins/two-factor/otp/index.mjs.map +1 -1
- package/dist/plugins/two-factor/totp/index.mjs +16 -8
- package/dist/plugins/two-factor/totp/index.mjs.map +1 -1
- package/dist/plugins/two-factor/verify-two-factor.mjs +9 -6
- package/dist/plugins/two-factor/verify-two-factor.mjs.map +1 -1
- package/dist/plugins/username/client.d.mts +35 -0
- package/dist/plugins/username/client.mjs +4 -1
- package/dist/plugins/username/client.mjs.map +1 -1
- package/dist/plugins/username/error-codes.d.mts +32 -8
- package/dist/plugins/username/index.d.mts +41 -17
- package/dist/plugins/username/index.mjs +21 -31
- package/dist/plugins/username/index.mjs.map +1 -1
- package/dist/plugins/username/schema.d.mts +3 -3
- package/dist/test-utils/test-instance.d.mts +1349 -1198
- package/dist/utils/is-api-error.d.mts +7 -0
- package/dist/utils/is-api-error.mjs +11 -0
- package/dist/utils/is-api-error.mjs.map +1 -0
- package/dist/utils/password.mjs +3 -3
- package/dist/utils/password.mjs.map +1 -1
- package/dist/utils/plugin-helper.mjs +2 -2
- package/dist/utils/plugin-helper.mjs.map +1 -1
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/plugins/two-factor/totp/index.ts"],"sourcesContent":["import { createAuthEndpoint } from \"@better-auth/core/api\";\nimport { BASE_ERROR_CODES } from \"@better-auth/core/error\";\nimport { createOTP } from \"@better-auth/utils/otp\";\nimport { APIError } from \"better-call\";\nimport * as z from \"zod\";\nimport { sessionMiddleware } from \"../../../api\";\nimport { setSessionCookie } from \"../../../cookies\";\nimport { symmetricDecrypt } from \"../../../crypto\";\nimport type { BackupCodeOptions } from \"../backup-codes\";\nimport { TWO_FACTOR_ERROR_CODES } from \"../error-code\";\nimport type {\n\tTwoFactorProvider,\n\tTwoFactorTable,\n\tUserWithTwoFactor,\n} from \"../types\";\nimport { verifyTwoFactor } from \"../verify-two-factor\";\n\nexport type TOTPOptions = {\n\t/**\n\t * Issuer\n\t */\n\tissuer?: string | undefined;\n\t/**\n\t * How many digits the otp to be\n\t *\n\t * @default 6\n\t */\n\tdigits?: (6 | 8) | undefined;\n\t/**\n\t * Period for otp in seconds.\n\t * @default 30\n\t */\n\tperiod?: number | undefined;\n\t/**\n\t * Backup codes configuration\n\t */\n\tbackupCodes?: BackupCodeOptions | undefined;\n\t/**\n\t * Disable totp\n\t */\n\tdisable?: boolean | undefined;\n};\n\nconst generateTOTPBodySchema = z.object({\n\tsecret: z.string().meta({\n\t\tdescription: \"The secret to generate the TOTP code\",\n\t}),\n});\n\nconst getTOTPURIBodySchema = z.object({\n\tpassword: z.string().meta({\n\t\tdescription: \"User password\",\n\t}),\n});\n\nconst verifyTOTPBodySchema = z.object({\n\tcode: z.string().meta({\n\t\tdescription: 'The otp code to verify. Eg: \"012345\"',\n\t}),\n\t/**\n\t * if true, the device will be trusted\n\t * for 30 days. It'll be refreshed on\n\t * every sign in request within this time.\n\t */\n\ttrustDevice: z\n\t\t.boolean()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"If true, the device will be trusted for 30 days. It'll be refreshed on every sign in request within this time. Eg: true\",\n\t\t})\n\t\t.optional(),\n});\n\nexport const totp2fa = (options?: TOTPOptions | undefined) => {\n\tconst opts = {\n\t\t...options,\n\t\tdigits: options?.digits || 6,\n\t\tperiod: options?.period || 30,\n\t};\n\n\tconst twoFactorTable = \"twoFactor\";\n\n\tconst generateTOTP = createAuthEndpoint(\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: generateTOTPBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tsummary: \"Generate TOTP code\",\n\t\t\t\t\tdescription: \"Use this endpoint to generate a TOTP code\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t200: {\n\t\t\t\t\t\t\tdescription: \"Successful response\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tcode: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tif (options?.disable) {\n\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\"totp isn't configured. please pass totp option on two factor plugin to enable totp\",\n\t\t\t\t);\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"totp isn't configured\",\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst code = await createOTP(ctx.body.secret, {\n\t\t\t\tperiod: opts.period,\n\t\t\t\tdigits: opts.digits,\n\t\t\t}).totp();\n\t\t\treturn { code };\n\t\t},\n\t);\n\n\tconst getTOTPURI = createAuthEndpoint(\n\t\t\"/two-factor/get-totp-uri\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tuse: [sessionMiddleware],\n\t\t\tbody: getTOTPURIBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tsummary: \"Get TOTP URI\",\n\t\t\t\t\tdescription: \"Use this endpoint to get the TOTP URI\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t200: {\n\t\t\t\t\t\t\tdescription: \"Successful response\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\ttotpURI: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tif (options?.disable) {\n\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\"totp isn't configured. please pass totp option on two factor plugin to enable totp\",\n\t\t\t\t);\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"totp isn't configured\",\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst user = ctx.context.session.user as UserWithTwoFactor;\n\t\t\tconst twoFactor = await ctx.context.adapter.findOne<TwoFactorTable>({\n\t\t\t\tmodel: twoFactorTable,\n\t\t\t\twhere: [\n\t\t\t\t\t{\n\t\t\t\t\t\tfield: \"userId\",\n\t\t\t\t\t\tvalue: user.id,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\t\t\tif (!twoFactor) {\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: TWO_FACTOR_ERROR_CODES.TOTP_NOT_ENABLED,\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst secret = await symmetricDecrypt({\n\t\t\t\tkey: ctx.context.secret,\n\t\t\t\tdata: twoFactor.secret,\n\t\t\t});\n\t\t\tawait ctx.context.password.checkPassword(user.id, ctx);\n\t\t\tconst totpURI = createOTP(secret, {\n\t\t\t\tdigits: opts.digits,\n\t\t\t\tperiod: opts.period,\n\t\t\t}).url(options?.issuer || ctx.context.appName, user.email);\n\t\t\treturn {\n\t\t\t\ttotpURI,\n\t\t\t};\n\t\t},\n\t);\n\n\tconst verifyTOTP = createAuthEndpoint(\n\t\t\"/two-factor/verify-totp\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: verifyTOTPBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tsummary: \"Verify two factor TOTP\",\n\t\t\t\t\tdescription: \"Verify two factor TOTP\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t200: {\n\t\t\t\t\t\t\tdescription: \"Successful response\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tstatus: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"boolean\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tif (options?.disable) {\n\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\"totp isn't configured. please pass totp option on two factor plugin to enable totp\",\n\t\t\t\t);\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"totp isn't configured\",\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst { session, valid, invalid } = await verifyTwoFactor(ctx);\n\t\t\tconst user = session.user as UserWithTwoFactor;\n\t\t\tconst twoFactor = await ctx.context.adapter.findOne<TwoFactorTable>({\n\t\t\t\tmodel: twoFactorTable,\n\t\t\t\twhere: [\n\t\t\t\t\t{\n\t\t\t\t\t\tfield: \"userId\",\n\t\t\t\t\t\tvalue: user.id,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\n\t\t\tif (!twoFactor) {\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: TWO_FACTOR_ERROR_CODES.TOTP_NOT_ENABLED,\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst decrypted = await symmetricDecrypt({\n\t\t\t\tkey: ctx.context.secret,\n\t\t\t\tdata: twoFactor.secret,\n\t\t\t});\n\t\t\tconst status = await createOTP(decrypted, {\n\t\t\t\tperiod: opts.period,\n\t\t\t\tdigits: opts.digits,\n\t\t\t}).verify(ctx.body.code);\n\t\t\tif (!status) {\n\t\t\t\treturn invalid(\"INVALID_CODE\");\n\t\t\t}\n\n\t\t\tif (!user.twoFactorEnabled) {\n\t\t\t\tif (!session.session) {\n\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\tmessage: BASE_ERROR_CODES.FAILED_TO_CREATE_SESSION,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tconst updatedUser = await ctx.context.internalAdapter.updateUser(\n\t\t\t\t\tuser.id,\n\t\t\t\t\t{\n\t\t\t\t\t\ttwoFactorEnabled: true,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t\tconst newSession = await ctx.context.internalAdapter\n\t\t\t\t\t.createSession(user.id, false, session.session)\n\t\t\t\t\t.catch((e) => {\n\t\t\t\t\t\tthrow e;\n\t\t\t\t\t});\n\n\t\t\t\tawait ctx.context.internalAdapter.deleteSession(session.session.token);\n\t\t\t\tawait setSessionCookie(ctx, {\n\t\t\t\t\tsession: newSession,\n\t\t\t\t\tuser: updatedUser,\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn valid(ctx);\n\t\t},\n\t);\n\n\treturn {\n\t\tid: \"totp\",\n\t\tendpoints: {\n\t\t\t/**\n\t\t\t * ### Endpoint\n\t\t\t *\n\t\t\t * POST `/totp/generate`\n\t\t\t *\n\t\t\t * ### API Methods\n\t\t\t *\n\t\t\t * **server:**\n\t\t\t * `auth.api.generateTOTP`\n\t\t\t *\n\t\t\t * @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/2fa#totp)\n\t\t\t */\n\t\t\tgenerateTOTP: generateTOTP,\n\t\t\t/**\n\t\t\t * ### Endpoint\n\t\t\t *\n\t\t\t * POST `/two-factor/get-totp-uri`\n\t\t\t *\n\t\t\t * ### API Methods\n\t\t\t *\n\t\t\t * **server:**\n\t\t\t * `auth.api.getTOTPURI`\n\t\t\t *\n\t\t\t * **client:**\n\t\t\t * `authClient.twoFactor.getTotpUri`\n\t\t\t *\n\t\t\t * @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/2fa#getting-totp-uri)\n\t\t\t */\n\t\t\tgetTOTPURI: getTOTPURI,\n\t\t\t/**\n\t\t\t * ### Endpoint\n\t\t\t *\n\t\t\t * POST `/two-factor/verify-totp`\n\t\t\t *\n\t\t\t * ### API Methods\n\t\t\t *\n\t\t\t * **server:**\n\t\t\t * `auth.api.verifyTOTP`\n\t\t\t *\n\t\t\t * **client:**\n\t\t\t * `authClient.twoFactor.verifyTotp`\n\t\t\t *\n\t\t\t * @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/2fa#verifying-totp)\n\t\t\t */\n\t\t\tverifyTOTP,\n\t\t},\n\t} satisfies TwoFactorProvider;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/plugins/two-factor/totp/index.ts"],"sourcesContent":["import { createAuthEndpoint } from \"@better-auth/core/api\";\nimport { APIError, BASE_ERROR_CODES } from \"@better-auth/core/error\";\nimport { createOTP } from \"@better-auth/utils/otp\";\nimport * as z from \"zod\";\nimport { sessionMiddleware } from \"../../../api\";\nimport { setSessionCookie } from \"../../../cookies\";\nimport { symmetricDecrypt } from \"../../../crypto\";\nimport type { BackupCodeOptions } from \"../backup-codes\";\nimport { TWO_FACTOR_ERROR_CODES } from \"../error-code\";\nimport type {\n\tTwoFactorProvider,\n\tTwoFactorTable,\n\tUserWithTwoFactor,\n} from \"../types\";\nimport { verifyTwoFactor } from \"../verify-two-factor\";\n\nexport type TOTPOptions = {\n\t/**\n\t * Issuer\n\t */\n\tissuer?: string | undefined;\n\t/**\n\t * How many digits the otp to be\n\t *\n\t * @default 6\n\t */\n\tdigits?: (6 | 8) | undefined;\n\t/**\n\t * Period for otp in seconds.\n\t * @default 30\n\t */\n\tperiod?: number | undefined;\n\t/**\n\t * Backup codes configuration\n\t */\n\tbackupCodes?: BackupCodeOptions | undefined;\n\t/**\n\t * Disable totp\n\t */\n\tdisable?: boolean | undefined;\n};\n\nconst generateTOTPBodySchema = z.object({\n\tsecret: z.string().meta({\n\t\tdescription: \"The secret to generate the TOTP code\",\n\t}),\n});\n\nconst getTOTPURIBodySchema = z.object({\n\tpassword: z.string().meta({\n\t\tdescription: \"User password\",\n\t}),\n});\n\nconst verifyTOTPBodySchema = z.object({\n\tcode: z.string().meta({\n\t\tdescription: 'The otp code to verify. Eg: \"012345\"',\n\t}),\n\t/**\n\t * if true, the device will be trusted\n\t * for 30 days. It'll be refreshed on\n\t * every sign in request within this time.\n\t */\n\ttrustDevice: z\n\t\t.boolean()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"If true, the device will be trusted for 30 days. It'll be refreshed on every sign in request within this time. Eg: true\",\n\t\t})\n\t\t.optional(),\n});\n\nexport const totp2fa = (options?: TOTPOptions | undefined) => {\n\tconst opts = {\n\t\t...options,\n\t\tdigits: options?.digits || 6,\n\t\tperiod: options?.period || 30,\n\t};\n\n\tconst twoFactorTable = \"twoFactor\";\n\n\tconst generateTOTP = createAuthEndpoint(\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: generateTOTPBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tsummary: \"Generate TOTP code\",\n\t\t\t\t\tdescription: \"Use this endpoint to generate a TOTP code\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t200: {\n\t\t\t\t\t\t\tdescription: \"Successful response\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tcode: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tif (options?.disable) {\n\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\"totp isn't configured. please pass totp option on two factor plugin to enable totp\",\n\t\t\t\t);\n\t\t\t\tthrow APIError.from(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"totp isn't configured\",\n\t\t\t\t\tcode: \"TOTP_NOT_CONFIGURED\",\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst code = await createOTP(ctx.body.secret, {\n\t\t\t\tperiod: opts.period,\n\t\t\t\tdigits: opts.digits,\n\t\t\t}).totp();\n\t\t\treturn { code };\n\t\t},\n\t);\n\n\tconst getTOTPURI = createAuthEndpoint(\n\t\t\"/two-factor/get-totp-uri\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tuse: [sessionMiddleware],\n\t\t\tbody: getTOTPURIBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tsummary: \"Get TOTP URI\",\n\t\t\t\t\tdescription: \"Use this endpoint to get the TOTP URI\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t200: {\n\t\t\t\t\t\t\tdescription: \"Successful response\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\ttotpURI: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tif (options?.disable) {\n\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\"totp isn't configured. please pass totp option on two factor plugin to enable totp\",\n\t\t\t\t);\n\t\t\t\tthrow APIError.from(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"totp isn't configured\",\n\t\t\t\t\tcode: \"TOTP_NOT_CONFIGURED\",\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst user = ctx.context.session.user as UserWithTwoFactor;\n\t\t\tconst twoFactor = await ctx.context.adapter.findOne<TwoFactorTable>({\n\t\t\t\tmodel: twoFactorTable,\n\t\t\t\twhere: [\n\t\t\t\t\t{\n\t\t\t\t\t\tfield: \"userId\",\n\t\t\t\t\t\tvalue: user.id,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\t\t\tif (!twoFactor) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tTWO_FACTOR_ERROR_CODES.TOTP_NOT_ENABLED,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst secret = await symmetricDecrypt({\n\t\t\t\tkey: ctx.context.secret,\n\t\t\t\tdata: twoFactor.secret,\n\t\t\t});\n\t\t\tawait ctx.context.password.checkPassword(user.id, ctx);\n\t\t\tconst totpURI = createOTP(secret, {\n\t\t\t\tdigits: opts.digits,\n\t\t\t\tperiod: opts.period,\n\t\t\t}).url(options?.issuer || ctx.context.appName, user.email);\n\t\t\treturn {\n\t\t\t\ttotpURI,\n\t\t\t};\n\t\t},\n\t);\n\n\tconst verifyTOTP = createAuthEndpoint(\n\t\t\"/two-factor/verify-totp\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: verifyTOTPBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tsummary: \"Verify two factor TOTP\",\n\t\t\t\t\tdescription: \"Verify two factor TOTP\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t200: {\n\t\t\t\t\t\t\tdescription: \"Successful response\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tstatus: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"boolean\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tif (options?.disable) {\n\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\"totp isn't configured. please pass totp option on two factor plugin to enable totp\",\n\t\t\t\t);\n\t\t\t\tthrow APIError.from(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"totp isn't configured\",\n\t\t\t\t\tcode: \"TOTP_NOT_CONFIGURED\",\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst { session, valid, invalid } = await verifyTwoFactor(ctx);\n\t\t\tconst user = session.user as UserWithTwoFactor;\n\t\t\tconst twoFactor = await ctx.context.adapter.findOne<TwoFactorTable>({\n\t\t\t\tmodel: twoFactorTable,\n\t\t\t\twhere: [\n\t\t\t\t\t{\n\t\t\t\t\t\tfield: \"userId\",\n\t\t\t\t\t\tvalue: user.id,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\n\t\t\tif (!twoFactor) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tTWO_FACTOR_ERROR_CODES.TOTP_NOT_ENABLED,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst decrypted = await symmetricDecrypt({\n\t\t\t\tkey: ctx.context.secret,\n\t\t\t\tdata: twoFactor.secret,\n\t\t\t});\n\t\t\tconst status = await createOTP(decrypted, {\n\t\t\t\tperiod: opts.period,\n\t\t\t\tdigits: opts.digits,\n\t\t\t}).verify(ctx.body.code);\n\t\t\tif (!status) {\n\t\t\t\treturn invalid(\"INVALID_CODE\");\n\t\t\t}\n\n\t\t\tif (!user.twoFactorEnabled) {\n\t\t\t\tif (!session.session) {\n\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\t\tBASE_ERROR_CODES.FAILED_TO_CREATE_SESSION,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst updatedUser = await ctx.context.internalAdapter.updateUser(\n\t\t\t\t\tuser.id,\n\t\t\t\t\t{\n\t\t\t\t\t\ttwoFactorEnabled: true,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t\tconst newSession = await ctx.context.internalAdapter\n\t\t\t\t\t.createSession(user.id, false, session.session)\n\t\t\t\t\t.catch((e) => {\n\t\t\t\t\t\tthrow e;\n\t\t\t\t\t});\n\n\t\t\t\tawait ctx.context.internalAdapter.deleteSession(session.session.token);\n\t\t\t\tawait setSessionCookie(ctx, {\n\t\t\t\t\tsession: newSession,\n\t\t\t\t\tuser: updatedUser,\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn valid(ctx);\n\t\t},\n\t);\n\n\treturn {\n\t\tid: \"totp\",\n\t\tendpoints: {\n\t\t\t/**\n\t\t\t * ### Endpoint\n\t\t\t *\n\t\t\t * POST `/totp/generate`\n\t\t\t *\n\t\t\t * ### API Methods\n\t\t\t *\n\t\t\t * **server:**\n\t\t\t * `auth.api.generateTOTP`\n\t\t\t *\n\t\t\t * @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/2fa#totp)\n\t\t\t */\n\t\t\tgenerateTOTP: generateTOTP,\n\t\t\t/**\n\t\t\t * ### Endpoint\n\t\t\t *\n\t\t\t * POST `/two-factor/get-totp-uri`\n\t\t\t *\n\t\t\t * ### API Methods\n\t\t\t *\n\t\t\t * **server:**\n\t\t\t * `auth.api.getTOTPURI`\n\t\t\t *\n\t\t\t * **client:**\n\t\t\t * `authClient.twoFactor.getTotpUri`\n\t\t\t *\n\t\t\t * @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/2fa#getting-totp-uri)\n\t\t\t */\n\t\t\tgetTOTPURI: getTOTPURI,\n\t\t\t/**\n\t\t\t * ### Endpoint\n\t\t\t *\n\t\t\t * POST `/two-factor/verify-totp`\n\t\t\t *\n\t\t\t * ### API Methods\n\t\t\t *\n\t\t\t * **server:**\n\t\t\t * `auth.api.verifyTOTP`\n\t\t\t *\n\t\t\t * **client:**\n\t\t\t * `authClient.twoFactor.verifyTotp`\n\t\t\t *\n\t\t\t * @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/2fa#verifying-totp)\n\t\t\t */\n\t\t\tverifyTOTP,\n\t\t},\n\t} satisfies TwoFactorProvider;\n};\n"],"mappings":"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"}
|
|
@@ -3,28 +3,31 @@ import { getSessionFromCtx } from "../../api/routes/session.mjs";
|
|
|
3
3
|
import "../../api/index.mjs";
|
|
4
4
|
import { TWO_FACTOR_ERROR_CODES } from "./error-code.mjs";
|
|
5
5
|
import { TRUST_DEVICE_COOKIE_MAX_AGE, TRUST_DEVICE_COOKIE_NAME, TWO_FACTOR_COOKIE_NAME } from "./constant.mjs";
|
|
6
|
-
import { APIError } from "better-
|
|
6
|
+
import { APIError } from "@better-auth/core/error";
|
|
7
7
|
import { createHMAC } from "@better-auth/utils/hmac";
|
|
8
8
|
|
|
9
9
|
//#region src/plugins/two-factor/verify-two-factor.ts
|
|
10
10
|
async function verifyTwoFactor(ctx) {
|
|
11
11
|
const invalid = (errorKey) => {
|
|
12
|
-
throw
|
|
12
|
+
throw APIError.from("UNAUTHORIZED", TWO_FACTOR_ERROR_CODES[errorKey]);
|
|
13
13
|
};
|
|
14
14
|
const session = await getSessionFromCtx(ctx);
|
|
15
15
|
if (!session) {
|
|
16
16
|
const cookieName = ctx.context.createAuthCookie(TWO_FACTOR_COOKIE_NAME);
|
|
17
17
|
const twoFactorCookie = await ctx.getSignedCookie(cookieName.name, ctx.context.secret);
|
|
18
|
-
if (!twoFactorCookie) throw
|
|
18
|
+
if (!twoFactorCookie) throw APIError.from("UNAUTHORIZED", TWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE);
|
|
19
19
|
const verificationToken = await ctx.context.internalAdapter.findVerificationValue(twoFactorCookie);
|
|
20
|
-
if (!verificationToken) throw
|
|
20
|
+
if (!verificationToken) throw APIError.from("UNAUTHORIZED", TWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE);
|
|
21
21
|
const user = await ctx.context.internalAdapter.findUserById(verificationToken.value);
|
|
22
|
-
if (!user) throw
|
|
22
|
+
if (!user) throw APIError.from("UNAUTHORIZED", TWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE);
|
|
23
23
|
const dontRememberMe = await ctx.getSignedCookie(ctx.context.authCookies.dontRememberToken.name, ctx.context.secret);
|
|
24
24
|
return {
|
|
25
25
|
valid: async (ctx$1) => {
|
|
26
26
|
const session$1 = await ctx$1.context.internalAdapter.createSession(verificationToken.value, !!dontRememberMe);
|
|
27
|
-
if (!session$1) throw
|
|
27
|
+
if (!session$1) throw APIError.from("INTERNAL_SERVER_ERROR", {
|
|
28
|
+
message: "failed to create session",
|
|
29
|
+
code: "FAILED_TO_CREATE_SESSION"
|
|
30
|
+
});
|
|
28
31
|
await ctx$1.context.internalAdapter.deleteVerificationValue(verificationToken.id);
|
|
29
32
|
await setSessionCookie(ctx$1, {
|
|
30
33
|
session: session$1,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify-two-factor.mjs","names":["session","ctx"],"sources":["../../../src/plugins/two-factor/verify-two-factor.ts"],"sourcesContent":["import type { GenericEndpointContext } from \"@better-auth/core\";\nimport {
|
|
1
|
+
{"version":3,"file":"verify-two-factor.mjs","names":["session","ctx"],"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 { setSessionCookie } from \"../../cookies\";\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 cookieName = ctx.context.createAuthCookie(TWO_FACTOR_COOKIE_NAME);\n\t\tconst twoFactorCookie = await ctx.getSignedCookie(\n\t\t\tcookieName.name,\n\t\t\tctx.context.secret,\n\t\t);\n\t\tif (!twoFactorCookie) {\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(twoFactorCookie);\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.deleteVerificationValue(\n\t\t\t\t\tverificationToken.id,\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\tctx.setCookie(cookieName.name, \"\", {\n\t\t\t\t\tmaxAge: 0,\n\t\t\t\t});\n\t\t\t\tif (ctx.body.trustDevice) {\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: TRUST_DEVICE_COOKIE_MAX_AGE,\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 token that will be used to\n\t\t\t\t\t * verify the device\n\t\t\t\t\t */\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}!${session.token}`,\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}!${session.token}`,\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\tctx.setCookie(ctx.context.authCookies.dontRememberToken.name, \"\", {\n\t\t\t\t\t\tmaxAge: 0,\n\t\t\t\t\t});\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: {\n\t\t\t\t\t\tid: user.id,\n\t\t\t\t\t\temail: user.email,\n\t\t\t\t\t\temailVerified: user.emailVerified,\n\t\t\t\t\t\tname: user.name,\n\t\t\t\t\t\timage: user.image,\n\t\t\t\t\t\tcreatedAt: user.createdAt,\n\t\t\t\t\t\tupdatedAt: user.updatedAt,\n\t\t\t\t\t},\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: twoFactorCookie,\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: {\n\t\t\t\t\tid: session.user.id,\n\t\t\t\t\temail: session.user.email,\n\t\t\t\t\temailVerified: session.user.emailVerified,\n\t\t\t\t\tname: session.user.name,\n\t\t\t\t\timage: session.user.image,\n\t\t\t\t\tcreatedAt: session.user.createdAt,\n\t\t\t\t\tupdatedAt: session.user.updatedAt,\n\t\t\t\t},\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":";;;;;;;;;AAaA,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,aAAa,IAAI,QAAQ,iBAAiB,uBAAuB;EACvE,MAAM,kBAAkB,MAAM,IAAI,gBACjC,WAAW,MACX,IAAI,QAAQ,OACZ;AACD,MAAI,CAAC,gBACJ,OAAM,SAAS,KACd,gBACA,uBAAuB,0BACvB;EAEF,MAAM,oBACL,MAAM,IAAI,QAAQ,gBAAgB,sBAAsB,gBAAgB;AACzE,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,UAAgC;IAC7C,MAAMA,YAAU,MAAMC,MAAI,QAAQ,gBAAgB,cACjD,kBAAkB,OAClB,CAAC,CAAC,eACF;AACD,QAAI,CAACD,UACJ,OAAM,SAAS,KAAK,yBAAyB;KAC5C,SAAS;KACT,MAAM;KACN,CAAC;AAGH,UAAMC,MAAI,QAAQ,gBAAgB,wBACjC,kBAAkB,GAClB;AACD,UAAM,iBAAiBA,OAAK;KAC3B;KACA;KACA,CAAC;AAEF,UAAI,UAAU,WAAW,MAAM,IAAI,EAClC,QAAQ,GACR,CAAC;AACF,QAAIA,MAAI,KAAK,aAAa;KACzB,MAAM,oBAAoBA,MAAI,QAAQ,iBACrC,0BACA,EACC,QAAQ,6BACR,CACD;;;;;KAKD,MAAM,QAAQ,MAAM,WAAW,WAAW,iBAAiB,CAAC,KAC3DA,MAAI,QAAQ,QACZ,GAAG,KAAK,GAAG,GAAGD,UAAQ,QACtB;AACD,WAAMC,MAAI,gBACT,kBAAkB,MAClB,GAAG,MAAM,GAAGD,UAAQ,SACpBC,MAAI,QAAQ,QACZ,kBAAkB,WAClB;AAED,WAAI,UAAUA,MAAI,QAAQ,YAAY,kBAAkB,MAAM,IAAI,EACjE,QAAQ,GACR,CAAC;;AAEH,WAAOA,MAAI,KAAK;KACf,OAAOD,UAAQ;KACf,MAAM;MACL,IAAI,KAAK;MACT,OAAO,KAAK;MACZ,eAAe,KAAK;MACpB,MAAM,KAAK;MACX,OAAO,KAAK;MACZ,WAAW,KAAK;MAChB,WAAW,KAAK;MAChB;KACD,CAAC;;GAEH;GACA,SAAS;IACR,SAAS;IACT;IACA;GACD,KAAK;GACL;;AAEF,QAAO;EACN,OAAO,OAAO,UAAgC;AAC7C,UAAOC,MAAI,KAAK;IACf,OAAO,QAAQ,QAAQ;IACvB,MAAM;KACL,IAAI,QAAQ,KAAK;KACjB,OAAO,QAAQ,KAAK;KACpB,eAAe,QAAQ,KAAK;KAC5B,MAAM,QAAQ,KAAK;KACnB,OAAO,QAAQ,KAAK;KACpB,WAAW,QAAQ,KAAK;KACxB,WAAW,QAAQ,KAAK;KACxB;IACD,CAAC;;EAEH;EACA;EACA,KAAK,GAAG,QAAQ,KAAK,GAAG,GAAG,QAAQ,QAAQ;EAC3C"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { USERNAME_ERROR_CODES } from "./error-codes.mjs";
|
|
1
2
|
import { username } from "./index.mjs";
|
|
2
3
|
|
|
3
4
|
//#region src/plugins/username/client.d.ts
|
|
@@ -8,6 +9,40 @@ declare const usernameClient: () => {
|
|
|
8
9
|
matcher: (path: string) => path is "/sign-in/username";
|
|
9
10
|
signal: "$sessionSignal";
|
|
10
11
|
}[];
|
|
12
|
+
$ERROR_CODES: {
|
|
13
|
+
readonly INVALID_USERNAME_OR_PASSWORD: {
|
|
14
|
+
code: "INVALID_USERNAME_OR_PASSWORD";
|
|
15
|
+
message: "Invalid username or password";
|
|
16
|
+
};
|
|
17
|
+
readonly EMAIL_NOT_VERIFIED: {
|
|
18
|
+
code: "EMAIL_NOT_VERIFIED";
|
|
19
|
+
message: "Email not verified";
|
|
20
|
+
};
|
|
21
|
+
readonly UNEXPECTED_ERROR: {
|
|
22
|
+
code: "UNEXPECTED_ERROR";
|
|
23
|
+
message: "Unexpected error";
|
|
24
|
+
};
|
|
25
|
+
readonly USERNAME_IS_ALREADY_TAKEN: {
|
|
26
|
+
code: "USERNAME_IS_ALREADY_TAKEN";
|
|
27
|
+
message: "Username is already taken. Please try another.";
|
|
28
|
+
};
|
|
29
|
+
readonly USERNAME_TOO_SHORT: {
|
|
30
|
+
code: "USERNAME_TOO_SHORT";
|
|
31
|
+
message: "Username is too short";
|
|
32
|
+
};
|
|
33
|
+
readonly USERNAME_TOO_LONG: {
|
|
34
|
+
code: "USERNAME_TOO_LONG";
|
|
35
|
+
message: "Username is too long";
|
|
36
|
+
};
|
|
37
|
+
readonly INVALID_USERNAME: {
|
|
38
|
+
code: "INVALID_USERNAME";
|
|
39
|
+
message: "Username is invalid";
|
|
40
|
+
};
|
|
41
|
+
readonly INVALID_DISPLAY_USERNAME: {
|
|
42
|
+
code: "INVALID_DISPLAY_USERNAME";
|
|
43
|
+
message: "Display username is invalid";
|
|
44
|
+
};
|
|
45
|
+
};
|
|
11
46
|
};
|
|
12
47
|
//#endregion
|
|
13
48
|
export { usernameClient };
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { USERNAME_ERROR_CODES } from "./error-codes.mjs";
|
|
2
|
+
|
|
1
3
|
//#region src/plugins/username/client.ts
|
|
2
4
|
const usernameClient = () => {
|
|
3
5
|
return {
|
|
@@ -6,7 +8,8 @@ const usernameClient = () => {
|
|
|
6
8
|
atomListeners: [{
|
|
7
9
|
matcher: (path) => path === "/sign-in/username",
|
|
8
10
|
signal: "$sessionSignal"
|
|
9
|
-
}]
|
|
11
|
+
}],
|
|
12
|
+
$ERROR_CODES: USERNAME_ERROR_CODES
|
|
10
13
|
};
|
|
11
14
|
};
|
|
12
15
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.mjs","names":[],"sources":["../../../src/plugins/username/client.ts"],"sourcesContent":["import type { BetterAuthClientPlugin } from \"@better-auth/core\";\nimport type { username } from \".\";\n\nexport const usernameClient = () => {\n\treturn {\n\t\tid: \"username\",\n\t\t$InferServerPlugin: {} as ReturnType<typeof username>,\n\t\tatomListeners: [\n\t\t\t{\n\t\t\t\tmatcher: (path) => path === \"/sign-in/username\",\n\t\t\t\tsignal: \"$sessionSignal\",\n\t\t\t},\n\t\t],\n\t} satisfies BetterAuthClientPlugin;\n};\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"client.mjs","names":[],"sources":["../../../src/plugins/username/client.ts"],"sourcesContent":["import type { BetterAuthClientPlugin } from \"@better-auth/core\";\nimport type { username } from \".\";\n\nimport { USERNAME_ERROR_CODES } from \"./error-codes\";\n\nexport * from \"./error-codes\";\n\nexport const usernameClient = () => {\n\treturn {\n\t\tid: \"username\",\n\t\t$InferServerPlugin: {} as ReturnType<typeof username>,\n\t\tatomListeners: [\n\t\t\t{\n\t\t\t\tmatcher: (path) => path === \"/sign-in/username\",\n\t\t\t\tsignal: \"$sessionSignal\",\n\t\t\t},\n\t\t],\n\t\t$ERROR_CODES: USERNAME_ERROR_CODES,\n\t} satisfies BetterAuthClientPlugin;\n};\n"],"mappings":";;;AAOA,MAAa,uBAAuB;AACnC,QAAO;EACN,IAAI;EACJ,oBAAoB,EAAE;EACtB,eAAe,CACd;GACC,UAAU,SAAS,SAAS;GAC5B,QAAQ;GACR,CACD;EACD,cAAc;EACd"}
|
|
@@ -1,13 +1,37 @@
|
|
|
1
1
|
//#region src/plugins/username/error-codes.d.ts
|
|
2
2
|
declare const USERNAME_ERROR_CODES: {
|
|
3
|
-
readonly INVALID_USERNAME_OR_PASSWORD:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
readonly
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
readonly INVALID_USERNAME_OR_PASSWORD: {
|
|
4
|
+
code: "INVALID_USERNAME_OR_PASSWORD";
|
|
5
|
+
message: "Invalid username or password";
|
|
6
|
+
};
|
|
7
|
+
readonly EMAIL_NOT_VERIFIED: {
|
|
8
|
+
code: "EMAIL_NOT_VERIFIED";
|
|
9
|
+
message: "Email not verified";
|
|
10
|
+
};
|
|
11
|
+
readonly UNEXPECTED_ERROR: {
|
|
12
|
+
code: "UNEXPECTED_ERROR";
|
|
13
|
+
message: "Unexpected error";
|
|
14
|
+
};
|
|
15
|
+
readonly USERNAME_IS_ALREADY_TAKEN: {
|
|
16
|
+
code: "USERNAME_IS_ALREADY_TAKEN";
|
|
17
|
+
message: "Username is already taken. Please try another.";
|
|
18
|
+
};
|
|
19
|
+
readonly USERNAME_TOO_SHORT: {
|
|
20
|
+
code: "USERNAME_TOO_SHORT";
|
|
21
|
+
message: "Username is too short";
|
|
22
|
+
};
|
|
23
|
+
readonly USERNAME_TOO_LONG: {
|
|
24
|
+
code: "USERNAME_TOO_LONG";
|
|
25
|
+
message: "Username is too long";
|
|
26
|
+
};
|
|
27
|
+
readonly INVALID_USERNAME: {
|
|
28
|
+
code: "INVALID_USERNAME";
|
|
29
|
+
message: "Username is invalid";
|
|
30
|
+
};
|
|
31
|
+
readonly INVALID_DISPLAY_USERNAME: {
|
|
32
|
+
code: "INVALID_DISPLAY_USERNAME";
|
|
33
|
+
message: "Display username is invalid";
|
|
34
|
+
};
|
|
11
35
|
};
|
|
12
36
|
//#endregion
|
|
13
37
|
export { USERNAME_ERROR_CODES };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { InferOptionSchema } from "../../types/plugins.mjs";
|
|
2
2
|
import { UsernameSchema } from "./schema.mjs";
|
|
3
3
|
import { USERNAME_ERROR_CODES } from "./error-codes.mjs";
|
|
4
|
-
import * as
|
|
4
|
+
import * as _better_auth_core16 from "@better-auth/core";
|
|
5
5
|
import * as _better_auth_core_db5 from "@better-auth/core/db";
|
|
6
6
|
import * as z from "zod";
|
|
7
|
-
import * as
|
|
7
|
+
import * as better_call112 from "better-call";
|
|
8
8
|
|
|
9
9
|
//#region src/plugins/username/index.d.ts
|
|
10
10
|
type UsernameOptions = {
|
|
@@ -67,7 +67,7 @@ type UsernameOptions = {
|
|
|
67
67
|
};
|
|
68
68
|
declare const username: (options?: UsernameOptions | undefined) => {
|
|
69
69
|
id: "username";
|
|
70
|
-
init(ctx:
|
|
70
|
+
init(ctx: _better_auth_core16.AuthContext): {
|
|
71
71
|
options: {
|
|
72
72
|
databaseHooks: {
|
|
73
73
|
user: {
|
|
@@ -80,7 +80,7 @@ declare const username: (options?: UsernameOptions | undefined) => {
|
|
|
80
80
|
emailVerified: boolean;
|
|
81
81
|
name: string;
|
|
82
82
|
image?: string | null | undefined;
|
|
83
|
-
} & Record<string, unknown>, context:
|
|
83
|
+
} & Record<string, unknown>, context: _better_auth_core16.GenericEndpointContext | null): Promise<{
|
|
84
84
|
data: {
|
|
85
85
|
displayUsername?: string | undefined;
|
|
86
86
|
username?: string | undefined;
|
|
@@ -103,7 +103,7 @@ declare const username: (options?: UsernameOptions | undefined) => {
|
|
|
103
103
|
emailVerified: boolean;
|
|
104
104
|
name: string;
|
|
105
105
|
image?: string | null | undefined;
|
|
106
|
-
}> & Record<string, unknown>, context:
|
|
106
|
+
}> & Record<string, unknown>, context: _better_auth_core16.GenericEndpointContext | null): Promise<{
|
|
107
107
|
data: {
|
|
108
108
|
displayUsername?: string | undefined;
|
|
109
109
|
username?: string | undefined;
|
|
@@ -122,7 +122,7 @@ declare const username: (options?: UsernameOptions | undefined) => {
|
|
|
122
122
|
};
|
|
123
123
|
};
|
|
124
124
|
endpoints: {
|
|
125
|
-
signInUsername:
|
|
125
|
+
signInUsername: better_call112.StrictEndpoint<"/sign-in/username", {
|
|
126
126
|
method: "POST";
|
|
127
127
|
body: z.ZodObject<{
|
|
128
128
|
username: z.ZodString;
|
|
@@ -187,7 +187,7 @@ declare const username: (options?: UsernameOptions | undefined) => {
|
|
|
187
187
|
updatedAt: Date;
|
|
188
188
|
};
|
|
189
189
|
} | null>;
|
|
190
|
-
isUsernameAvailable:
|
|
190
|
+
isUsernameAvailable: better_call112.StrictEndpoint<"/is-username-available", {
|
|
191
191
|
method: "POST";
|
|
192
192
|
body: z.ZodObject<{
|
|
193
193
|
username: z.ZodString;
|
|
@@ -221,20 +221,44 @@ declare const username: (options?: UsernameOptions | undefined) => {
|
|
|
221
221
|
};
|
|
222
222
|
hooks: {
|
|
223
223
|
before: {
|
|
224
|
-
matcher(context:
|
|
225
|
-
handler: (inputContext:
|
|
224
|
+
matcher(context: _better_auth_core16.HookEndpointContext): boolean;
|
|
225
|
+
handler: (inputContext: better_call112.MiddlewareInputContext<better_call112.MiddlewareOptions>) => Promise<void>;
|
|
226
226
|
}[];
|
|
227
227
|
};
|
|
228
228
|
options: UsernameOptions | undefined;
|
|
229
229
|
$ERROR_CODES: {
|
|
230
|
-
readonly INVALID_USERNAME_OR_PASSWORD:
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
readonly
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
230
|
+
readonly INVALID_USERNAME_OR_PASSWORD: {
|
|
231
|
+
code: "INVALID_USERNAME_OR_PASSWORD";
|
|
232
|
+
message: "Invalid username or password";
|
|
233
|
+
};
|
|
234
|
+
readonly EMAIL_NOT_VERIFIED: {
|
|
235
|
+
code: "EMAIL_NOT_VERIFIED";
|
|
236
|
+
message: "Email not verified";
|
|
237
|
+
};
|
|
238
|
+
readonly UNEXPECTED_ERROR: {
|
|
239
|
+
code: "UNEXPECTED_ERROR";
|
|
240
|
+
message: "Unexpected error";
|
|
241
|
+
};
|
|
242
|
+
readonly USERNAME_IS_ALREADY_TAKEN: {
|
|
243
|
+
code: "USERNAME_IS_ALREADY_TAKEN";
|
|
244
|
+
message: "Username is already taken. Please try another.";
|
|
245
|
+
};
|
|
246
|
+
readonly USERNAME_TOO_SHORT: {
|
|
247
|
+
code: "USERNAME_TOO_SHORT";
|
|
248
|
+
message: "Username is too short";
|
|
249
|
+
};
|
|
250
|
+
readonly USERNAME_TOO_LONG: {
|
|
251
|
+
code: "USERNAME_TOO_LONG";
|
|
252
|
+
message: "Username is too long";
|
|
253
|
+
};
|
|
254
|
+
readonly INVALID_USERNAME: {
|
|
255
|
+
code: "INVALID_USERNAME";
|
|
256
|
+
message: "Username is invalid";
|
|
257
|
+
};
|
|
258
|
+
readonly INVALID_DISPLAY_USERNAME: {
|
|
259
|
+
code: "INVALID_DISPLAY_USERNAME";
|
|
260
|
+
message: "Display username is invalid";
|
|
261
|
+
};
|
|
238
262
|
};
|
|
239
263
|
};
|
|
240
264
|
//#endregion
|
|
@@ -5,9 +5,8 @@ import { createEmailVerificationToken } from "../../api/routes/email-verificatio
|
|
|
5
5
|
import "../../api/index.mjs";
|
|
6
6
|
import { USERNAME_ERROR_CODES } from "./error-codes.mjs";
|
|
7
7
|
import { getSchema } from "./schema.mjs";
|
|
8
|
-
import { BASE_ERROR_CODES } from "@better-auth/core/error";
|
|
8
|
+
import { APIError, BASE_ERROR_CODES } from "@better-auth/core/error";
|
|
9
9
|
import * as z from "zod";
|
|
10
|
-
import { APIError } from "better-call";
|
|
11
10
|
import { createAuthEndpoint, createAuthMiddleware } from "@better-auth/core/api";
|
|
12
11
|
|
|
13
12
|
//#region src/plugins/username/index.ts
|
|
@@ -88,23 +87,20 @@ const username = (options) => {
|
|
|
88
87
|
}, async (ctx) => {
|
|
89
88
|
if (!ctx.body.username || !ctx.body.password) {
|
|
90
89
|
ctx.context.logger.error("Username or password not found");
|
|
91
|
-
throw
|
|
90
|
+
throw APIError.from("UNAUTHORIZED", USERNAME_ERROR_CODES.INVALID_USERNAME_OR_PASSWORD);
|
|
92
91
|
}
|
|
93
92
|
const username$1 = options?.validationOrder?.username === "pre-normalization" ? normalizer(ctx.body.username) : ctx.body.username;
|
|
94
93
|
const minUsernameLength = options?.minUsernameLength || 3;
|
|
95
94
|
const maxUsernameLength = options?.maxUsernameLength || 30;
|
|
96
95
|
if (username$1.length < minUsernameLength) {
|
|
97
96
|
ctx.context.logger.error("Username too short", { username: username$1 });
|
|
98
|
-
throw
|
|
99
|
-
code: "USERNAME_TOO_SHORT",
|
|
100
|
-
message: USERNAME_ERROR_CODES.USERNAME_TOO_SHORT
|
|
101
|
-
});
|
|
97
|
+
throw APIError.from("UNPROCESSABLE_ENTITY", USERNAME_ERROR_CODES.USERNAME_TOO_SHORT);
|
|
102
98
|
}
|
|
103
99
|
if (username$1.length > maxUsernameLength) {
|
|
104
100
|
ctx.context.logger.error("Username too long", { username: username$1 });
|
|
105
|
-
throw
|
|
101
|
+
throw APIError.from("UNPROCESSABLE_ENTITY", USERNAME_ERROR_CODES.USERNAME_TOO_LONG);
|
|
106
102
|
}
|
|
107
|
-
if (!await (options?.usernameValidator || defaultUsernameValidator)(username$1)) throw
|
|
103
|
+
if (!await (options?.usernameValidator || defaultUsernameValidator)(username$1)) throw APIError.from("UNPROCESSABLE_ENTITY", USERNAME_ERROR_CODES.INVALID_USERNAME);
|
|
108
104
|
const user = await ctx.context.adapter.findOne({
|
|
109
105
|
model: "user",
|
|
110
106
|
where: [{
|
|
@@ -115,7 +111,7 @@ const username = (options) => {
|
|
|
115
111
|
if (!user) {
|
|
116
112
|
await ctx.context.password.hash(ctx.body.password);
|
|
117
113
|
ctx.context.logger.error("User not found", { username: username$1 });
|
|
118
|
-
throw
|
|
114
|
+
throw APIError.from("UNAUTHORIZED", USERNAME_ERROR_CODES.INVALID_USERNAME_OR_PASSWORD);
|
|
119
115
|
}
|
|
120
116
|
const account = await ctx.context.adapter.findOne({
|
|
121
117
|
model: "account",
|
|
@@ -127,21 +123,21 @@ const username = (options) => {
|
|
|
127
123
|
value: "credential"
|
|
128
124
|
}]
|
|
129
125
|
});
|
|
130
|
-
if (!account) throw
|
|
126
|
+
if (!account) throw APIError.from("UNAUTHORIZED", USERNAME_ERROR_CODES.INVALID_USERNAME_OR_PASSWORD);
|
|
131
127
|
const currentPassword = account?.password;
|
|
132
128
|
if (!currentPassword) {
|
|
133
129
|
ctx.context.logger.error("Password not found", { username: username$1 });
|
|
134
|
-
throw
|
|
130
|
+
throw APIError.from("UNAUTHORIZED", USERNAME_ERROR_CODES.INVALID_USERNAME_OR_PASSWORD);
|
|
135
131
|
}
|
|
136
132
|
if (!await ctx.context.password.verify({
|
|
137
133
|
hash: currentPassword,
|
|
138
134
|
password: ctx.body.password
|
|
139
135
|
})) {
|
|
140
136
|
ctx.context.logger.error("Invalid password");
|
|
141
|
-
throw
|
|
137
|
+
throw APIError.from("UNAUTHORIZED", USERNAME_ERROR_CODES.INVALID_USERNAME_OR_PASSWORD);
|
|
142
138
|
}
|
|
143
139
|
if (ctx.context.options?.emailAndPassword?.requireEmailVerification && !user.emailVerified) {
|
|
144
|
-
if (!ctx.context.options?.emailVerification?.sendVerificationEmail) throw
|
|
140
|
+
if (!ctx.context.options?.emailVerification?.sendVerificationEmail) throw APIError.from("FORBIDDEN", USERNAME_ERROR_CODES.EMAIL_NOT_VERIFIED);
|
|
145
141
|
if (ctx.context.options?.emailVerification?.sendOnSignIn) {
|
|
146
142
|
const token = await createEmailVerificationToken(ctx.context.secret, user.email, void 0, ctx.context.options.emailVerification?.expiresIn);
|
|
147
143
|
const url = `${ctx.context.baseURL}/verify-email?token=${token}&callbackURL=${ctx.body.callbackURL || "/"}`;
|
|
@@ -151,12 +147,12 @@ const username = (options) => {
|
|
|
151
147
|
token
|
|
152
148
|
}, ctx.request));
|
|
153
149
|
}
|
|
154
|
-
throw
|
|
150
|
+
throw APIError.from("FORBIDDEN", USERNAME_ERROR_CODES.EMAIL_NOT_VERIFIED);
|
|
155
151
|
}
|
|
156
152
|
const session = await ctx.context.internalAdapter.createSession(user.id, ctx.body.rememberMe === false);
|
|
157
153
|
if (!session) return ctx.json(null, {
|
|
158
154
|
status: 500,
|
|
159
|
-
body: { message: BASE_ERROR_CODES.FAILED_TO_CREATE_SESSION }
|
|
155
|
+
body: { message: BASE_ERROR_CODES.FAILED_TO_CREATE_SESSION.message }
|
|
160
156
|
});
|
|
161
157
|
await setSessionCookie(ctx, {
|
|
162
158
|
session,
|
|
@@ -182,15 +178,12 @@ const username = (options) => {
|
|
|
182
178
|
body: isUsernameAvailableBodySchema
|
|
183
179
|
}, async (ctx) => {
|
|
184
180
|
const username$1 = ctx.body.username;
|
|
185
|
-
if (!username$1) throw
|
|
181
|
+
if (!username$1) throw APIError.from("UNPROCESSABLE_ENTITY", USERNAME_ERROR_CODES.INVALID_USERNAME);
|
|
186
182
|
const minUsernameLength = options?.minUsernameLength || 3;
|
|
187
183
|
const maxUsernameLength = options?.maxUsernameLength || 30;
|
|
188
|
-
if (username$1.length < minUsernameLength) throw
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
});
|
|
192
|
-
if (username$1.length > maxUsernameLength) throw new APIError("UNPROCESSABLE_ENTITY", { message: USERNAME_ERROR_CODES.USERNAME_TOO_LONG });
|
|
193
|
-
if (!await (options?.usernameValidator || defaultUsernameValidator)(username$1)) throw new APIError("UNPROCESSABLE_ENTITY", { message: USERNAME_ERROR_CODES.INVALID_USERNAME });
|
|
184
|
+
if (username$1.length < minUsernameLength) throw APIError.from("UNPROCESSABLE_ENTITY", USERNAME_ERROR_CODES.USERNAME_TOO_SHORT);
|
|
185
|
+
if (username$1.length > maxUsernameLength) throw APIError.from("UNPROCESSABLE_ENTITY", USERNAME_ERROR_CODES.USERNAME_TOO_LONG);
|
|
186
|
+
if (!await (options?.usernameValidator || defaultUsernameValidator)(username$1)) throw APIError.from("UNPROCESSABLE_ENTITY", USERNAME_ERROR_CODES.INVALID_USERNAME);
|
|
194
187
|
if (await ctx.context.adapter.findOne({
|
|
195
188
|
model: "user",
|
|
196
189
|
where: [{
|
|
@@ -214,12 +207,9 @@ const username = (options) => {
|
|
|
214
207
|
if (username$1 !== void 0 && typeof username$1 === "string") {
|
|
215
208
|
const minUsernameLength = options?.minUsernameLength || 3;
|
|
216
209
|
const maxUsernameLength = options?.maxUsernameLength || 30;
|
|
217
|
-
if (username$1.length < minUsernameLength) throw
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
});
|
|
221
|
-
if (username$1.length > maxUsernameLength) throw new APIError("BAD_REQUEST", { message: USERNAME_ERROR_CODES.USERNAME_TOO_LONG });
|
|
222
|
-
if (!await (options?.usernameValidator || defaultUsernameValidator)(username$1)) throw new APIError("BAD_REQUEST", { message: USERNAME_ERROR_CODES.INVALID_USERNAME });
|
|
210
|
+
if (username$1.length < minUsernameLength) throw APIError.from("BAD_REQUEST", USERNAME_ERROR_CODES.USERNAME_TOO_SHORT);
|
|
211
|
+
if (username$1.length > maxUsernameLength) throw APIError.from("BAD_REQUEST", USERNAME_ERROR_CODES.USERNAME_TOO_LONG);
|
|
212
|
+
if (!await (options?.usernameValidator || defaultUsernameValidator)(username$1)) throw APIError.from("BAD_REQUEST", USERNAME_ERROR_CODES.INVALID_USERNAME);
|
|
223
213
|
const user = await ctx.context.adapter.findOne({
|
|
224
214
|
model: "user",
|
|
225
215
|
where: [{
|
|
@@ -229,12 +219,12 @@ const username = (options) => {
|
|
|
229
219
|
});
|
|
230
220
|
const blockChangeSignUp = ctx.path === "/sign-up/email" && user;
|
|
231
221
|
const blockChangeUpdateUser = ctx.path === "/update-user" && user && ctx.context.session && user.id !== ctx.context.session.session.userId;
|
|
232
|
-
if (blockChangeSignUp || blockChangeUpdateUser) throw
|
|
222
|
+
if (blockChangeSignUp || blockChangeUpdateUser) throw APIError.from("BAD_REQUEST", USERNAME_ERROR_CODES.USERNAME_IS_ALREADY_TAKEN);
|
|
233
223
|
}
|
|
234
224
|
const displayUsername = typeof ctx.body.displayUsername === "string" && options?.validationOrder?.displayUsername === "post-normalization" ? displayUsernameNormalizer(ctx.body.displayUsername) : ctx.body.displayUsername;
|
|
235
225
|
if (displayUsername !== void 0 && typeof displayUsername === "string") {
|
|
236
226
|
if (options?.displayUsernameValidator) {
|
|
237
|
-
if (!await options.displayUsernameValidator(displayUsername)) throw
|
|
227
|
+
if (!await options.displayUsernameValidator(displayUsername)) throw APIError.from("BAD_REQUEST", USERNAME_ERROR_CODES.INVALID_DISPLAY_USERNAME);
|
|
238
228
|
}
|
|
239
229
|
}
|
|
240
230
|
})
|