better-auth 1.2.6-beta.7 → 1.2.7-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/adapters/drizzle-adapter/index.cjs +186 -249
- package/dist/adapters/drizzle-adapter/index.d.cts +11 -49
- package/dist/adapters/drizzle-adapter/index.d.mts +11 -49
- package/dist/adapters/drizzle-adapter/index.d.ts +11 -49
- package/dist/adapters/drizzle-adapter/index.mjs +186 -249
- package/dist/adapters/index.cjs +26 -0
- package/dist/adapters/index.d.cts +17 -0
- package/dist/adapters/index.d.mts +17 -0
- package/dist/adapters/index.d.ts +17 -0
- package/dist/adapters/index.mjs +20 -0
- package/dist/adapters/kysely-adapter/index.cjs +7 -7
- package/dist/adapters/kysely-adapter/index.d.cts +17 -49
- package/dist/adapters/kysely-adapter/index.d.mts +17 -49
- package/dist/adapters/kysely-adapter/index.d.ts +17 -49
- package/dist/adapters/kysely-adapter/index.mjs +8 -8
- package/dist/adapters/memory-adapter/index.cjs +7 -8
- package/dist/adapters/memory-adapter/index.d.cts +9 -49
- package/dist/adapters/memory-adapter/index.d.mts +9 -49
- package/dist/adapters/memory-adapter/index.d.ts +9 -49
- package/dist/adapters/memory-adapter/index.mjs +8 -9
- package/dist/adapters/mongodb-adapter/index.cjs +2 -2
- package/dist/adapters/mongodb-adapter/index.d.cts +4 -4
- package/dist/adapters/mongodb-adapter/index.d.mts +4 -4
- package/dist/adapters/mongodb-adapter/index.d.ts +4 -4
- package/dist/adapters/mongodb-adapter/index.mjs +3 -3
- package/dist/adapters/prisma-adapter/index.cjs +130 -203
- package/dist/adapters/prisma-adapter/index.d.cts +17 -49
- package/dist/adapters/prisma-adapter/index.d.mts +17 -49
- package/dist/adapters/prisma-adapter/index.d.ts +17 -49
- package/dist/adapters/prisma-adapter/index.mjs +131 -204
- package/dist/adapters/test.cjs +710 -377
- package/dist/adapters/test.d.cts +64 -5
- package/dist/adapters/test.d.mts +64 -5
- package/dist/adapters/test.d.ts +64 -5
- package/dist/adapters/test.mjs +712 -380
- package/dist/api/index.cjs +61 -25
- package/dist/api/index.d.cts +3 -3
- package/dist/api/index.d.mts +3 -3
- package/dist/api/index.d.ts +3 -3
- package/dist/api/index.mjs +63 -27
- package/dist/client/index.d.cts +3 -3
- package/dist/client/index.d.mts +3 -3
- package/dist/client/index.d.ts +3 -3
- package/dist/client/plugins/index.cjs +13 -15
- package/dist/client/plugins/index.d.cts +80 -19
- package/dist/client/plugins/index.d.mts +80 -19
- package/dist/client/plugins/index.d.ts +80 -19
- package/dist/client/plugins/index.mjs +13 -16
- package/dist/client/react/index.cjs +4 -4
- package/dist/client/react/index.d.cts +3 -3
- package/dist/client/react/index.d.mts +3 -3
- package/dist/client/react/index.d.ts +3 -3
- package/dist/client/solid/index.d.cts +3 -3
- package/dist/client/solid/index.d.mts +3 -3
- package/dist/client/solid/index.d.ts +3 -3
- package/dist/client/svelte/index.d.cts +3 -3
- package/dist/client/svelte/index.d.mts +3 -3
- package/dist/client/svelte/index.d.ts +3 -3
- package/dist/client/vue/index.d.cts +3 -3
- package/dist/client/vue/index.d.mts +3 -3
- package/dist/client/vue/index.d.ts +3 -3
- package/dist/cookies/index.cjs +13 -2
- package/dist/cookies/index.d.cts +3 -3
- package/dist/cookies/index.d.mts +3 -3
- package/dist/cookies/index.d.ts +3 -3
- package/dist/cookies/index.mjs +13 -2
- package/dist/db/index.cjs +6 -5
- package/dist/db/index.d.cts +4 -4
- package/dist/db/index.d.mts +4 -4
- package/dist/db/index.d.ts +4 -4
- package/dist/db/index.mjs +7 -6
- package/dist/index.cjs +11 -7
- package/dist/index.d.cts +4 -4
- package/dist/index.d.mts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.mjs +14 -10
- package/dist/integrations/next-js.cjs +4 -5
- package/dist/integrations/next-js.d.cts +3 -3
- package/dist/integrations/next-js.d.mts +3 -3
- package/dist/integrations/next-js.d.ts +3 -3
- package/dist/integrations/next-js.mjs +5 -6
- package/dist/integrations/node.d.cts +3 -3
- package/dist/integrations/node.d.mts +3 -3
- package/dist/integrations/node.d.ts +3 -3
- package/dist/integrations/react-start.cjs +5 -6
- package/dist/integrations/react-start.d.cts +3 -3
- package/dist/integrations/react-start.d.mts +3 -3
- package/dist/integrations/react-start.d.ts +3 -3
- package/dist/integrations/react-start.mjs +6 -7
- package/dist/integrations/svelte-kit.d.cts +3 -3
- package/dist/integrations/svelte-kit.d.mts +3 -3
- package/dist/integrations/svelte-kit.d.ts +3 -3
- package/dist/oauth2/index.d.cts +5 -5
- package/dist/oauth2/index.d.mts +5 -5
- package/dist/oauth2/index.d.ts +5 -5
- package/dist/plugins/access/index.d.cts +1 -1
- package/dist/plugins/access/index.d.mts +1 -1
- package/dist/plugins/access/index.d.ts +1 -1
- package/dist/plugins/admin/access/index.d.cts +1 -1
- package/dist/plugins/admin/access/index.d.mts +1 -1
- package/dist/plugins/admin/access/index.d.ts +1 -1
- package/dist/plugins/admin/index.cjs +4 -4
- package/dist/plugins/admin/index.d.cts +74 -14
- package/dist/plugins/admin/index.d.mts +74 -14
- package/dist/plugins/admin/index.d.ts +74 -14
- package/dist/plugins/admin/index.mjs +5 -5
- package/dist/plugins/anonymous/index.cjs +4 -5
- package/dist/plugins/anonymous/index.d.cts +3 -3
- package/dist/plugins/anonymous/index.d.mts +3 -3
- package/dist/plugins/anonymous/index.d.ts +3 -3
- package/dist/plugins/anonymous/index.mjs +5 -6
- package/dist/plugins/bearer/index.cjs +2 -2
- package/dist/plugins/bearer/index.d.cts +3 -3
- package/dist/plugins/bearer/index.d.mts +3 -3
- package/dist/plugins/bearer/index.d.ts +3 -3
- package/dist/plugins/bearer/index.mjs +3 -3
- package/dist/plugins/captcha/index.cjs +110 -45
- package/dist/plugins/captcha/index.d.cts +26 -6
- package/dist/plugins/captcha/index.d.mts +26 -6
- package/dist/plugins/captcha/index.d.ts +26 -6
- package/dist/plugins/captcha/index.mjs +110 -45
- package/dist/plugins/custom-session/index.cjs +24 -5
- package/dist/plugins/custom-session/index.d.cts +25 -6
- package/dist/plugins/custom-session/index.d.mts +25 -6
- package/dist/plugins/custom-session/index.d.ts +25 -6
- package/dist/plugins/custom-session/index.mjs +25 -6
- package/dist/plugins/email-otp/index.cjs +96 -30
- package/dist/plugins/email-otp/index.d.cts +33 -10
- package/dist/plugins/email-otp/index.d.mts +33 -10
- package/dist/plugins/email-otp/index.d.ts +33 -10
- package/dist/plugins/email-otp/index.mjs +97 -31
- package/dist/plugins/generic-oauth/index.cjs +81 -20
- package/dist/plugins/generic-oauth/index.d.cts +46 -3
- package/dist/plugins/generic-oauth/index.d.mts +46 -3
- package/dist/plugins/generic-oauth/index.d.ts +46 -3
- package/dist/plugins/generic-oauth/index.mjs +82 -21
- package/dist/plugins/haveibeenpwned/index.cjs +98 -0
- package/dist/plugins/haveibeenpwned/index.d.cts +36 -0
- package/dist/plugins/haveibeenpwned/index.d.mts +36 -0
- package/dist/plugins/haveibeenpwned/index.d.ts +36 -0
- package/dist/plugins/haveibeenpwned/index.mjs +96 -0
- package/dist/plugins/index.cjs +583 -19
- package/dist/plugins/index.d.cts +7 -5
- package/dist/plugins/index.d.mts +7 -5
- package/dist/plugins/index.d.ts +7 -5
- package/dist/plugins/index.mjs +583 -21
- package/dist/plugins/jwt/index.cjs +45 -21
- package/dist/plugins/jwt/index.d.cts +52 -6
- package/dist/plugins/jwt/index.d.mts +52 -6
- package/dist/plugins/jwt/index.d.ts +52 -6
- package/dist/plugins/jwt/index.mjs +46 -22
- package/dist/plugins/magic-link/index.cjs +3 -3
- package/dist/plugins/magic-link/index.mjs +4 -4
- package/dist/plugins/multi-session/index.cjs +3 -3
- package/dist/plugins/multi-session/index.d.cts +3 -3
- package/dist/plugins/multi-session/index.d.mts +3 -3
- package/dist/plugins/multi-session/index.d.ts +3 -3
- package/dist/plugins/multi-session/index.mjs +4 -4
- package/dist/plugins/oauth-proxy/index.cjs +4 -4
- package/dist/plugins/oauth-proxy/index.d.cts +3 -3
- package/dist/plugins/oauth-proxy/index.d.mts +3 -3
- package/dist/plugins/oauth-proxy/index.d.ts +3 -3
- package/dist/plugins/oauth-proxy/index.mjs +5 -5
- package/dist/plugins/oidc-provider/index.cjs +227 -8
- package/dist/plugins/oidc-provider/index.d.cts +215 -3
- package/dist/plugins/oidc-provider/index.d.mts +215 -3
- package/dist/plugins/oidc-provider/index.d.ts +215 -3
- package/dist/plugins/oidc-provider/index.mjs +228 -9
- package/dist/plugins/one-tap/index.cjs +5 -5
- package/dist/plugins/one-tap/index.mjs +6 -6
- package/dist/plugins/one-time-token/index.cjs +119 -0
- package/dist/plugins/one-time-token/index.d.cts +134 -0
- package/dist/plugins/one-time-token/index.d.mts +134 -0
- package/dist/plugins/one-time-token/index.d.ts +134 -0
- package/dist/plugins/one-time-token/index.mjs +117 -0
- package/dist/plugins/open-api/index.cjs +3 -3
- package/dist/plugins/open-api/index.d.cts +3 -3
- package/dist/plugins/open-api/index.d.mts +3 -3
- package/dist/plugins/open-api/index.d.ts +3 -3
- package/dist/plugins/open-api/index.mjs +4 -4
- package/dist/plugins/organization/access/index.d.cts +1 -1
- package/dist/plugins/organization/access/index.d.mts +1 -1
- package/dist/plugins/organization/access/index.d.ts +1 -1
- package/dist/plugins/organization/index.cjs +4 -4
- package/dist/plugins/organization/index.d.cts +708 -55
- package/dist/plugins/organization/index.d.mts +708 -55
- package/dist/plugins/organization/index.d.ts +708 -55
- package/dist/plugins/organization/index.mjs +5 -5
- package/dist/plugins/passkey/index.cjs +82 -8
- package/dist/plugins/passkey/index.d.cts +72 -3
- package/dist/plugins/passkey/index.d.mts +72 -3
- package/dist/plugins/passkey/index.d.ts +72 -3
- package/dist/plugins/passkey/index.mjs +83 -9
- package/dist/plugins/phone-number/index.cjs +194 -26
- package/dist/plugins/phone-number/index.d.cts +132 -8
- package/dist/plugins/phone-number/index.d.mts +132 -8
- package/dist/plugins/phone-number/index.d.ts +132 -8
- package/dist/plugins/phone-number/index.mjs +195 -27
- package/dist/plugins/sso/index.cjs +190 -7
- package/dist/plugins/sso/index.d.cts +181 -15
- package/dist/plugins/sso/index.d.mts +181 -15
- package/dist/plugins/sso/index.d.ts +181 -15
- package/dist/plugins/sso/index.mjs +191 -8
- package/dist/plugins/two-factor/index.cjs +443 -92
- package/dist/plugins/two-factor/index.d.cts +230 -396
- package/dist/plugins/two-factor/index.d.mts +230 -396
- package/dist/plugins/two-factor/index.d.ts +230 -396
- package/dist/plugins/two-factor/index.mjs +431 -80
- package/dist/plugins/username/index.cjs +34 -31
- package/dist/plugins/username/index.d.cts +15 -12
- package/dist/plugins/username/index.d.mts +15 -12
- package/dist/plugins/username/index.d.ts +15 -12
- package/dist/plugins/username/index.mjs +35 -32
- package/dist/shared/better-auth.1DR6suCQ.mjs +307 -0
- package/dist/shared/{better-auth.BSsp73pg.cjs → better-auth.B7cZ2juS.cjs} +15 -14
- package/dist/shared/{better-auth.bKwabe3I.d.mts → better-auth.B88xucNq.d.mts} +529 -39
- package/dist/shared/{better-auth.CApEjVDP.cjs → better-auth.BW8BpneG.cjs} +4 -1
- package/dist/shared/{better-auth.BiQsvaIP.d.cts → better-auth.BcU1Kjyq.d.cts} +2051 -518
- package/dist/shared/better-auth.BfG24BjZ.cjs +118 -0
- package/dist/shared/{better-auth.A3TjrU8G.mjs → better-auth.Bk5IMdhM.mjs} +32 -12
- package/dist/shared/{better-auth.D9VnBkRI.mjs → better-auth.Bm9HxIzE.mjs} +47 -24
- package/dist/shared/{better-auth.BRf6Iynu.d.ts → better-auth.Bwc-6kOr.d.ts} +1 -1
- package/dist/shared/{better-auth.D-oLmHIj.d.mts → better-auth.CA2hFK4N.d.ts} +2051 -518
- package/dist/shared/{better-auth.Dmhe30iW.d.mts → better-auth.CGukGrxT.d.cts} +1 -1
- package/dist/shared/{better-auth.CsSpq0zL.cjs → better-auth.CHUzBidy.cjs} +46 -23
- package/dist/shared/{better-auth.DWRligF8.d.cts → better-auth.CT9J6rD-.d.cts} +539 -7
- package/dist/shared/better-auth.CVCo5Z2T.cjs +310 -0
- package/dist/shared/{better-auth.D4jH-sJA.mjs → better-auth.CWwVo_61.mjs} +458 -118
- package/dist/shared/{better-auth.Bi8FQwDD.d.cts → better-auth.CYegVoq1.d.cts} +1 -1
- package/dist/shared/{better-auth.Bi8FQwDD.d.mts → better-auth.CYegVoq1.d.mts} +1 -1
- package/dist/shared/{better-auth.Bi8FQwDD.d.ts → better-auth.CYegVoq1.d.ts} +1 -1
- package/dist/shared/{better-auth.CepcSj5H.mjs → better-auth.Cc72UxUH.mjs} +1 -2
- package/dist/shared/{better-auth.BWp5dztg.d.ts → better-auth.CmN4mlPh.d.ts} +539 -7
- package/dist/shared/{better-auth.DH3YjMQH.mjs → better-auth.Cqykj82J.mjs} +1 -1
- package/dist/shared/{better-auth.wcdMj2cT.d.mts → better-auth.DIt2e3lu.d.mts} +539 -7
- package/dist/shared/{better-auth.BANAxdkL.d.ts → better-auth.DNTAFSt1.d.ts} +529 -39
- package/dist/shared/{better-auth.DU2QNVc_.d.ts → better-auth.DQ7OSJbI.d.mts} +2051 -518
- package/dist/shared/{better-auth.DLTzKoOS.cjs → better-auth.DSVbLSt7.cjs} +4 -1
- package/dist/shared/{better-auth.B2Fw1vhH.d.cts → better-auth.DTiSPWEk.d.cts} +529 -39
- package/dist/shared/better-auth.DURsStt9.mjs +116 -0
- package/dist/shared/{better-auth.BIjcZ_vt.cjs → better-auth.DYoLD99C.cjs} +31 -11
- package/dist/shared/{better-auth.CV1L7TPV.cjs → better-auth.D_ZIX1O8.cjs} +317 -47
- package/dist/shared/{better-auth.C5H9XEzZ.cjs → better-auth.DcWKCjjf.cjs} +1 -2
- package/dist/shared/{better-auth.BDYXUcLv.cjs → better-auth.Dg0siV5C.cjs} +457 -117
- package/dist/shared/better-auth.DjryM8pE.cjs +760 -0
- package/dist/shared/{better-auth.DPBQN9Fs.mjs → better-auth.Dn_Ms1Uf.mjs} +318 -48
- package/dist/shared/{better-auth.DiG4KL2x.mjs → better-auth.OuYYTHC7.mjs} +4 -1
- package/dist/shared/{better-auth.DtC8i3pf.d.cts → better-auth.S1jimRbX.d.mts} +1 -1
- package/dist/shared/better-auth.SPmq4a4z.d.mts +344 -0
- package/dist/shared/{better-auth.cOCrlspr.mjs → better-auth.bkwPl2G4.mjs} +4 -1
- package/dist/shared/better-auth.cp2rC2iM.d.ts +344 -0
- package/dist/shared/better-auth.eVy4DZvP.d.cts +344 -0
- package/dist/shared/{better-auth.BrOpzmqo.mjs → better-auth.iKoUsdFE.mjs} +15 -14
- package/dist/shared/better-auth.rSYJCd3o.mjs +758 -0
- package/dist/social-providers/index.cjs +75 -3
- package/dist/social-providers/index.d.cts +2 -2
- package/dist/social-providers/index.d.mts +2 -2
- package/dist/social-providers/index.d.ts +2 -2
- package/dist/social-providers/index.mjs +77 -6
- package/dist/types/index.d.cts +4 -4
- package/dist/types/index.d.mts +4 -4
- package/dist/types/index.d.ts +4 -4
- package/package.json +42 -5
- package/dist/chunks/server.cjs +0 -905
- package/dist/chunks/server.mjs +0 -895
- package/dist/shared/better-auth.BcoSd9tC.mjs +0 -10
- package/dist/shared/better-auth.BnRFp-t0.mjs +0 -405
- package/dist/shared/better-auth.C1-vpKly.cjs +0 -12
- package/dist/shared/better-auth.ClTSOgiD.mjs +0 -140
- package/dist/shared/better-auth.DC8JQbiE.mjs +0 -173
- package/dist/shared/better-auth.DWHWPllD.cjs +0 -175
- package/dist/shared/better-auth.DqLjzBlO.cjs +0 -408
- package/dist/shared/better-auth.m575EIBC.cjs +0 -144
|
@@ -2,21 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
const random = require('../../shared/better-auth.CYeOI8C-.cjs');
|
|
4
4
|
const zod = require('zod');
|
|
5
|
-
const refreshToken = require('../../shared/better-auth.
|
|
5
|
+
const refreshToken = require('../../shared/better-auth.Dg0siV5C.cjs');
|
|
6
6
|
const betterCall = require('better-call');
|
|
7
7
|
const cookies_index = require('../../cookies/index.cjs');
|
|
8
|
-
const schema$1 = require('../../shared/better-auth.
|
|
8
|
+
const schema$1 = require('../../shared/better-auth.DcWKCjjf.cjs');
|
|
9
9
|
require('../../shared/better-auth.DiSjtgs9.cjs');
|
|
10
10
|
require('../../shared/better-auth.GpOOav9x.cjs');
|
|
11
11
|
require('defu');
|
|
12
12
|
const crypto_index = require('../../crypto/index.cjs');
|
|
13
|
-
|
|
14
|
-
const
|
|
13
|
+
require('@better-auth/utils/base64');
|
|
14
|
+
const hmac = require('@better-auth/utils/hmac');
|
|
15
15
|
require('@better-auth/utils/hash');
|
|
16
16
|
require('@noble/ciphers/chacha');
|
|
17
17
|
require('@noble/ciphers/utils');
|
|
18
18
|
require('@noble/ciphers/webcrypto');
|
|
19
|
-
require('@better-auth/utils/base64');
|
|
20
19
|
require('jose');
|
|
21
20
|
require('@noble/hashes/scrypt');
|
|
22
21
|
require('@better-auth/utils');
|
|
@@ -24,7 +23,6 @@ require('@better-auth/utils/hex');
|
|
|
24
23
|
require('@noble/hashes/utils');
|
|
25
24
|
const otp = require('@better-auth/utils/otp');
|
|
26
25
|
const password = require('../../shared/better-auth.CDXNofOe.cjs');
|
|
27
|
-
const hmac = require('@better-auth/utils/hmac');
|
|
28
26
|
const client = require('../../shared/better-auth.DnER2-iT.cjs');
|
|
29
27
|
require('@better-auth/utils/random');
|
|
30
28
|
require('../../social-providers/index.cjs');
|
|
@@ -33,14 +31,151 @@ require('../../shared/better-auth.Cm29ZKNJ.cjs');
|
|
|
33
31
|
require('../../shared/better-auth.C1hdVENX.cjs');
|
|
34
32
|
require('../../shared/better-auth.ANpbi45u.cjs');
|
|
35
33
|
require('../../shared/better-auth.QbbyHMYf.cjs');
|
|
34
|
+
require('../../shared/better-auth.D3mtHEZg.cjs');
|
|
36
35
|
require('../../shared/better-auth.Bg6iw3ig.cjs');
|
|
37
36
|
require('../../shared/better-auth.BMYo0QR-.cjs');
|
|
38
37
|
require('../../shared/better-auth.C-R0J0n1.cjs');
|
|
39
38
|
require('jose/errors');
|
|
40
|
-
require('../../shared/better-auth.D3mtHEZg.cjs');
|
|
41
39
|
require('@better-auth/utils/binary');
|
|
42
40
|
require('../../shared/better-auth.YUF6P-PB.cjs');
|
|
43
41
|
|
|
42
|
+
const TWO_FACTOR_ERROR_CODES = {
|
|
43
|
+
OTP_NOT_ENABLED: "OTP not enabled",
|
|
44
|
+
OTP_HAS_EXPIRED: "OTP has expired",
|
|
45
|
+
TOTP_NOT_ENABLED: "TOTP not enabled",
|
|
46
|
+
TWO_FACTOR_NOT_ENABLED: "Two factor isn't enabled",
|
|
47
|
+
BACKUP_CODES_NOT_ENABLED: "Backup codes aren't enabled",
|
|
48
|
+
INVALID_BACKUP_CODE: "Invalid backup code",
|
|
49
|
+
INVALID_CODE: "Invalid code",
|
|
50
|
+
TOO_MANY_ATTEMPTS_REQUEST_NEW_CODE: "Too many attempts. Please request a new code.",
|
|
51
|
+
INVALID_TWO_FACTOR_COOKIE: "Invalid two factor cookie"
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const TWO_FACTOR_COOKIE_NAME = "two_factor";
|
|
55
|
+
const TRUST_DEVICE_COOKIE_NAME = "trust_device";
|
|
56
|
+
|
|
57
|
+
async function verifyTwoFactor(ctx) {
|
|
58
|
+
const session = await refreshToken.getSessionFromCtx(ctx);
|
|
59
|
+
if (!session) {
|
|
60
|
+
const cookieName = ctx.context.createAuthCookie(TWO_FACTOR_COOKIE_NAME);
|
|
61
|
+
const twoFactorCookie = await ctx.getSignedCookie(
|
|
62
|
+
cookieName.name,
|
|
63
|
+
ctx.context.secret
|
|
64
|
+
);
|
|
65
|
+
if (!twoFactorCookie) {
|
|
66
|
+
throw new betterCall.APIError("UNAUTHORIZED", {
|
|
67
|
+
message: TWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
const verificationToken = await ctx.context.internalAdapter.findVerificationValue(twoFactorCookie);
|
|
71
|
+
if (!verificationToken) {
|
|
72
|
+
throw new betterCall.APIError("UNAUTHORIZED", {
|
|
73
|
+
message: TWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
const user = await ctx.context.internalAdapter.findUserById(
|
|
77
|
+
verificationToken.value
|
|
78
|
+
);
|
|
79
|
+
if (!user) {
|
|
80
|
+
throw new betterCall.APIError("UNAUTHORIZED", {
|
|
81
|
+
message: TWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
const dontRememberMe = await ctx.getSignedCookie(
|
|
85
|
+
ctx.context.authCookies.dontRememberToken.name,
|
|
86
|
+
ctx.context.secret
|
|
87
|
+
);
|
|
88
|
+
return {
|
|
89
|
+
valid: async (ctx2) => {
|
|
90
|
+
const session2 = await ctx2.context.internalAdapter.createSession(
|
|
91
|
+
verificationToken.value,
|
|
92
|
+
ctx2.headers,
|
|
93
|
+
!!dontRememberMe
|
|
94
|
+
);
|
|
95
|
+
if (!session2) {
|
|
96
|
+
throw new betterCall.APIError("INTERNAL_SERVER_ERROR", {
|
|
97
|
+
message: "failed to create session"
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
await cookies_index.setSessionCookie(ctx2, {
|
|
101
|
+
session: session2,
|
|
102
|
+
user
|
|
103
|
+
});
|
|
104
|
+
if (ctx2.body.trustDevice) {
|
|
105
|
+
const trustDeviceCookie = ctx2.context.createAuthCookie(
|
|
106
|
+
TRUST_DEVICE_COOKIE_NAME,
|
|
107
|
+
{
|
|
108
|
+
maxAge: 30 * 24 * 60 * 60
|
|
109
|
+
// 30 days, it'll be refreshed on sign in requests
|
|
110
|
+
}
|
|
111
|
+
);
|
|
112
|
+
const token = await hmac.createHMAC("SHA-256", "base64urlnopad").sign(
|
|
113
|
+
ctx2.context.secret,
|
|
114
|
+
`${user.id}!${session2.token}`
|
|
115
|
+
);
|
|
116
|
+
await ctx2.setSignedCookie(
|
|
117
|
+
trustDeviceCookie.name,
|
|
118
|
+
`${token}!${session2.token}`,
|
|
119
|
+
ctx2.context.secret,
|
|
120
|
+
trustDeviceCookie.attributes
|
|
121
|
+
);
|
|
122
|
+
ctx2.setCookie(ctx2.context.authCookies.dontRememberToken.name, "", {
|
|
123
|
+
maxAge: 0
|
|
124
|
+
});
|
|
125
|
+
ctx2.setCookie(cookieName.name, "", {
|
|
126
|
+
maxAge: 0
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
return ctx2.json({
|
|
130
|
+
token: session2.token,
|
|
131
|
+
user: {
|
|
132
|
+
id: user.id,
|
|
133
|
+
email: user.email,
|
|
134
|
+
emailVerified: user.emailVerified,
|
|
135
|
+
name: user.name,
|
|
136
|
+
image: user.image,
|
|
137
|
+
createdAt: user.createdAt,
|
|
138
|
+
updatedAt: user.updatedAt
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
},
|
|
142
|
+
invalid: async (errorKey) => {
|
|
143
|
+
throw new betterCall.APIError("UNAUTHORIZED", {
|
|
144
|
+
message: TWO_FACTOR_ERROR_CODES[errorKey]
|
|
145
|
+
});
|
|
146
|
+
},
|
|
147
|
+
session: {
|
|
148
|
+
session: null,
|
|
149
|
+
user
|
|
150
|
+
},
|
|
151
|
+
key: twoFactorCookie
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
return {
|
|
155
|
+
valid: async (ctx2) => {
|
|
156
|
+
return ctx2.json({
|
|
157
|
+
token: session.session.token,
|
|
158
|
+
user: {
|
|
159
|
+
id: session.user.id,
|
|
160
|
+
email: session.user.email,
|
|
161
|
+
emailVerified: session.user.emailVerified,
|
|
162
|
+
name: session.user.name,
|
|
163
|
+
image: session.user.image,
|
|
164
|
+
createdAt: session.user.createdAt,
|
|
165
|
+
updatedAt: session.user.updatedAt
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
},
|
|
169
|
+
invalid: async () => {
|
|
170
|
+
throw new betterCall.APIError("UNAUTHORIZED", {
|
|
171
|
+
message: TWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE
|
|
172
|
+
});
|
|
173
|
+
},
|
|
174
|
+
session,
|
|
175
|
+
key: `${session.user.id}!${session.session.id}`
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
44
179
|
function generateBackupCodesFn(options) {
|
|
45
180
|
return Array.from({ length: options?.amount ?? 10 }).fill(null).map(() => random.generateRandomString(options?.length ?? 10, "a-z", "0-9", "A-Z")).map((code) => `${code.slice(0, 5)}-${code.slice(5)}`);
|
|
46
181
|
}
|
|
@@ -108,10 +243,112 @@ const backupCode2fa = (options) => {
|
|
|
108
243
|
description: "If true, the device will be trusted for 30 days. It'll be refreshed on every sign in request within this time."
|
|
109
244
|
}).optional()
|
|
110
245
|
}),
|
|
111
|
-
|
|
246
|
+
metadata: {
|
|
247
|
+
openapi: {
|
|
248
|
+
description: "Verify a backup code for two-factor authentication",
|
|
249
|
+
responses: {
|
|
250
|
+
"200": {
|
|
251
|
+
description: "Backup code verified successfully",
|
|
252
|
+
content: {
|
|
253
|
+
"application/json": {
|
|
254
|
+
schema: {
|
|
255
|
+
type: "object",
|
|
256
|
+
properties: {
|
|
257
|
+
user: {
|
|
258
|
+
type: "object",
|
|
259
|
+
properties: {
|
|
260
|
+
id: {
|
|
261
|
+
type: "string",
|
|
262
|
+
description: "Unique identifier of the user"
|
|
263
|
+
},
|
|
264
|
+
email: {
|
|
265
|
+
type: "string",
|
|
266
|
+
format: "email",
|
|
267
|
+
nullable: true,
|
|
268
|
+
description: "User's email address"
|
|
269
|
+
},
|
|
270
|
+
emailVerified: {
|
|
271
|
+
type: "boolean",
|
|
272
|
+
nullable: true,
|
|
273
|
+
description: "Whether the email is verified"
|
|
274
|
+
},
|
|
275
|
+
name: {
|
|
276
|
+
type: "string",
|
|
277
|
+
nullable: true,
|
|
278
|
+
description: "User's name"
|
|
279
|
+
},
|
|
280
|
+
image: {
|
|
281
|
+
type: "string",
|
|
282
|
+
format: "uri",
|
|
283
|
+
nullable: true,
|
|
284
|
+
description: "User's profile image URL"
|
|
285
|
+
},
|
|
286
|
+
twoFactorEnabled: {
|
|
287
|
+
type: "boolean",
|
|
288
|
+
description: "Whether two-factor authentication is enabled for the user"
|
|
289
|
+
},
|
|
290
|
+
createdAt: {
|
|
291
|
+
type: "string",
|
|
292
|
+
format: "date-time",
|
|
293
|
+
description: "Timestamp when the user was created"
|
|
294
|
+
},
|
|
295
|
+
updatedAt: {
|
|
296
|
+
type: "string",
|
|
297
|
+
format: "date-time",
|
|
298
|
+
description: "Timestamp when the user was last updated"
|
|
299
|
+
}
|
|
300
|
+
},
|
|
301
|
+
required: [
|
|
302
|
+
"id",
|
|
303
|
+
"twoFactorEnabled",
|
|
304
|
+
"createdAt",
|
|
305
|
+
"updatedAt"
|
|
306
|
+
],
|
|
307
|
+
description: "The authenticated user object with two-factor details"
|
|
308
|
+
},
|
|
309
|
+
session: {
|
|
310
|
+
type: "object",
|
|
311
|
+
properties: {
|
|
312
|
+
token: {
|
|
313
|
+
type: "string",
|
|
314
|
+
description: "Session token"
|
|
315
|
+
},
|
|
316
|
+
userId: {
|
|
317
|
+
type: "string",
|
|
318
|
+
description: "ID of the user associated with the session"
|
|
319
|
+
},
|
|
320
|
+
createdAt: {
|
|
321
|
+
type: "string",
|
|
322
|
+
format: "date-time",
|
|
323
|
+
description: "Timestamp when the session was created"
|
|
324
|
+
},
|
|
325
|
+
expiresAt: {
|
|
326
|
+
type: "string",
|
|
327
|
+
format: "date-time",
|
|
328
|
+
description: "Timestamp when the session expires"
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
required: [
|
|
332
|
+
"token",
|
|
333
|
+
"userId",
|
|
334
|
+
"createdAt",
|
|
335
|
+
"expiresAt"
|
|
336
|
+
],
|
|
337
|
+
description: "The current session object, included unless disableSession is true"
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
required: ["user", "session"]
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
112
348
|
},
|
|
113
349
|
async (ctx) => {
|
|
114
|
-
const
|
|
350
|
+
const { session, valid } = await verifyTwoFactor(ctx);
|
|
351
|
+
const user = session.user;
|
|
115
352
|
const twoFactor = await ctx.context.adapter.findOne({
|
|
116
353
|
model: twoFactorTable,
|
|
117
354
|
where: [
|
|
@@ -123,7 +360,7 @@ const backupCode2fa = (options) => {
|
|
|
123
360
|
});
|
|
124
361
|
if (!twoFactor) {
|
|
125
362
|
throw new betterCall.APIError("BAD_REQUEST", {
|
|
126
|
-
message:
|
|
363
|
+
message: TWO_FACTOR_ERROR_CODES.BACKUP_CODES_NOT_ENABLED
|
|
127
364
|
});
|
|
128
365
|
}
|
|
129
366
|
const validate = await verifyBackupCode(
|
|
@@ -135,7 +372,7 @@ const backupCode2fa = (options) => {
|
|
|
135
372
|
);
|
|
136
373
|
if (!validate.status) {
|
|
137
374
|
throw new betterCall.APIError("UNAUTHORIZED", {
|
|
138
|
-
message:
|
|
375
|
+
message: TWO_FACTOR_ERROR_CODES.INVALID_BACKUP_CODE
|
|
139
376
|
});
|
|
140
377
|
}
|
|
141
378
|
const updatedBackupCodes = await crypto_index.symmetricEncrypt({
|
|
@@ -155,14 +392,19 @@ const backupCode2fa = (options) => {
|
|
|
155
392
|
]
|
|
156
393
|
});
|
|
157
394
|
if (!ctx.body.disableSession) {
|
|
158
|
-
|
|
159
|
-
session: ctx.context.session.session,
|
|
160
|
-
user
|
|
161
|
-
});
|
|
395
|
+
return valid(ctx);
|
|
162
396
|
}
|
|
163
397
|
return ctx.json({
|
|
164
|
-
|
|
165
|
-
|
|
398
|
+
token: session.session?.token,
|
|
399
|
+
user: {
|
|
400
|
+
id: session.user?.id,
|
|
401
|
+
email: session.user.email,
|
|
402
|
+
emailVerified: session.user.emailVerified,
|
|
403
|
+
name: session.user.name,
|
|
404
|
+
image: session.user.image,
|
|
405
|
+
createdAt: session.user.createdAt,
|
|
406
|
+
updatedAt: session.user.updatedAt
|
|
407
|
+
}
|
|
166
408
|
});
|
|
167
409
|
}
|
|
168
410
|
),
|
|
@@ -173,13 +415,43 @@ const backupCode2fa = (options) => {
|
|
|
173
415
|
body: zod.z.object({
|
|
174
416
|
password: zod.z.string()
|
|
175
417
|
}),
|
|
176
|
-
use: [refreshToken.sessionMiddleware]
|
|
418
|
+
use: [refreshToken.sessionMiddleware],
|
|
419
|
+
metadata: {
|
|
420
|
+
openapi: {
|
|
421
|
+
description: "Generate new backup codes for two-factor authentication",
|
|
422
|
+
responses: {
|
|
423
|
+
"200": {
|
|
424
|
+
description: "Backup codes generated successfully",
|
|
425
|
+
content: {
|
|
426
|
+
"application/json": {
|
|
427
|
+
schema: {
|
|
428
|
+
type: "object",
|
|
429
|
+
properties: {
|
|
430
|
+
status: {
|
|
431
|
+
type: "boolean",
|
|
432
|
+
description: "Indicates if the backup codes were generated successfully",
|
|
433
|
+
enum: [true]
|
|
434
|
+
},
|
|
435
|
+
backupCodes: {
|
|
436
|
+
type: "array",
|
|
437
|
+
items: { type: "string" },
|
|
438
|
+
description: "Array of generated backup codes in plain text"
|
|
439
|
+
}
|
|
440
|
+
},
|
|
441
|
+
required: ["status", "backupCodes"]
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
177
449
|
},
|
|
178
450
|
async (ctx) => {
|
|
179
451
|
const user = ctx.context.session.user;
|
|
180
452
|
if (!user.twoFactorEnabled) {
|
|
181
453
|
throw new betterCall.APIError("BAD_REQUEST", {
|
|
182
|
-
message:
|
|
454
|
+
message: TWO_FACTOR_ERROR_CODES.TWO_FACTOR_NOT_ENABLED
|
|
183
455
|
});
|
|
184
456
|
}
|
|
185
457
|
await ctx.context.password.checkPassword(user.id, ctx);
|
|
@@ -237,7 +509,7 @@ const backupCode2fa = (options) => {
|
|
|
237
509
|
);
|
|
238
510
|
if (!backupCodes) {
|
|
239
511
|
throw new betterCall.APIError("BAD_REQUEST", {
|
|
240
|
-
message:
|
|
512
|
+
message: TWO_FACTOR_ERROR_CODES.BACKUP_CODES_NOT_ENABLED
|
|
241
513
|
});
|
|
242
514
|
}
|
|
243
515
|
return ctx.json({
|
|
@@ -269,7 +541,6 @@ const otp2fa = (options) => {
|
|
|
269
541
|
*/
|
|
270
542
|
trustDevice: zod.z.boolean().optional()
|
|
271
543
|
}).optional(),
|
|
272
|
-
use: [verifyMiddleware.verifyTwoFactorMiddleware],
|
|
273
544
|
metadata: {
|
|
274
545
|
openapi: {
|
|
275
546
|
summary: "Send two factor OTP",
|
|
@@ -303,28 +574,31 @@ const otp2fa = (options) => {
|
|
|
303
574
|
message: "otp isn't configured"
|
|
304
575
|
});
|
|
305
576
|
}
|
|
306
|
-
const
|
|
577
|
+
const { session, key } = await verifyTwoFactor(ctx);
|
|
307
578
|
const twoFactor = await ctx.context.adapter.findOne({
|
|
308
579
|
model: twoFactorTable,
|
|
309
580
|
where: [
|
|
310
581
|
{
|
|
311
582
|
field: "userId",
|
|
312
|
-
value: user.id
|
|
583
|
+
value: session.user.id
|
|
313
584
|
}
|
|
314
585
|
]
|
|
315
586
|
});
|
|
316
587
|
if (!twoFactor) {
|
|
317
588
|
throw new betterCall.APIError("BAD_REQUEST", {
|
|
318
|
-
message:
|
|
589
|
+
message: TWO_FACTOR_ERROR_CODES.OTP_NOT_ENABLED
|
|
319
590
|
});
|
|
320
591
|
}
|
|
321
592
|
const code = random.generateRandomString(opts.digits, "0-9");
|
|
322
593
|
await ctx.context.internalAdapter.createVerificationValue({
|
|
323
|
-
value: code
|
|
324
|
-
identifier: `2fa-otp-${
|
|
594
|
+
value: `${code}!0`,
|
|
595
|
+
identifier: `2fa-otp-${key}`,
|
|
325
596
|
expiresAt: new Date(Date.now() + opts.period)
|
|
326
597
|
});
|
|
327
|
-
await options.sendOTP(
|
|
598
|
+
await options.sendOTP(
|
|
599
|
+
{ user: session.user, otp: code },
|
|
600
|
+
ctx.request
|
|
601
|
+
);
|
|
328
602
|
return ctx.json({ status: true });
|
|
329
603
|
}
|
|
330
604
|
);
|
|
@@ -343,23 +617,67 @@ const otp2fa = (options) => {
|
|
|
343
617
|
*/
|
|
344
618
|
trustDevice: zod.z.boolean().optional()
|
|
345
619
|
}),
|
|
346
|
-
use: [verifyMiddleware.verifyTwoFactorMiddleware],
|
|
347
620
|
metadata: {
|
|
348
621
|
openapi: {
|
|
349
622
|
summary: "Verify two factor OTP",
|
|
350
623
|
description: "Verify two factor OTP",
|
|
351
624
|
responses: {
|
|
352
|
-
200: {
|
|
353
|
-
description: "
|
|
625
|
+
"200": {
|
|
626
|
+
description: "Two-factor OTP verified successfully",
|
|
354
627
|
content: {
|
|
355
628
|
"application/json": {
|
|
356
629
|
schema: {
|
|
357
630
|
type: "object",
|
|
358
631
|
properties: {
|
|
359
|
-
|
|
360
|
-
type: "
|
|
632
|
+
token: {
|
|
633
|
+
type: "string",
|
|
634
|
+
description: "Session token for the authenticated session"
|
|
635
|
+
},
|
|
636
|
+
user: {
|
|
637
|
+
type: "object",
|
|
638
|
+
properties: {
|
|
639
|
+
id: {
|
|
640
|
+
type: "string",
|
|
641
|
+
description: "Unique identifier of the user"
|
|
642
|
+
},
|
|
643
|
+
email: {
|
|
644
|
+
type: "string",
|
|
645
|
+
format: "email",
|
|
646
|
+
nullable: true,
|
|
647
|
+
description: "User's email address"
|
|
648
|
+
},
|
|
649
|
+
emailVerified: {
|
|
650
|
+
type: "boolean",
|
|
651
|
+
nullable: true,
|
|
652
|
+
description: "Whether the email is verified"
|
|
653
|
+
},
|
|
654
|
+
name: {
|
|
655
|
+
type: "string",
|
|
656
|
+
nullable: true,
|
|
657
|
+
description: "User's name"
|
|
658
|
+
},
|
|
659
|
+
image: {
|
|
660
|
+
type: "string",
|
|
661
|
+
format: "uri",
|
|
662
|
+
nullable: true,
|
|
663
|
+
description: "User's profile image URL"
|
|
664
|
+
},
|
|
665
|
+
createdAt: {
|
|
666
|
+
type: "string",
|
|
667
|
+
format: "date-time",
|
|
668
|
+
description: "Timestamp when the user was created"
|
|
669
|
+
},
|
|
670
|
+
updatedAt: {
|
|
671
|
+
type: "string",
|
|
672
|
+
format: "date-time",
|
|
673
|
+
description: "Timestamp when the user was last updated"
|
|
674
|
+
}
|
|
675
|
+
},
|
|
676
|
+
required: ["id", "createdAt", "updatedAt"],
|
|
677
|
+
description: "The authenticated user object"
|
|
361
678
|
}
|
|
362
|
-
}
|
|
679
|
+
},
|
|
680
|
+
required: ["token", "user"]
|
|
363
681
|
}
|
|
364
682
|
}
|
|
365
683
|
}
|
|
@@ -369,54 +687,90 @@ const otp2fa = (options) => {
|
|
|
369
687
|
}
|
|
370
688
|
},
|
|
371
689
|
async (ctx) => {
|
|
372
|
-
const
|
|
690
|
+
const { session, key, valid, invalid } = await verifyTwoFactor(ctx);
|
|
373
691
|
const twoFactor = await ctx.context.adapter.findOne({
|
|
374
692
|
model: twoFactorTable,
|
|
375
693
|
where: [
|
|
376
694
|
{
|
|
377
695
|
field: "userId",
|
|
378
|
-
value: user.id
|
|
696
|
+
value: session.user.id
|
|
379
697
|
}
|
|
380
698
|
]
|
|
381
699
|
});
|
|
382
700
|
if (!twoFactor) {
|
|
383
701
|
throw new betterCall.APIError("BAD_REQUEST", {
|
|
384
|
-
message:
|
|
702
|
+
message: TWO_FACTOR_ERROR_CODES.OTP_NOT_ENABLED
|
|
385
703
|
});
|
|
386
704
|
}
|
|
387
705
|
const toCheckOtp = await ctx.context.internalAdapter.findVerificationValue(
|
|
388
|
-
`2fa-otp-${
|
|
706
|
+
`2fa-otp-${key}`
|
|
389
707
|
);
|
|
708
|
+
const [otp, counter] = toCheckOtp?.value?.split("!") ?? [];
|
|
390
709
|
if (!toCheckOtp || toCheckOtp.expiresAt < /* @__PURE__ */ new Date()) {
|
|
710
|
+
await ctx.context.internalAdapter.deleteVerificationValue(
|
|
711
|
+
`2fa-otp-${key}`
|
|
712
|
+
);
|
|
713
|
+
throw new betterCall.APIError("BAD_REQUEST", {
|
|
714
|
+
message: TWO_FACTOR_ERROR_CODES.OTP_HAS_EXPIRED
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
const allowedAttempts = options?.allowedAttempts || 5;
|
|
718
|
+
if (parseInt(counter) >= allowedAttempts) {
|
|
719
|
+
await ctx.context.internalAdapter.deleteVerificationValue(
|
|
720
|
+
`2fa-otp-${key}`
|
|
721
|
+
);
|
|
391
722
|
throw new betterCall.APIError("BAD_REQUEST", {
|
|
392
|
-
message:
|
|
723
|
+
message: TWO_FACTOR_ERROR_CODES.TOO_MANY_ATTEMPTS_REQUEST_NEW_CODE
|
|
393
724
|
});
|
|
394
725
|
}
|
|
395
|
-
if (
|
|
396
|
-
if (!user.twoFactorEnabled) {
|
|
726
|
+
if (otp === ctx.body.code) {
|
|
727
|
+
if (!session.user.twoFactorEnabled) {
|
|
728
|
+
if (!session.session) {
|
|
729
|
+
throw new betterCall.APIError("BAD_REQUEST", {
|
|
730
|
+
message: refreshToken.BASE_ERROR_CODES.FAILED_TO_CREATE_SESSION
|
|
731
|
+
});
|
|
732
|
+
}
|
|
397
733
|
const updatedUser = await ctx.context.internalAdapter.updateUser(
|
|
398
|
-
user.id,
|
|
734
|
+
session.user.id,
|
|
399
735
|
{
|
|
400
736
|
twoFactorEnabled: true
|
|
401
737
|
}
|
|
402
738
|
);
|
|
403
739
|
const newSession = await ctx.context.internalAdapter.createSession(
|
|
404
|
-
user.id,
|
|
405
|
-
ctx.
|
|
740
|
+
session.user.id,
|
|
741
|
+
ctx.headers,
|
|
406
742
|
false,
|
|
407
|
-
|
|
743
|
+
session.session
|
|
408
744
|
);
|
|
409
745
|
await ctx.context.internalAdapter.deleteSession(
|
|
410
|
-
|
|
746
|
+
session.session.token
|
|
411
747
|
);
|
|
412
748
|
await cookies_index.setSessionCookie(ctx, {
|
|
413
749
|
session: newSession,
|
|
414
750
|
user: updatedUser
|
|
415
751
|
});
|
|
752
|
+
return ctx.json({
|
|
753
|
+
token: newSession.token,
|
|
754
|
+
user: {
|
|
755
|
+
id: updatedUser.id,
|
|
756
|
+
email: updatedUser.email,
|
|
757
|
+
emailVerified: updatedUser.emailVerified,
|
|
758
|
+
name: updatedUser.name,
|
|
759
|
+
image: updatedUser.image,
|
|
760
|
+
createdAt: updatedUser.createdAt,
|
|
761
|
+
updatedAt: updatedUser.updatedAt
|
|
762
|
+
}
|
|
763
|
+
});
|
|
416
764
|
}
|
|
417
|
-
return
|
|
765
|
+
return valid(ctx);
|
|
418
766
|
} else {
|
|
419
|
-
|
|
767
|
+
await ctx.context.internalAdapter.updateVerificationValue(
|
|
768
|
+
toCheckOtp.id,
|
|
769
|
+
{
|
|
770
|
+
value: `${otp}!${parseInt(counter) + 1}`
|
|
771
|
+
}
|
|
772
|
+
);
|
|
773
|
+
return invalid("INVALID_CODE");
|
|
420
774
|
}
|
|
421
775
|
}
|
|
422
776
|
);
|
|
@@ -440,7 +794,11 @@ const totp2fa = (options) => {
|
|
|
440
794
|
"/totp/generate",
|
|
441
795
|
{
|
|
442
796
|
method: "POST",
|
|
443
|
-
|
|
797
|
+
body: zod.z.object({
|
|
798
|
+
secret: zod.z.string({
|
|
799
|
+
description: "The secret to generate the TOTP code"
|
|
800
|
+
})
|
|
801
|
+
}),
|
|
444
802
|
metadata: {
|
|
445
803
|
openapi: {
|
|
446
804
|
summary: "Generate TOTP code",
|
|
@@ -462,7 +820,8 @@ const totp2fa = (options) => {
|
|
|
462
820
|
}
|
|
463
821
|
}
|
|
464
822
|
}
|
|
465
|
-
}
|
|
823
|
+
},
|
|
824
|
+
SERVER_ONLY: true
|
|
466
825
|
}
|
|
467
826
|
},
|
|
468
827
|
async (ctx) => {
|
|
@@ -474,22 +833,7 @@ const totp2fa = (options) => {
|
|
|
474
833
|
message: "totp isn't configured"
|
|
475
834
|
});
|
|
476
835
|
}
|
|
477
|
-
const
|
|
478
|
-
const twoFactor = await ctx.context.adapter.findOne({
|
|
479
|
-
model: twoFactorTable,
|
|
480
|
-
where: [
|
|
481
|
-
{
|
|
482
|
-
field: "userId",
|
|
483
|
-
value: user.id
|
|
484
|
-
}
|
|
485
|
-
]
|
|
486
|
-
});
|
|
487
|
-
if (!twoFactor) {
|
|
488
|
-
throw new betterCall.APIError("BAD_REQUEST", {
|
|
489
|
-
message: errorCode.TWO_FACTOR_ERROR_CODES.TOTP_NOT_ENABLED
|
|
490
|
-
});
|
|
491
|
-
}
|
|
492
|
-
const code = await otp.createOTP(twoFactor.secret, {
|
|
836
|
+
const code = await otp.createOTP(ctx.body.secret, {
|
|
493
837
|
period: opts.period,
|
|
494
838
|
digits: opts.digits
|
|
495
839
|
}).totp();
|
|
@@ -551,7 +895,7 @@ const totp2fa = (options) => {
|
|
|
551
895
|
});
|
|
552
896
|
if (!twoFactor || !user.twoFactorEnabled) {
|
|
553
897
|
throw new betterCall.APIError("BAD_REQUEST", {
|
|
554
|
-
message:
|
|
898
|
+
message: TWO_FACTOR_ERROR_CODES.TOTP_NOT_ENABLED
|
|
555
899
|
});
|
|
556
900
|
}
|
|
557
901
|
const secret = await crypto_index.symmetricDecrypt({
|
|
@@ -585,7 +929,6 @@ const totp2fa = (options) => {
|
|
|
585
929
|
description: "If true, the device will be trusted for 30 days. It'll be refreshed on every sign in request within this time."
|
|
586
930
|
}).optional()
|
|
587
931
|
}),
|
|
588
|
-
use: [verifyMiddleware.verifyTwoFactorMiddleware],
|
|
589
932
|
metadata: {
|
|
590
933
|
openapi: {
|
|
591
934
|
summary: "Verify two factor TOTP",
|
|
@@ -619,7 +962,8 @@ const totp2fa = (options) => {
|
|
|
619
962
|
message: "totp isn't configured"
|
|
620
963
|
});
|
|
621
964
|
}
|
|
622
|
-
const
|
|
965
|
+
const { session, valid, invalid } = await verifyTwoFactor(ctx);
|
|
966
|
+
const user = session.user;
|
|
623
967
|
const twoFactor = await ctx.context.adapter.findOne({
|
|
624
968
|
model: twoFactorTable,
|
|
625
969
|
where: [
|
|
@@ -631,7 +975,7 @@ const totp2fa = (options) => {
|
|
|
631
975
|
});
|
|
632
976
|
if (!twoFactor) {
|
|
633
977
|
throw new betterCall.APIError("BAD_REQUEST", {
|
|
634
|
-
message:
|
|
978
|
+
message: TWO_FACTOR_ERROR_CODES.TOTP_NOT_ENABLED
|
|
635
979
|
});
|
|
636
980
|
}
|
|
637
981
|
const decrypted = await crypto_index.symmetricDecrypt({
|
|
@@ -643,9 +987,14 @@ const totp2fa = (options) => {
|
|
|
643
987
|
digits: opts.digits
|
|
644
988
|
}).verify(ctx.body.code);
|
|
645
989
|
if (!status) {
|
|
646
|
-
return
|
|
990
|
+
return invalid("INVALID_CODE");
|
|
647
991
|
}
|
|
648
992
|
if (!user.twoFactorEnabled) {
|
|
993
|
+
if (!session.session) {
|
|
994
|
+
throw new betterCall.APIError("BAD_REQUEST", {
|
|
995
|
+
message: refreshToken.BASE_ERROR_CODES.FAILED_TO_CREATE_SESSION
|
|
996
|
+
});
|
|
997
|
+
}
|
|
649
998
|
const updatedUser = await ctx.context.internalAdapter.updateUser(
|
|
650
999
|
user.id,
|
|
651
1000
|
{
|
|
@@ -653,23 +1002,16 @@ const totp2fa = (options) => {
|
|
|
653
1002
|
},
|
|
654
1003
|
ctx
|
|
655
1004
|
);
|
|
656
|
-
const newSession = await ctx.context.internalAdapter.createSession(
|
|
657
|
-
user.id,
|
|
658
|
-
ctx.request,
|
|
659
|
-
false,
|
|
660
|
-
ctx.context.session.session
|
|
661
|
-
).catch((e) => {
|
|
1005
|
+
const newSession = await ctx.context.internalAdapter.createSession(user.id, ctx.headers, false, session.session).catch((e) => {
|
|
662
1006
|
throw e;
|
|
663
1007
|
});
|
|
664
|
-
await ctx.context.internalAdapter.deleteSession(
|
|
665
|
-
ctx.context.session.session.token
|
|
666
|
-
);
|
|
1008
|
+
await ctx.context.internalAdapter.deleteSession(session.session.token);
|
|
667
1009
|
await cookies_index.setSessionCookie(ctx, {
|
|
668
1010
|
session: newSession,
|
|
669
1011
|
user: updatedUser
|
|
670
1012
|
});
|
|
671
1013
|
}
|
|
672
|
-
return
|
|
1014
|
+
return valid(ctx);
|
|
673
1015
|
}
|
|
674
1016
|
);
|
|
675
1017
|
return {
|
|
@@ -738,7 +1080,10 @@ const twoFactor = (options) => {
|
|
|
738
1080
|
body: zod.z.object({
|
|
739
1081
|
password: zod.z.string({
|
|
740
1082
|
description: "User password"
|
|
741
|
-
})
|
|
1083
|
+
}),
|
|
1084
|
+
issuer: zod.z.string({
|
|
1085
|
+
description: "Custom issuer for the TOTP URI"
|
|
1086
|
+
}).optional()
|
|
742
1087
|
}),
|
|
743
1088
|
use: [refreshToken.sessionMiddleware],
|
|
744
1089
|
metadata: {
|
|
@@ -775,7 +1120,7 @@ const twoFactor = (options) => {
|
|
|
775
1120
|
},
|
|
776
1121
|
async (ctx) => {
|
|
777
1122
|
const user = ctx.context.session.user;
|
|
778
|
-
const { password: password$1 } = ctx.body;
|
|
1123
|
+
const { password: password$1, issuer } = ctx.body;
|
|
779
1124
|
const isPasswordValid = await password.validatePassword(ctx, {
|
|
780
1125
|
password: password$1,
|
|
781
1126
|
userId: user.id
|
|
@@ -804,7 +1149,7 @@ const twoFactor = (options) => {
|
|
|
804
1149
|
);
|
|
805
1150
|
const newSession = await ctx.context.internalAdapter.createSession(
|
|
806
1151
|
updatedUser.id,
|
|
807
|
-
ctx.
|
|
1152
|
+
ctx.headers,
|
|
808
1153
|
false,
|
|
809
1154
|
ctx.context.session.session
|
|
810
1155
|
);
|
|
@@ -836,7 +1181,7 @@ const twoFactor = (options) => {
|
|
|
836
1181
|
const totpURI = otp.createOTP(secret, {
|
|
837
1182
|
digits: options?.totpOptions?.digits || 6,
|
|
838
1183
|
period: options?.totpOptions?.period
|
|
839
|
-
}).url(options?.issuer || ctx.context.appName, user.email);
|
|
1184
|
+
}).url(issuer || options?.issuer || ctx.context.appName, user.email);
|
|
840
1185
|
return ctx.json({ totpURI, backupCodes: backupCodes.backupCodes });
|
|
841
1186
|
}
|
|
842
1187
|
),
|
|
@@ -904,7 +1249,7 @@ const twoFactor = (options) => {
|
|
|
904
1249
|
});
|
|
905
1250
|
const newSession = await ctx.context.internalAdapter.createSession(
|
|
906
1251
|
updatedUser.id,
|
|
907
|
-
ctx.
|
|
1252
|
+
ctx.headers,
|
|
908
1253
|
false,
|
|
909
1254
|
ctx.context.session.session
|
|
910
1255
|
);
|
|
@@ -935,7 +1280,7 @@ const twoFactor = (options) => {
|
|
|
935
1280
|
return;
|
|
936
1281
|
}
|
|
937
1282
|
const trustDeviceCookieName = ctx.context.createAuthCookie(
|
|
938
|
-
|
|
1283
|
+
TRUST_DEVICE_COOKIE_NAME
|
|
939
1284
|
);
|
|
940
1285
|
const trustDeviceCookie = await ctx.getSignedCookie(
|
|
941
1286
|
trustDeviceCookieName.name,
|
|
@@ -963,16 +1308,22 @@ const twoFactor = (options) => {
|
|
|
963
1308
|
}
|
|
964
1309
|
cookies_index.deleteSessionCookie(ctx, true);
|
|
965
1310
|
await ctx.context.internalAdapter.deleteSession(data.session.token);
|
|
1311
|
+
const maxAge = options?.otpOptions?.period || 60 * 5;
|
|
966
1312
|
const twoFactorCookie = ctx.context.createAuthCookie(
|
|
967
|
-
|
|
1313
|
+
TWO_FACTOR_COOKIE_NAME,
|
|
968
1314
|
{
|
|
969
|
-
maxAge
|
|
970
|
-
// 10 minutes
|
|
1315
|
+
maxAge
|
|
971
1316
|
}
|
|
972
1317
|
);
|
|
1318
|
+
const identifier = `2fa-${random.generateRandomString(20)}`;
|
|
1319
|
+
await ctx.context.internalAdapter.createVerificationValue({
|
|
1320
|
+
value: data.user.id,
|
|
1321
|
+
identifier,
|
|
1322
|
+
expiresAt: new Date(Date.now() + maxAge * 1e3)
|
|
1323
|
+
});
|
|
973
1324
|
await ctx.setSignedCookie(
|
|
974
1325
|
twoFactorCookie.name,
|
|
975
|
-
|
|
1326
|
+
identifier,
|
|
976
1327
|
ctx.context.secret,
|
|
977
1328
|
twoFactorCookie.attributes
|
|
978
1329
|
);
|
|
@@ -993,10 +1344,10 @@ const twoFactor = (options) => {
|
|
|
993
1344
|
max: 3
|
|
994
1345
|
}
|
|
995
1346
|
],
|
|
996
|
-
$ERROR_CODES:
|
|
1347
|
+
$ERROR_CODES: TWO_FACTOR_ERROR_CODES
|
|
997
1348
|
};
|
|
998
1349
|
};
|
|
999
1350
|
|
|
1000
|
-
exports.TWO_FACTOR_ERROR_CODES = errorCode.TWO_FACTOR_ERROR_CODES;
|
|
1001
1351
|
exports.twoFactorClient = client.twoFactorClient;
|
|
1352
|
+
exports.TWO_FACTOR_ERROR_CODES = TWO_FACTOR_ERROR_CODES;
|
|
1002
1353
|
exports.twoFactor = twoFactor;
|