better-auth 1.7.12 → 1.7.14
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/adapters/index.d.mts +5 -10
- package/dist/api/dispatch.d.mts +3 -5
- package/dist/api/dispatch.mjs +6 -4
- package/dist/api/index.d.mts +506 -508
- package/dist/api/index.mjs +35 -34
- package/dist/api/middlewares/authorization.d.mts +15 -12
- package/dist/api/middlewares/authorization.mjs +2 -1
- package/dist/api/middlewares/index.d.mts +2 -1
- package/dist/api/middlewares/origin-check.d.mts +4 -7
- package/dist/api/routes/account.d.mts +11 -15
- package/dist/api/routes/account.mjs +7 -4
- package/dist/api/routes/callback.d.mts +2 -5
- package/dist/api/routes/callback.mjs +4 -2
- package/dist/api/routes/email-verification.d.mts +7 -12
- package/dist/api/routes/error.d.mts +2 -5
- package/dist/api/routes/index.d.mts +2 -1
- package/dist/api/routes/ok.d.mts +2 -5
- package/dist/api/routes/password.d.mts +8 -11
- package/dist/api/routes/password.mjs +3 -2
- package/dist/api/routes/session.d.mts +26 -29
- package/dist/api/routes/sign-in.d.mts +4 -6
- package/dist/api/routes/sign-out.d.mts +2 -5
- package/dist/api/routes/sign-up.d.mts +4 -6
- package/dist/api/routes/update-session.d.mts +4 -6
- package/dist/api/routes/update-user.d.mts +14 -16
- package/dist/api/routes/update-user.mjs +1 -1
- package/dist/api/state/oauth.d.mts +0 -3
- package/dist/api/state/oauth.mjs +2 -1
- package/dist/auth/full.d.mts +3 -4
- package/dist/auth/minimal.d.mts +3 -3
- package/dist/client/broadcast-channel.d.mts +6 -7
- package/dist/client/config.d.mts +15 -19
- package/dist/client/config.mjs +2 -2
- package/dist/client/equality.d.mts +3 -5
- package/dist/client/focus-manager.d.mts +4 -5
- package/dist/client/index.d.mts +8 -7
- package/dist/client/lynx/index.d.mts +6 -7
- package/dist/client/lynx/index.mjs +3 -2
- package/dist/client/lynx/lynx-store.d.mts +3 -4
- package/dist/client/online-manager.d.mts +4 -5
- package/dist/client/parser.d.mts +9 -5
- package/dist/client/path-to-object.d.mts +13 -14
- package/dist/client/plugins/index.d.mts +1 -1
- package/dist/client/plugins/infer-plugin.d.mts +2 -4
- package/dist/client/query.d.mts +4 -6
- package/dist/client/react/index.d.mts +6 -7
- package/dist/client/react/index.mjs +3 -2
- package/dist/client/react/react-store.d.mts +3 -4
- package/dist/client/session-atom.d.mts +4 -4
- package/dist/client/session-refresh.d.mts +3 -5
- package/dist/client/solid/index.d.mts +6 -7
- package/dist/client/solid/index.mjs +3 -2
- package/dist/client/svelte/index.d.mts +6 -7
- package/dist/client/svelte/index.mjs +3 -2
- package/dist/client/types.d.mts +11 -11
- package/dist/client/vanilla.d.mts +4 -5
- package/dist/client/vanilla.mjs +3 -2
- package/dist/client/vue/index.d.mts +6 -7
- package/dist/client/vue/index.mjs +3 -2
- package/dist/context/create-context.mjs +7 -7
- package/dist/context/helpers.mjs +9 -7
- package/dist/cookies/cookie-utils.d.mts +13 -13
- package/dist/cookies/cookie-utils.mjs +1 -3
- package/dist/cookies/index.d.mts +17 -18
- package/dist/cookies/index.mjs +11 -7
- package/dist/cookies/jwt.mjs +2 -1
- package/dist/cookies/session-store.d.mts +6 -5
- package/dist/cookies/session-store.mjs +3 -2
- package/dist/crypto/buffer.d.mts +2 -3
- package/dist/crypto/index.d.mts +9 -16
- package/dist/crypto/index.mjs +2 -1
- package/dist/crypto/jwt.d.mts +5 -7
- package/dist/crypto/jwt.mjs +2 -1
- package/dist/crypto/password.d.mts +1 -5
- package/dist/crypto/random.d.mts +2 -3
- package/dist/db/adapter-base.d.mts +2 -4
- package/dist/db/adapter-kysely.d.mts +2 -4
- package/dist/db/field-converter.d.mts +3 -5
- package/dist/db/field.d.mts +11 -12
- package/dist/db/get-migration.d.mts +4 -8
- package/dist/db/get-migration.mjs +22 -22
- package/dist/db/get-schema.d.mts +2 -4
- package/dist/db/internal-adapter.d.mts +2 -4
- package/dist/db/internal-adapter.mjs +6 -4
- package/dist/db/revoke-unproven-account-access.d.mts +3 -4
- package/dist/db/schema.d.mts +15 -16
- package/dist/db/schema.mjs +6 -3
- package/dist/db/to-zod.d.mts +5 -9
- package/dist/db/with-hooks.d.mts +3 -5
- package/dist/index.d.mts +4 -3
- package/dist/integrations/next-js.d.mts +7 -11
- package/dist/integrations/node.d.mts +5 -7
- package/dist/integrations/node.mjs +4 -2
- package/dist/integrations/solid-start.d.mts +2 -3
- package/dist/integrations/svelte-kit.d.mts +6 -14
- package/dist/integrations/tanstack-start-solid.d.mts +4 -8
- package/dist/integrations/tanstack-start.d.mts +4 -8
- package/dist/oauth2/link-account.d.mts +5 -11
- package/dist/oauth2/link-account.mjs +12 -6
- package/dist/oauth2/state.d.mts +5 -7
- package/dist/oauth2/state.mjs +1 -1
- package/dist/oauth2/utils.d.mts +4 -6
- package/dist/package.mjs +1 -1
- package/dist/plugins/access/access.d.mts +4 -6
- package/dist/plugins/access/index.mjs +1 -2
- package/dist/plugins/access/types.d.mts +12 -14
- package/dist/plugins/additional-fields/client.d.mts +2 -4
- package/dist/plugins/admin/access/index.mjs +1 -2
- package/dist/plugins/admin/access/statement.d.mts +7 -7
- package/dist/plugins/admin/admin.d.mts +122 -128
- package/dist/plugins/admin/client.d.mts +32 -32
- package/dist/plugins/admin/client.mjs +1 -1
- package/dist/plugins/admin/error-codes.d.mts +25 -28
- package/dist/plugins/admin/index.d.mts +1 -1
- package/dist/plugins/admin/index.mjs +1 -2
- package/dist/plugins/admin/schema.d.mts +3 -4
- package/dist/plugins/admin/types.d.mts +8 -8
- package/dist/plugins/anonymous/client.d.mts +10 -12
- package/dist/plugins/anonymous/client.mjs +1 -1
- package/dist/plugins/anonymous/error-codes.d.mts +10 -13
- package/dist/plugins/anonymous/index.d.mts +17 -21
- package/dist/plugins/anonymous/schema.d.mts +2 -3
- package/dist/plugins/anonymous/types.d.mts +5 -6
- package/dist/plugins/bearer/index.d.mts +7 -11
- package/dist/plugins/captcha/constants.d.mts +2 -3
- package/dist/plugins/captcha/index.d.mts +6 -9
- package/dist/plugins/captcha/types.d.mts +11 -13
- package/dist/plugins/custom-session/client.d.mts +2 -4
- package/dist/plugins/custom-session/index.d.mts +10 -16
- package/dist/plugins/device-authorization/client.d.mts +2 -4
- package/dist/plugins/device-authorization/index.d.mts +39 -44
- package/dist/plugins/device-authorization/index.mjs +10 -9
- package/dist/plugins/device-authorization/routes.d.mts +7 -5
- package/dist/plugins/device-authorization/routes.mjs +6 -5
- package/dist/plugins/device-authorization/schema.d.mts +2 -3
- package/dist/plugins/email-otp/client.d.mts +5 -7
- package/dist/plugins/email-otp/client.mjs +1 -1
- package/dist/plugins/email-otp/error-codes.d.mts +5 -8
- package/dist/plugins/email-otp/index.d.mts +69 -76
- package/dist/plugins/email-otp/otp-token.mjs +19 -7
- package/dist/plugins/email-otp/types.d.mts +2 -4
- package/dist/plugins/generic-oauth/index.d.mts +9 -13
- package/dist/plugins/generic-oauth/index.mjs +6 -4
- package/dist/plugins/generic-oauth/providers/auth0.d.mts +3 -5
- package/dist/plugins/generic-oauth/providers/gumroad.d.mts +3 -5
- package/dist/plugins/generic-oauth/providers/hubspot.d.mts +3 -5
- package/dist/plugins/generic-oauth/providers/hubspot.mjs +2 -1
- package/dist/plugins/generic-oauth/providers/index.d.mts +2 -1
- package/dist/plugins/generic-oauth/providers/keycloak.d.mts +3 -5
- package/dist/plugins/generic-oauth/providers/line.d.mts +3 -5
- package/dist/plugins/generic-oauth/providers/microsoft-entra-id.d.mts +3 -5
- package/dist/plugins/generic-oauth/providers/okta.d.mts +3 -5
- package/dist/plugins/generic-oauth/providers/patreon.d.mts +3 -5
- package/dist/plugins/generic-oauth/providers/slack.d.mts +3 -5
- package/dist/plugins/generic-oauth/providers/yandex.d.mts +3 -5
- package/dist/plugins/generic-oauth/types.d.mts +6 -7
- package/dist/plugins/haveibeenpwned/index.d.mts +7 -11
- package/dist/plugins/index.d.mts +4 -1
- package/dist/plugins/jwt/client.d.mts +2 -3
- package/dist/plugins/jwt/index.d.mts +11 -14
- package/dist/plugins/jwt/index.mjs +1 -1
- package/dist/plugins/jwt/schema.d.mts +2 -3
- package/dist/plugins/jwt/sign.d.mts +9 -8
- package/dist/plugins/jwt/types.d.mts +7 -8
- package/dist/plugins/jwt/utils.d.mts +6 -9
- package/dist/plugins/jwt/verify.d.mts +2 -4
- package/dist/plugins/last-login-method/client.d.mts +3 -4
- package/dist/plugins/last-login-method/index.d.mts +5 -9
- package/dist/plugins/last-login-method/index.mjs +1 -1
- package/dist/plugins/magic-link/client.d.mts +2 -4
- package/dist/plugins/magic-link/index.d.mts +7 -10
- package/dist/plugins/magic-link/index.mjs +20 -17
- package/dist/plugins/multi-session/client.d.mts +3 -5
- package/dist/plugins/multi-session/client.mjs +1 -1
- package/dist/plugins/multi-session/error-codes.d.mts +3 -6
- package/dist/plugins/multi-session/index.d.mts +10 -14
- package/dist/plugins/oauth-popup/client.d.mts +11 -17
- package/dist/plugins/oauth-popup/client.mjs +6 -4
- package/dist/plugins/oauth-popup/constants.d.mts +5 -6
- package/dist/plugins/oauth-popup/error-codes.d.mts +6 -9
- package/dist/plugins/oauth-popup/index.d.mts +11 -15
- package/dist/plugins/oauth-popup/index.mjs +8 -7
- package/dist/plugins/oauth-popup/types.d.mts +4 -6
- package/dist/plugins/oauth-proxy/index.d.mts +11 -15
- package/dist/plugins/oauth-proxy/index.mjs +10 -7
- package/dist/plugins/one-tap/client.d.mts +6 -9
- package/dist/plugins/one-tap/index.d.mts +4 -7
- package/dist/plugins/one-time-token/client.d.mts +2 -3
- package/dist/plugins/one-time-token/index.d.mts +8 -10
- package/dist/plugins/open-api/generator.d.mts +8 -8
- package/dist/plugins/open-api/generator.mjs +5 -5
- package/dist/plugins/open-api/index.d.mts +5 -7
- package/dist/plugins/organization/access/index.mjs +1 -2
- package/dist/plugins/organization/access/statement.d.mts +8 -8
- package/dist/plugins/organization/adapter.d.mts +60 -87
- package/dist/plugins/organization/adapter.mjs +14 -10
- package/dist/plugins/organization/client.d.mts +108 -107
- package/dist/plugins/organization/client.mjs +9 -5
- package/dist/plugins/organization/error-codes.d.mts +61 -64
- package/dist/plugins/organization/has-permission.d.mts +2 -4
- package/dist/plugins/organization/has-permission.mjs +1 -1
- package/dist/plugins/organization/index.d.mts +1 -1
- package/dist/plugins/organization/organization.d.mts +20 -20
- package/dist/plugins/organization/organization.mjs +13 -8
- package/dist/plugins/organization/permission.d.mts +4 -5
- package/dist/plugins/organization/routes/crud-access-control.d.mts +28 -28
- package/dist/plugins/organization/routes/crud-invites.d.mts +64 -65
- package/dist/plugins/organization/routes/crud-invites.mjs +21 -18
- package/dist/plugins/organization/routes/crud-members.d.mts +53 -56
- package/dist/plugins/organization/routes/crud-members.mjs +23 -21
- package/dist/plugins/organization/routes/crud-org.d.mts +63 -65
- package/dist/plugins/organization/routes/crud-org.mjs +4 -2
- package/dist/plugins/organization/routes/crud-team.d.mts +65 -66
- package/dist/plugins/organization/routes/crud-team.mjs +4 -2
- package/dist/plugins/organization/schema.d.mts +33 -33
- package/dist/plugins/organization/schema.mjs +1 -1
- package/dist/plugins/organization/types.d.mts +16 -17
- package/dist/plugins/phone-number/client.d.mts +14 -16
- package/dist/plugins/phone-number/client.mjs +1 -1
- package/dist/plugins/phone-number/error-codes.d.mts +14 -17
- package/dist/plugins/phone-number/index.d.mts +48 -54
- package/dist/plugins/phone-number/schema.d.mts +2 -3
- package/dist/plugins/phone-number/types.d.mts +4 -5
- package/dist/plugins/siwe/client.d.mts +2 -4
- package/dist/plugins/siwe/index.d.mts +7 -9
- package/dist/plugins/siwe/index.mjs +1 -1
- package/dist/plugins/siwe/parse-message.mjs +1 -3
- package/dist/plugins/siwe/schema.d.mts +3 -4
- package/dist/plugins/siwe/types.d.mts +4 -4
- package/dist/plugins/test-utils/auth-helpers.mjs +4 -2
- package/dist/plugins/test-utils/index.d.mts +3 -5
- package/dist/plugins/test-utils/types.d.mts +7 -7
- package/dist/plugins/two-factor/backup-codes/index.d.mts +11 -14
- package/dist/plugins/two-factor/backup-codes/index.mjs +12 -6
- package/dist/plugins/two-factor/client.d.mts +16 -19
- package/dist/plugins/two-factor/client.mjs +1 -1
- package/dist/plugins/two-factor/constant.mjs +1 -1
- package/dist/plugins/two-factor/error-code.d.mts +15 -18
- package/dist/plugins/two-factor/index.d.mts +31 -35
- package/dist/plugins/two-factor/index.mjs +6 -3
- package/dist/plugins/two-factor/otp/index.d.mts +5 -8
- package/dist/plugins/two-factor/otp/index.mjs +159 -154
- package/dist/plugins/two-factor/schema.d.mts +2 -3
- package/dist/plugins/two-factor/totp/index.d.mts +7 -10
- package/dist/plugins/two-factor/totp/index.mjs +164 -148
- package/dist/plugins/two-factor/types.d.mts +6 -7
- package/dist/plugins/username/client.d.mts +12 -14
- package/dist/plugins/username/client.mjs +1 -1
- package/dist/plugins/username/error-codes.d.mts +11 -14
- package/dist/plugins/username/index.d.mts +26 -31
- package/dist/plugins/username/schema.d.mts +7 -10
- package/dist/state.d.mts +4 -5
- package/dist/test-utils/headers.d.mts +2 -3
- package/dist/test-utils/http-test-instance.d.mts +26 -29
- package/dist/test-utils/test-instance.d.mts +7 -6
- package/dist/test-utils/test-instance.mjs +4 -2
- package/dist/types/adapter.d.mts +1 -1
- package/dist/types/api.d.mts +4 -6
- package/dist/types/auth.d.mts +2 -4
- package/dist/types/helper.d.mts +11 -12
- package/dist/types/index.d.mts +2 -2
- package/dist/types/models.d.mts +4 -5
- package/dist/types/plugins.d.mts +7 -8
- package/dist/utils/hide-metadata.d.mts +2 -3
- package/dist/utils/index.d.mts +2 -1
- package/dist/utils/is-api-error.mjs +1 -2
- package/dist/utils/time.d.mts +4 -4
- package/dist/utils/url.d.mts +13 -15
- package/dist/utils/wildcard.mjs +5 -3
- package/package.json +12 -12
|
@@ -57,6 +57,163 @@ const otp2fa = (options) => {
|
|
|
57
57
|
if (typeof opts.storeOTP === "object" && "hash" in opts.storeOTP) return [storedOtp, await opts.storeOTP.hash(userInput)];
|
|
58
58
|
return [storedOtp, userInput];
|
|
59
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Generate OTP and send it to the user.
|
|
62
|
+
*/
|
|
63
|
+
const send2FaOTP = createAuthEndpoint("/two-factor/send-otp", {
|
|
64
|
+
method: "POST",
|
|
65
|
+
body: send2FaOTPBodySchema,
|
|
66
|
+
metadata: { openapi: {
|
|
67
|
+
summary: "Send two factor OTP",
|
|
68
|
+
description: "Send two factor OTP to the user",
|
|
69
|
+
responses: { 200: {
|
|
70
|
+
description: "Successful response",
|
|
71
|
+
content: { "application/json": { schema: {
|
|
72
|
+
type: "object",
|
|
73
|
+
properties: { status: { type: "boolean" } }
|
|
74
|
+
} } }
|
|
75
|
+
} }
|
|
76
|
+
} }
|
|
77
|
+
}, async (ctx) => {
|
|
78
|
+
if (!options || !options.sendOTP) {
|
|
79
|
+
ctx.context.logger.error("send otp isn't configured. Please configure the send otp function on otp options.");
|
|
80
|
+
throw APIError.from("BAD_REQUEST", {
|
|
81
|
+
message: "otp isn't configured",
|
|
82
|
+
code: "OTP_NOT_CONFIGURED"
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
const { session, key } = await verifyTwoFactor(ctx);
|
|
86
|
+
const code = generateRandomString(opts.digits, "0-9");
|
|
87
|
+
const hashedCode = await storeOTP(ctx, code);
|
|
88
|
+
await ctx.context.internalAdapter.createVerificationValue({
|
|
89
|
+
value: `${hashedCode}:0`,
|
|
90
|
+
identifier: `2fa-otp-${key}`,
|
|
91
|
+
expiresAt: new Date(Date.now() + opts.period)
|
|
92
|
+
});
|
|
93
|
+
const sendOTPResult = options.sendOTP({
|
|
94
|
+
user: session.user,
|
|
95
|
+
otp: code
|
|
96
|
+
}, ctx);
|
|
97
|
+
if (sendOTPResult instanceof Promise) await ctx.context.runInBackgroundOrAwait(sendOTPResult.catch((e) => {
|
|
98
|
+
ctx.context.logger.error("Failed to send two-factor OTP", e);
|
|
99
|
+
}));
|
|
100
|
+
return ctx.json({ status: true });
|
|
101
|
+
});
|
|
102
|
+
const verifyOTP = createAuthEndpoint("/two-factor/verify-otp", {
|
|
103
|
+
method: "POST",
|
|
104
|
+
body: verifyOTPBodySchema,
|
|
105
|
+
metadata: { openapi: {
|
|
106
|
+
summary: "Verify two factor OTP",
|
|
107
|
+
description: "Verify two factor OTP",
|
|
108
|
+
responses: { "200": {
|
|
109
|
+
description: "Two-factor OTP verified successfully",
|
|
110
|
+
content: { "application/json": { schema: {
|
|
111
|
+
type: "object",
|
|
112
|
+
properties: {
|
|
113
|
+
token: {
|
|
114
|
+
type: "string",
|
|
115
|
+
description: "Session token for the authenticated session"
|
|
116
|
+
},
|
|
117
|
+
user: {
|
|
118
|
+
type: "object",
|
|
119
|
+
properties: {
|
|
120
|
+
id: {
|
|
121
|
+
type: "string",
|
|
122
|
+
description: "Unique identifier of the user"
|
|
123
|
+
},
|
|
124
|
+
email: {
|
|
125
|
+
type: "string",
|
|
126
|
+
format: "email",
|
|
127
|
+
nullable: true,
|
|
128
|
+
description: "User's email address"
|
|
129
|
+
},
|
|
130
|
+
emailVerified: {
|
|
131
|
+
type: "boolean",
|
|
132
|
+
nullable: true,
|
|
133
|
+
description: "Whether the email is verified"
|
|
134
|
+
},
|
|
135
|
+
name: {
|
|
136
|
+
type: "string",
|
|
137
|
+
nullable: true,
|
|
138
|
+
description: "User's name"
|
|
139
|
+
},
|
|
140
|
+
image: {
|
|
141
|
+
type: "string",
|
|
142
|
+
format: "uri",
|
|
143
|
+
nullable: true,
|
|
144
|
+
description: "User's profile image URL"
|
|
145
|
+
},
|
|
146
|
+
createdAt: {
|
|
147
|
+
type: "string",
|
|
148
|
+
format: "date-time",
|
|
149
|
+
description: "Timestamp when the user was created"
|
|
150
|
+
},
|
|
151
|
+
updatedAt: {
|
|
152
|
+
type: "string",
|
|
153
|
+
format: "date-time",
|
|
154
|
+
description: "Timestamp when the user was last updated"
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
required: [
|
|
158
|
+
"id",
|
|
159
|
+
"createdAt",
|
|
160
|
+
"updatedAt"
|
|
161
|
+
],
|
|
162
|
+
description: "The authenticated user object"
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
required: ["token", "user"]
|
|
166
|
+
} } }
|
|
167
|
+
} }
|
|
168
|
+
} }
|
|
169
|
+
}, async (ctx) => {
|
|
170
|
+
const { session, key, valid, invalid } = await verifyTwoFactor(ctx);
|
|
171
|
+
const isSignIn = !session.session;
|
|
172
|
+
const twoFactorTable = "twoFactor";
|
|
173
|
+
let twoFactor = null;
|
|
174
|
+
if (isSignIn) {
|
|
175
|
+
twoFactor = await ctx.context.adapter.findOne({
|
|
176
|
+
model: twoFactorTable,
|
|
177
|
+
where: [{
|
|
178
|
+
field: "userId",
|
|
179
|
+
value: session.user.id
|
|
180
|
+
}]
|
|
181
|
+
});
|
|
182
|
+
if (twoFactor) await assertTwoFactorNotLocked(ctx, twoFactorTable, twoFactor);
|
|
183
|
+
}
|
|
184
|
+
const consumed = await ctx.context.internalAdapter.consumeVerificationValue(`2fa-otp-${key}`);
|
|
185
|
+
if (!consumed) throw APIError.from("BAD_REQUEST", TWO_FACTOR_ERROR_CODES.OTP_HAS_EXPIRED);
|
|
186
|
+
const [otp, counter] = consumed.value?.split(":") ?? [];
|
|
187
|
+
const allowedAttempts = options?.allowedAttempts || 5;
|
|
188
|
+
const attempts = parseInt(counter, 10) || 0;
|
|
189
|
+
if (attempts >= allowedAttempts) throw APIError.from("BAD_REQUEST", TWO_FACTOR_ERROR_CODES.TOO_MANY_ATTEMPTS_REQUEST_NEW_CODE);
|
|
190
|
+
const [storedValue, inputValue] = await decryptOrHashForComparison(ctx, otp, ctx.body.code);
|
|
191
|
+
if (constantTimeEqual(new TextEncoder().encode(storedValue), new TextEncoder().encode(inputValue))) {
|
|
192
|
+
if (twoFactor) await resetTwoFactorFailures(ctx, twoFactorTable, twoFactor);
|
|
193
|
+
if (!session.user.twoFactorEnabled) {
|
|
194
|
+
if (!session.session) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.FAILED_TO_CREATE_SESSION);
|
|
195
|
+
const updatedUser = await ctx.context.internalAdapter.updateUser(session.user.id, { twoFactorEnabled: true });
|
|
196
|
+
const newSession = await ctx.context.internalAdapter.createSession(session.user.id, false, session.session);
|
|
197
|
+
await setSessionCookie(ctx, {
|
|
198
|
+
session: newSession,
|
|
199
|
+
user: updatedUser
|
|
200
|
+
});
|
|
201
|
+
await ctx.context.internalAdapter.deleteSession(session.session.token);
|
|
202
|
+
return ctx.json({
|
|
203
|
+
token: newSession.token,
|
|
204
|
+
user: parseUserOutput(ctx.context.options, updatedUser)
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
return valid(ctx);
|
|
208
|
+
}
|
|
209
|
+
await ctx.context.internalAdapter.createVerificationValue({
|
|
210
|
+
value: `${otp}:${attempts + 1}`,
|
|
211
|
+
identifier: `2fa-otp-${key}`,
|
|
212
|
+
expiresAt: consumed.expiresAt
|
|
213
|
+
});
|
|
214
|
+
if (twoFactor) await recordTwoFactorFailure(ctx, twoFactorTable, twoFactor);
|
|
215
|
+
return invalid("INVALID_CODE");
|
|
216
|
+
});
|
|
60
217
|
return {
|
|
61
218
|
id: "otp",
|
|
62
219
|
version: PACKAGE_VERSION,
|
|
@@ -76,45 +233,7 @@ const otp2fa = (options) => {
|
|
|
76
233
|
*
|
|
77
234
|
* @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/2fa#api-method-two-factor-send-otp)
|
|
78
235
|
*/
|
|
79
|
-
sendTwoFactorOTP:
|
|
80
|
-
method: "POST",
|
|
81
|
-
body: send2FaOTPBodySchema,
|
|
82
|
-
metadata: { openapi: {
|
|
83
|
-
summary: "Send two factor OTP",
|
|
84
|
-
description: "Send two factor OTP to the user",
|
|
85
|
-
responses: { 200: {
|
|
86
|
-
description: "Successful response",
|
|
87
|
-
content: { "application/json": { schema: {
|
|
88
|
-
type: "object",
|
|
89
|
-
properties: { status: { type: "boolean" } }
|
|
90
|
-
} } }
|
|
91
|
-
} }
|
|
92
|
-
} }
|
|
93
|
-
}, async (ctx) => {
|
|
94
|
-
if (!options || !options.sendOTP) {
|
|
95
|
-
ctx.context.logger.error("send otp isn't configured. Please configure the send otp function on otp options.");
|
|
96
|
-
throw APIError.from("BAD_REQUEST", {
|
|
97
|
-
message: "otp isn't configured",
|
|
98
|
-
code: "OTP_NOT_CONFIGURED"
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
const { session, key } = await verifyTwoFactor(ctx);
|
|
102
|
-
const code = generateRandomString(opts.digits, "0-9");
|
|
103
|
-
const hashedCode = await storeOTP(ctx, code);
|
|
104
|
-
await ctx.context.internalAdapter.createVerificationValue({
|
|
105
|
-
value: `${hashedCode}:0`,
|
|
106
|
-
identifier: `2fa-otp-${key}`,
|
|
107
|
-
expiresAt: new Date(Date.now() + opts.period)
|
|
108
|
-
});
|
|
109
|
-
const sendOTPResult = options.sendOTP({
|
|
110
|
-
user: session.user,
|
|
111
|
-
otp: code
|
|
112
|
-
}, ctx);
|
|
113
|
-
if (sendOTPResult instanceof Promise) await ctx.context.runInBackgroundOrAwait(sendOTPResult.catch((e) => {
|
|
114
|
-
ctx.context.logger.error("Failed to send two-factor OTP", e);
|
|
115
|
-
}));
|
|
116
|
-
return ctx.json({ status: true });
|
|
117
|
-
}),
|
|
236
|
+
sendTwoFactorOTP: send2FaOTP,
|
|
118
237
|
/**
|
|
119
238
|
* ### Endpoint
|
|
120
239
|
*
|
|
@@ -130,121 +249,7 @@ const otp2fa = (options) => {
|
|
|
130
249
|
*
|
|
131
250
|
* @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/2fa#api-method-two-factor-verify-otp)
|
|
132
251
|
*/
|
|
133
|
-
verifyTwoFactorOTP:
|
|
134
|
-
method: "POST",
|
|
135
|
-
body: verifyOTPBodySchema,
|
|
136
|
-
metadata: { openapi: {
|
|
137
|
-
summary: "Verify two factor OTP",
|
|
138
|
-
description: "Verify two factor OTP",
|
|
139
|
-
responses: { "200": {
|
|
140
|
-
description: "Two-factor OTP verified successfully",
|
|
141
|
-
content: { "application/json": { schema: {
|
|
142
|
-
type: "object",
|
|
143
|
-
properties: {
|
|
144
|
-
token: {
|
|
145
|
-
type: "string",
|
|
146
|
-
description: "Session token for the authenticated session"
|
|
147
|
-
},
|
|
148
|
-
user: {
|
|
149
|
-
type: "object",
|
|
150
|
-
properties: {
|
|
151
|
-
id: {
|
|
152
|
-
type: "string",
|
|
153
|
-
description: "Unique identifier of the user"
|
|
154
|
-
},
|
|
155
|
-
email: {
|
|
156
|
-
type: "string",
|
|
157
|
-
format: "email",
|
|
158
|
-
nullable: true,
|
|
159
|
-
description: "User's email address"
|
|
160
|
-
},
|
|
161
|
-
emailVerified: {
|
|
162
|
-
type: "boolean",
|
|
163
|
-
nullable: true,
|
|
164
|
-
description: "Whether the email is verified"
|
|
165
|
-
},
|
|
166
|
-
name: {
|
|
167
|
-
type: "string",
|
|
168
|
-
nullable: true,
|
|
169
|
-
description: "User's name"
|
|
170
|
-
},
|
|
171
|
-
image: {
|
|
172
|
-
type: "string",
|
|
173
|
-
format: "uri",
|
|
174
|
-
nullable: true,
|
|
175
|
-
description: "User's profile image URL"
|
|
176
|
-
},
|
|
177
|
-
createdAt: {
|
|
178
|
-
type: "string",
|
|
179
|
-
format: "date-time",
|
|
180
|
-
description: "Timestamp when the user was created"
|
|
181
|
-
},
|
|
182
|
-
updatedAt: {
|
|
183
|
-
type: "string",
|
|
184
|
-
format: "date-time",
|
|
185
|
-
description: "Timestamp when the user was last updated"
|
|
186
|
-
}
|
|
187
|
-
},
|
|
188
|
-
required: [
|
|
189
|
-
"id",
|
|
190
|
-
"createdAt",
|
|
191
|
-
"updatedAt"
|
|
192
|
-
],
|
|
193
|
-
description: "The authenticated user object"
|
|
194
|
-
}
|
|
195
|
-
},
|
|
196
|
-
required: ["token", "user"]
|
|
197
|
-
} } }
|
|
198
|
-
} }
|
|
199
|
-
} }
|
|
200
|
-
}, async (ctx) => {
|
|
201
|
-
const { session, key, valid, invalid } = await verifyTwoFactor(ctx);
|
|
202
|
-
const isSignIn = !session.session;
|
|
203
|
-
const twoFactorTable = "twoFactor";
|
|
204
|
-
let twoFactor = null;
|
|
205
|
-
if (isSignIn) {
|
|
206
|
-
twoFactor = await ctx.context.adapter.findOne({
|
|
207
|
-
model: twoFactorTable,
|
|
208
|
-
where: [{
|
|
209
|
-
field: "userId",
|
|
210
|
-
value: session.user.id
|
|
211
|
-
}]
|
|
212
|
-
});
|
|
213
|
-
if (twoFactor) await assertTwoFactorNotLocked(ctx, twoFactorTable, twoFactor);
|
|
214
|
-
}
|
|
215
|
-
const consumed = await ctx.context.internalAdapter.consumeVerificationValue(`2fa-otp-${key}`);
|
|
216
|
-
if (!consumed) throw APIError.from("BAD_REQUEST", TWO_FACTOR_ERROR_CODES.OTP_HAS_EXPIRED);
|
|
217
|
-
const [otp, counter] = consumed.value?.split(":") ?? [];
|
|
218
|
-
const allowedAttempts = options?.allowedAttempts || 5;
|
|
219
|
-
const attempts = parseInt(counter, 10) || 0;
|
|
220
|
-
if (attempts >= allowedAttempts) throw APIError.from("BAD_REQUEST", TWO_FACTOR_ERROR_CODES.TOO_MANY_ATTEMPTS_REQUEST_NEW_CODE);
|
|
221
|
-
const [storedValue, inputValue] = await decryptOrHashForComparison(ctx, otp, ctx.body.code);
|
|
222
|
-
if (constantTimeEqual(new TextEncoder().encode(storedValue), new TextEncoder().encode(inputValue))) {
|
|
223
|
-
if (twoFactor) await resetTwoFactorFailures(ctx, twoFactorTable, twoFactor);
|
|
224
|
-
if (!session.user.twoFactorEnabled) {
|
|
225
|
-
if (!session.session) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.FAILED_TO_CREATE_SESSION);
|
|
226
|
-
const updatedUser = await ctx.context.internalAdapter.updateUser(session.user.id, { twoFactorEnabled: true });
|
|
227
|
-
const newSession = await ctx.context.internalAdapter.createSession(session.user.id, false, session.session);
|
|
228
|
-
await setSessionCookie(ctx, {
|
|
229
|
-
session: newSession,
|
|
230
|
-
user: updatedUser
|
|
231
|
-
});
|
|
232
|
-
await ctx.context.internalAdapter.deleteSession(session.session.token);
|
|
233
|
-
return ctx.json({
|
|
234
|
-
token: newSession.token,
|
|
235
|
-
user: parseUserOutput(ctx.context.options, updatedUser)
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
|
-
return valid(ctx);
|
|
239
|
-
}
|
|
240
|
-
await ctx.context.internalAdapter.createVerificationValue({
|
|
241
|
-
value: `${otp}:${attempts + 1}`,
|
|
242
|
-
identifier: `2fa-otp-${key}`,
|
|
243
|
-
expiresAt: consumed.expiresAt
|
|
244
|
-
});
|
|
245
|
-
if (twoFactor) await recordTwoFactorFailure(ctx, twoFactorTable, twoFactor);
|
|
246
|
-
return invalid("INVALID_CODE");
|
|
247
|
-
})
|
|
252
|
+
verifyTwoFactorOTP: verifyOTP
|
|
248
253
|
}
|
|
249
254
|
};
|
|
250
255
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region src/plugins/two-factor/schema.d.ts
|
|
2
|
-
declare const schema: {
|
|
2
|
+
export declare const schema: {
|
|
3
3
|
user: {
|
|
4
4
|
fields: {
|
|
5
5
|
twoFactorEnabled: {
|
|
@@ -55,5 +55,4 @@ declare const schema: {
|
|
|
55
55
|
};
|
|
56
56
|
};
|
|
57
57
|
};
|
|
58
|
-
//#endregion
|
|
59
|
-
export { schema };
|
|
58
|
+
//#endregion
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { BackupCodeOptions } from "../backup-codes/index.mjs";
|
|
2
2
|
import { UserWithTwoFactor } from "../types.mjs";
|
|
3
|
-
import * as _$better_call0 from "better-call";
|
|
4
3
|
import * as z from "zod";
|
|
5
|
-
|
|
6
4
|
//#region src/plugins/two-factor/totp/index.d.ts
|
|
7
|
-
type TOTPOptions = {
|
|
5
|
+
export type TOTPOptions = {
|
|
8
6
|
/**
|
|
9
7
|
* Issuer
|
|
10
8
|
*/
|
|
@@ -36,7 +34,7 @@ type TOTPOptions = {
|
|
|
36
34
|
*/
|
|
37
35
|
disable?: boolean | undefined;
|
|
38
36
|
};
|
|
39
|
-
declare const totp2fa: (options?: TOTPOptions | undefined) => {
|
|
37
|
+
export declare const totp2fa: (options?: TOTPOptions | undefined) => {
|
|
40
38
|
id: "totp";
|
|
41
39
|
version: string;
|
|
42
40
|
endpoints: {
|
|
@@ -52,7 +50,7 @@ declare const totp2fa: (options?: TOTPOptions | undefined) => {
|
|
|
52
50
|
*
|
|
53
51
|
* @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/2fa#totp)
|
|
54
52
|
*/
|
|
55
|
-
generateTOTP:
|
|
53
|
+
generateTOTP: import("better-call").StrictEndpoint<string, {
|
|
56
54
|
method: "POST";
|
|
57
55
|
body: z.ZodObject<{
|
|
58
56
|
secret: z.ZodString;
|
|
@@ -98,9 +96,9 @@ declare const totp2fa: (options?: TOTPOptions | undefined) => {
|
|
|
98
96
|
*
|
|
99
97
|
* @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/2fa#getting-totp-uri)
|
|
100
98
|
*/
|
|
101
|
-
getTOTPURI:
|
|
99
|
+
getTOTPURI: import("better-call").StrictEndpoint<"/two-factor/get-totp-uri", {
|
|
102
100
|
method: "POST";
|
|
103
|
-
use:
|
|
101
|
+
use: import("better-call").Middleware<import("better-call").MiddlewareOptions, (inputContext: import("better-call").MiddlewareInputContext<import("better-call").MiddlewareOptions>) => Promise<{
|
|
104
102
|
session: {
|
|
105
103
|
session: Record<string, any> & {
|
|
106
104
|
id: string;
|
|
@@ -169,7 +167,7 @@ declare const totp2fa: (options?: TOTPOptions | undefined) => {
|
|
|
169
167
|
*
|
|
170
168
|
* @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/2fa#verifying-totp)
|
|
171
169
|
*/
|
|
172
|
-
verifyTOTP:
|
|
170
|
+
verifyTOTP: import("better-call").StrictEndpoint<"/two-factor/verify-totp", {
|
|
173
171
|
method: "POST";
|
|
174
172
|
body: z.ZodObject<{
|
|
175
173
|
code: z.ZodString;
|
|
@@ -215,5 +213,4 @@ declare const totp2fa: (options?: TOTPOptions | undefined) => {
|
|
|
215
213
|
}>;
|
|
216
214
|
};
|
|
217
215
|
};
|
|
218
|
-
//#endregion
|
|
219
|
-
export { TOTPOptions, totp2fa };
|
|
216
|
+
//#endregion
|