@absolutejs/auth 0.75.4 → 0.76.0
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/cli/migrate.js +13 -2
- package/dist/cli/migrate.js.map +4 -4
- package/dist/index.d.ts +80 -4
- package/dist/index.js +290 -57
- package/dist/index.js.map +12 -12
- package/dist/mfa/challenge.d.ts +53 -7
- package/dist/mfa/management.d.ts +49 -6
- package/dist/mfa/postgresMfaStore.d.ts +31 -1
- package/dist/mfa/rotation.d.ts +1 -1
- package/dist/mfa/routes.d.ts +78 -2
- package/dist/mfa/sms.d.ts +7 -2
- package/dist/mfa/totp.d.ts +8 -2
- package/dist/mfa/types.d.ts +47 -0
- package/dist/server.js +288 -57
- package/dist/server.js.map +12 -12
- package/package.json +1 -1
package/dist/mfa/challenge.d.ts
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import { type MfaRouteProps } from './config';
|
|
2
|
+
export type MfaChallengeOptions = {
|
|
3
|
+
backupCodesAvailable: boolean;
|
|
4
|
+
factors: Array<{
|
|
5
|
+
id: string;
|
|
6
|
+
label: string;
|
|
7
|
+
phone: string | null;
|
|
8
|
+
type: 'sms' | 'totp';
|
|
9
|
+
}>;
|
|
10
|
+
};
|
|
2
11
|
export declare const mfaChallenge: <UserType>({ authSessionStore, challengeRoute, cookieSecure, encryptionKey, getChallengeUser, getUserId, mfaStore, onMfaChallengeError, onMfaChallengeSuccess, onSendSmsCode, verificationProvider, sessionDurationMs, smsCodeLength, smsCodeTtlMs, smsMaxAttempts, smsResendCooldownMs, totpMaxAttempts }: MfaRouteProps<UserType>) => import("elysia/types").AddRoute<"", "local", {
|
|
3
12
|
decorator: {};
|
|
4
13
|
store: {
|
|
@@ -9,7 +18,39 @@ export declare const mfaChallenge: <UserType>({ authSessionStore, challengeRoute
|
|
|
9
18
|
}, {
|
|
10
19
|
typebox: {};
|
|
11
20
|
error: [];
|
|
12
|
-
}, import("elysia/types").DefaultMetadata, {
|
|
21
|
+
}, import("elysia/types").DefaultMetadata, {
|
|
22
|
+
[x: string]: {
|
|
23
|
+
get: {
|
|
24
|
+
body: unknown;
|
|
25
|
+
params: {};
|
|
26
|
+
query: unknown;
|
|
27
|
+
headers: unknown;
|
|
28
|
+
response: {
|
|
29
|
+
401: "No MFA challenge in progress";
|
|
30
|
+
200: {
|
|
31
|
+
backupCodesAvailable: boolean;
|
|
32
|
+
factors: Array<{
|
|
33
|
+
id: string;
|
|
34
|
+
label: string;
|
|
35
|
+
phone: string | null;
|
|
36
|
+
type: "sms" | "totp";
|
|
37
|
+
}>;
|
|
38
|
+
};
|
|
39
|
+
422: {
|
|
40
|
+
type: "validation";
|
|
41
|
+
title: "Validation Error";
|
|
42
|
+
status: 422;
|
|
43
|
+
detail?: string;
|
|
44
|
+
on: string;
|
|
45
|
+
found?: unknown;
|
|
46
|
+
property?: string;
|
|
47
|
+
expected?: string;
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
error: never;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
}, import("elysia/types").DefaultEphemeral, {
|
|
13
54
|
derive: {};
|
|
14
55
|
schema: {};
|
|
15
56
|
schemas: {};
|
|
@@ -19,16 +60,18 @@ export declare const mfaChallenge: <UserType>({ authSessionStore, challengeRoute
|
|
|
19
60
|
body: import("typebox").TObject<{
|
|
20
61
|
action: import("typebox").TOptional<import("typebox").TUnion<[import("typebox").TLiteral<"send">, import("typebox").TLiteral<"verify">]>>;
|
|
21
62
|
code: import("typebox").TOptional<import("typebox").TString>;
|
|
22
|
-
factor: import("typebox").TOptional<import("typebox").TLiteral<"sms">>;
|
|
63
|
+
factor: import("typebox").TOptional<import("typebox").TUnion<[import("typebox").TLiteral<"backup_codes">, import("typebox").TLiteral<"sms">, import("typebox").TLiteral<"totp">]>>;
|
|
64
|
+
factorId: import("typebox").TOptional<import("typebox").TString>;
|
|
23
65
|
}>;
|
|
24
66
|
cookie: import("elysia").TCookieObject<{
|
|
25
67
|
user_session_id: import("typebox").TOptional<import("typebox").TTemplateLiteral<"^.*-.*-.*-.*-.*$">>;
|
|
26
68
|
}>;
|
|
27
|
-
}, {}, `/${string}`>, import("elysia/types").MergeScopedSchemas<{}, {}, {}>>, {}, ({ body: { action, code, factor }, cookie: { user_session_id }, status, store: { session, unregisteredSession } }: {
|
|
69
|
+
}, {}, `/${string}`>, import("elysia/types").MergeScopedSchemas<{}, {}, {}>>, {}, ({ body: { action, code, factor, factorId }, cookie: { user_session_id }, status, store: { session, unregisteredSession } }: {
|
|
28
70
|
body: {
|
|
29
71
|
code?: string | undefined;
|
|
30
72
|
action?: "verify" | "send" | undefined;
|
|
31
|
-
|
|
73
|
+
factorId?: string | undefined;
|
|
74
|
+
factor?: "sms" | "backup_codes" | "totp" | undefined;
|
|
32
75
|
};
|
|
33
76
|
query: Record<string, string | undefined>;
|
|
34
77
|
params: {};
|
|
@@ -172,7 +215,8 @@ export declare const mfaChallenge: <UserType>({ authSessionStore, challengeRoute
|
|
|
172
215
|
body: import("typebox").TObject<{
|
|
173
216
|
action: import("typebox").TOptional<import("typebox").TUnion<[import("typebox").TLiteral<"send">, import("typebox").TLiteral<"verify">]>>;
|
|
174
217
|
code: import("typebox").TOptional<import("typebox").TString>;
|
|
175
|
-
factor: import("typebox").TOptional<import("typebox").TLiteral<"sms">>;
|
|
218
|
+
factor: import("typebox").TOptional<import("typebox").TUnion<[import("typebox").TLiteral<"backup_codes">, import("typebox").TLiteral<"sms">, import("typebox").TLiteral<"totp">]>>;
|
|
219
|
+
factorId: import("typebox").TOptional<import("typebox").TString>;
|
|
176
220
|
}>;
|
|
177
221
|
cookie: import("elysia").TCookieObject<{
|
|
178
222
|
user_session_id: import("typebox").TOptional<import("typebox").TTemplateLiteral<"^.*-.*-.*-.*-.*$">>;
|
|
@@ -195,8 +239,10 @@ export declare const mfaChallenge: <UserType>({ authSessionStore, challengeRoute
|
|
|
195
239
|
session: import("..").SessionRecord<UserType>;
|
|
196
240
|
unregisteredSession: import("..").UnregisteredSessionRecord;
|
|
197
241
|
};
|
|
198
|
-
}) => Promise<import("elysia").ElysiaStatus<"Too Many Requests", "SMS resend cooldown active", 429> | import("elysia").ElysiaStatus<"Bad Request" | "Too Many Requests" | "Service Unavailable", string, 400 | 429 | 503> | import("elysia").ElysiaStatus<"Unauthorized", "No MFA challenge in progress", 401> | import("elysia").ElysiaStatus<"OK", {
|
|
242
|
+
}) => Promise<import("elysia").ElysiaStatus<"Too Many Requests", "SMS resend cooldown active", 429> | import("elysia").ElysiaStatus<"Bad Request" | "Too Many Requests" | "Service Unavailable", string, 400 | 429 | 503> | import("elysia").ElysiaStatus<"Unauthorized", "No MFA challenge in progress", 401> | import("elysia").ElysiaStatus<"Bad Request", "No SMS code in progress", 400> | import("elysia").ElysiaStatus<"Bad Request", "SMS factor not found", 400> | import("elysia").ElysiaStatus<"OK", {
|
|
243
|
+
readonly factorId: string;
|
|
244
|
+
readonly phone: string;
|
|
199
245
|
readonly status: "sent";
|
|
200
|
-
}, 200> | import("elysia").ElysiaStatus<"Bad Request", "SMS code required", 400> | import("elysia").ElysiaStatus<"
|
|
246
|
+
}, 200> | import("elysia").ElysiaStatus<"Bad Request", "SMS code required", 400> | import("elysia").ElysiaStatus<"Unauthorized", "SMS code expired", 401> | import("elysia").ElysiaStatus<"Unauthorized", "Too many attempts", 401> | import("elysia").ElysiaStatus<"Unauthorized" | "Too Many Requests", "SMS code expired" | "Invalid MFA code", 401 | 429> | import("elysia").ElysiaStatus<"Unauthorized", "SMS challenge is no longer active", 401> | import("elysia").ElysiaStatus<"OK", {
|
|
201
247
|
readonly status: "authenticated";
|
|
202
248
|
}, 200> | import("elysia").ElysiaStatus<"Unauthorized", "Invalid MFA code", 401>>>;
|
package/dist/mfa/management.d.ts
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import { type MfaRouteProps } from './config';
|
|
2
|
+
import { type MfaFactorType } from './types';
|
|
3
|
+
export type MfaPublicFactor = {
|
|
4
|
+
id: string;
|
|
5
|
+
label: string;
|
|
6
|
+
phone: string | null;
|
|
7
|
+
type: Exclude<MfaFactorType, 'backup_codes'>;
|
|
8
|
+
};
|
|
2
9
|
export type MfaStatus = {
|
|
3
10
|
backupCodesRemaining: number;
|
|
4
11
|
enabled: boolean;
|
|
12
|
+
factors: MfaPublicFactor[];
|
|
5
13
|
smsBackup: {
|
|
6
14
|
enabled: boolean;
|
|
7
15
|
phone: string | null;
|
|
@@ -32,6 +40,7 @@ export declare const mfaManagementRoutes: <UserType>({ authSessionStore, getUser
|
|
|
32
40
|
200: {
|
|
33
41
|
backupCodesRemaining: number;
|
|
34
42
|
enabled: boolean;
|
|
43
|
+
factors: MfaPublicFactor[];
|
|
35
44
|
smsBackup: {
|
|
36
45
|
enabled: boolean;
|
|
37
46
|
phone: string | null;
|
|
@@ -54,20 +63,51 @@ export declare const mfaManagementRoutes: <UserType>({ authSessionStore, getUser
|
|
|
54
63
|
error: never;
|
|
55
64
|
};
|
|
56
65
|
};
|
|
66
|
+
} & {
|
|
67
|
+
[x: string]: {
|
|
68
|
+
delete: {
|
|
69
|
+
body: unknown;
|
|
70
|
+
params: {};
|
|
71
|
+
query: unknown;
|
|
72
|
+
headers: unknown;
|
|
73
|
+
response: {
|
|
74
|
+
401: "Recent authentication required" | "Authentication required";
|
|
75
|
+
200: {
|
|
76
|
+
readonly status: "disabled";
|
|
77
|
+
};
|
|
78
|
+
422: {
|
|
79
|
+
type: "validation";
|
|
80
|
+
title: "Validation Error";
|
|
81
|
+
status: 422;
|
|
82
|
+
detail?: string;
|
|
83
|
+
on: string;
|
|
84
|
+
found?: unknown;
|
|
85
|
+
property?: string;
|
|
86
|
+
expected?: string;
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
error: never;
|
|
90
|
+
};
|
|
91
|
+
};
|
|
57
92
|
}, import("elysia/types").DefaultEphemeral, {
|
|
58
93
|
derive: {};
|
|
59
94
|
schema: {};
|
|
60
95
|
schemas: {};
|
|
61
96
|
response: {};
|
|
62
97
|
error: [];
|
|
63
|
-
}, "delete", `/${string}`, import("elysia/types").IntersectIfObjectSchema<import("elysia").UnwrapRoute<{
|
|
98
|
+
}, "delete", `/${string}/factors/:factorId`, import("elysia/types").IntersectIfObjectSchema<import("elysia").UnwrapRoute<{
|
|
64
99
|
cookie: import("elysia").TCookieObject<{
|
|
65
100
|
user_session_id: import("typebox").TOptional<import("typebox").TTemplateLiteral<"^.*-.*-.*-.*-.*$">>;
|
|
66
101
|
}>;
|
|
67
|
-
|
|
102
|
+
params: import("typebox").TObject<{
|
|
103
|
+
factorId: import("typebox").TString;
|
|
104
|
+
}>;
|
|
105
|
+
}, {}, `/${string}/factors/:factorId`>, import("elysia/types").MergeScopedSchemas<{}, {}, {}>>, {}, ({ cookie: { user_session_id }, params: { factorId }, status, store: { session } }: {
|
|
68
106
|
body: unknown;
|
|
69
107
|
query: Record<string, string | undefined>;
|
|
70
|
-
params: {
|
|
108
|
+
params: {
|
|
109
|
+
factorId: string;
|
|
110
|
+
};
|
|
71
111
|
headers: Record<string, string | undefined>;
|
|
72
112
|
cookie: Record<string, import("elysia").Cookie<unknown>> & {
|
|
73
113
|
user_session_id: import("elysia").Cookie<`${string}-${string}-${string}-${string}-${string}` | undefined>;
|
|
@@ -208,7 +248,10 @@ export declare const mfaManagementRoutes: <UserType>({ authSessionStore, getUser
|
|
|
208
248
|
cookie: import("elysia").TCookieObject<{
|
|
209
249
|
user_session_id: import("typebox").TOptional<import("typebox").TTemplateLiteral<"^.*-.*-.*-.*-.*$">>;
|
|
210
250
|
}>;
|
|
211
|
-
|
|
251
|
+
params: import("typebox").TObject<{
|
|
252
|
+
factorId: import("typebox").TString;
|
|
253
|
+
}>;
|
|
254
|
+
}, {}, `/${string}/factors/:factorId`>, import("elysia/types").MergeScopedSchemas<{}, {}, {}>>>, NoInfer<{
|
|
212
255
|
decorator: {};
|
|
213
256
|
store: {
|
|
214
257
|
session: import("..").SessionRecord<UserType>;
|
|
@@ -226,6 +269,6 @@ export declare const mfaManagementRoutes: <UserType>({ authSessionStore, getUser
|
|
|
226
269
|
session: import("..").SessionRecord<UserType>;
|
|
227
270
|
unregisteredSession: import("..").UnregisteredSessionRecord;
|
|
228
271
|
};
|
|
229
|
-
}) => Promise<import("elysia").ElysiaStatus<"Unauthorized", "Recent authentication required", 401> | import("elysia").ElysiaStatus<"Unauthorized", "Authentication required", 401> | import("elysia").ElysiaStatus<"OK", {
|
|
230
|
-
readonly status: "
|
|
272
|
+
}) => Promise<import("elysia").ElysiaStatus<"Unauthorized", "Recent authentication required", 401> | import("elysia").ElysiaStatus<"Unauthorized", "Authentication required", 401> | import("elysia").ElysiaStatus<"Not Found", "MFA factor not found", 404> | import("elysia").ElysiaStatus<"OK", {
|
|
273
|
+
readonly status: "removed";
|
|
231
274
|
}, 200>>>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type AnyPgDatabase } from '../stores/postgres';
|
|
2
|
-
import type { MFAStore } from './types';
|
|
2
|
+
import type { MfaFactor, MFAStore } from './types';
|
|
3
3
|
export declare const mfaEnrollmentsTable: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
4
4
|
name: "auth_mfa_enrollments";
|
|
5
5
|
schema: undefined;
|
|
@@ -49,6 +49,21 @@ export declare const mfaEnrollmentsTable: import("drizzle-orm/pg-core").PgTableW
|
|
|
49
49
|
identity: undefined;
|
|
50
50
|
generated: undefined;
|
|
51
51
|
}>;
|
|
52
|
+
mfa_factors: import("drizzle-orm/pg-core").PgBuildColumn<"auth_mfa_enrollments", import("drizzle-orm/pg-core").Set$Type<import("drizzle-orm/pg-core").PgJsonbBuilder, MfaFactor[]>, {
|
|
53
|
+
name: string;
|
|
54
|
+
tableName: "auth_mfa_enrollments";
|
|
55
|
+
dataType: "object json";
|
|
56
|
+
data: MfaFactor[];
|
|
57
|
+
driverParam: unknown;
|
|
58
|
+
notNull: false;
|
|
59
|
+
hasDefault: false;
|
|
60
|
+
isPrimaryKey: false;
|
|
61
|
+
isAutoincrement: false;
|
|
62
|
+
hasRuntimeDefault: false;
|
|
63
|
+
enumValues: undefined;
|
|
64
|
+
identity: undefined;
|
|
65
|
+
generated: undefined;
|
|
66
|
+
}>;
|
|
52
67
|
sms_challenge_id: import("drizzle-orm/pg-core").PgBuildColumn<"auth_mfa_enrollments", import("drizzle-orm/pg-core").PgTextBuilder<[string, ...string[]]>, {
|
|
53
68
|
name: string;
|
|
54
69
|
tableName: "auth_mfa_enrollments";
|
|
@@ -124,6 +139,21 @@ export declare const mfaEnrollmentsTable: import("drizzle-orm/pg-core").PgTableW
|
|
|
124
139
|
identity: undefined;
|
|
125
140
|
generated: undefined;
|
|
126
141
|
}>;
|
|
142
|
+
sms_pending_factor_id: import("drizzle-orm/pg-core").PgBuildColumn<"auth_mfa_enrollments", import("drizzle-orm/pg-core").PgTextBuilder<[string, ...string[]]>, {
|
|
143
|
+
name: string;
|
|
144
|
+
tableName: "auth_mfa_enrollments";
|
|
145
|
+
dataType: "string";
|
|
146
|
+
data: string;
|
|
147
|
+
driverParam: string;
|
|
148
|
+
notNull: false;
|
|
149
|
+
hasDefault: false;
|
|
150
|
+
isPrimaryKey: false;
|
|
151
|
+
isAutoincrement: false;
|
|
152
|
+
hasRuntimeDefault: false;
|
|
153
|
+
enumValues: undefined;
|
|
154
|
+
identity: undefined;
|
|
155
|
+
generated: undefined;
|
|
156
|
+
}>;
|
|
127
157
|
sms_pending_purpose: import("drizzle-orm/pg-core").PgBuildColumn<"auth_mfa_enrollments", import("drizzle-orm/pg-core").Set$Type<import("drizzle-orm/pg-core").PgTextBuilder<[string, ...string[]]>, import("..").VerificationPurpose | undefined>, {
|
|
128
158
|
name: string;
|
|
129
159
|
tableName: "auth_mfa_enrollments";
|
package/dist/mfa/rotation.d.ts
CHANGED
package/dist/mfa/routes.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ export declare const mfaRoutes: <UserType>(config: MfaRouteProps<UserType>) => E
|
|
|
22
22
|
200: {
|
|
23
23
|
backupCodesRemaining: number;
|
|
24
24
|
enabled: boolean;
|
|
25
|
+
factors: import("./management").MfaPublicFactor[];
|
|
25
26
|
smsBackup: {
|
|
26
27
|
enabled: boolean;
|
|
27
28
|
phone: string | null;
|
|
@@ -70,16 +71,52 @@ export declare const mfaRoutes: <UserType>(config: MfaRouteProps<UserType>) => E
|
|
|
70
71
|
error: never;
|
|
71
72
|
};
|
|
72
73
|
};
|
|
74
|
+
} & {
|
|
75
|
+
[x: string]: {
|
|
76
|
+
factors: {
|
|
77
|
+
":factorId": {
|
|
78
|
+
delete: {
|
|
79
|
+
body: unknown;
|
|
80
|
+
params: {
|
|
81
|
+
factorId: string;
|
|
82
|
+
};
|
|
83
|
+
query: unknown;
|
|
84
|
+
headers: unknown;
|
|
85
|
+
response: {
|
|
86
|
+
401: "Recent authentication required" | "Authentication required";
|
|
87
|
+
404: "MFA factor not found";
|
|
88
|
+
200: {
|
|
89
|
+
readonly status: "removed";
|
|
90
|
+
};
|
|
91
|
+
422: {
|
|
92
|
+
type: "validation";
|
|
93
|
+
title: "Validation Error";
|
|
94
|
+
status: 422;
|
|
95
|
+
detail?: string;
|
|
96
|
+
on: string;
|
|
97
|
+
found?: unknown;
|
|
98
|
+
property?: string;
|
|
99
|
+
expected?: string;
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
error: never;
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
};
|
|
73
107
|
} & {
|
|
74
108
|
[x: string]: {
|
|
75
109
|
post: {
|
|
76
|
-
body:
|
|
110
|
+
body: {
|
|
111
|
+
label?: string | undefined;
|
|
112
|
+
};
|
|
77
113
|
params: {};
|
|
78
114
|
query: unknown;
|
|
79
115
|
headers: unknown;
|
|
80
116
|
response: {
|
|
81
117
|
401: "Recent authentication required" | "Authentication required";
|
|
82
118
|
200: {
|
|
119
|
+
readonly factorId: string;
|
|
83
120
|
readonly secret: string;
|
|
84
121
|
readonly uri: string;
|
|
85
122
|
};
|
|
@@ -101,6 +138,7 @@ export declare const mfaRoutes: <UserType>(config: MfaRouteProps<UserType>) => E
|
|
|
101
138
|
[x: string]: {
|
|
102
139
|
post: {
|
|
103
140
|
body: {
|
|
141
|
+
factorId?: string | undefined;
|
|
104
142
|
code: string;
|
|
105
143
|
};
|
|
106
144
|
params: {};
|
|
@@ -130,6 +168,7 @@ export declare const mfaRoutes: <UserType>(config: MfaRouteProps<UserType>) => E
|
|
|
130
168
|
[x: string]: {
|
|
131
169
|
post: {
|
|
132
170
|
body: {
|
|
171
|
+
label?: string | undefined;
|
|
133
172
|
phone: string;
|
|
134
173
|
};
|
|
135
174
|
params: {};
|
|
@@ -140,6 +179,7 @@ export declare const mfaRoutes: <UserType>(config: MfaRouteProps<UserType>) => E
|
|
|
140
179
|
401: "Recent authentication required" | "Authentication required";
|
|
141
180
|
501: "SMS MFA is not configured";
|
|
142
181
|
200: {
|
|
182
|
+
readonly factorId: string;
|
|
143
183
|
readonly phone: string;
|
|
144
184
|
};
|
|
145
185
|
422: {
|
|
@@ -162,6 +202,7 @@ export declare const mfaRoutes: <UserType>(config: MfaRouteProps<UserType>) => E
|
|
|
162
202
|
[x: string]: {
|
|
163
203
|
post: {
|
|
164
204
|
body: {
|
|
205
|
+
factorId?: string | undefined;
|
|
165
206
|
code: string;
|
|
166
207
|
};
|
|
167
208
|
params: {};
|
|
@@ -189,13 +230,46 @@ export declare const mfaRoutes: <UserType>(config: MfaRouteProps<UserType>) => E
|
|
|
189
230
|
error: never;
|
|
190
231
|
};
|
|
191
232
|
};
|
|
233
|
+
} & {
|
|
234
|
+
[x: string]: {
|
|
235
|
+
get: {
|
|
236
|
+
body: unknown;
|
|
237
|
+
params: {};
|
|
238
|
+
query: unknown;
|
|
239
|
+
headers: unknown;
|
|
240
|
+
response: {
|
|
241
|
+
401: "No MFA challenge in progress";
|
|
242
|
+
200: {
|
|
243
|
+
backupCodesAvailable: boolean;
|
|
244
|
+
factors: Array<{
|
|
245
|
+
id: string;
|
|
246
|
+
label: string;
|
|
247
|
+
phone: string | null;
|
|
248
|
+
type: "sms" | "totp";
|
|
249
|
+
}>;
|
|
250
|
+
};
|
|
251
|
+
422: {
|
|
252
|
+
type: "validation";
|
|
253
|
+
title: "Validation Error";
|
|
254
|
+
status: 422;
|
|
255
|
+
detail?: string;
|
|
256
|
+
on: string;
|
|
257
|
+
found?: unknown;
|
|
258
|
+
property?: string;
|
|
259
|
+
expected?: string;
|
|
260
|
+
};
|
|
261
|
+
};
|
|
262
|
+
error: never;
|
|
263
|
+
};
|
|
264
|
+
};
|
|
192
265
|
} & {
|
|
193
266
|
[x: string]: {
|
|
194
267
|
post: {
|
|
195
268
|
body: {
|
|
196
269
|
code?: string | undefined;
|
|
197
270
|
action?: "verify" | "send" | undefined;
|
|
198
|
-
|
|
271
|
+
factorId?: string | undefined;
|
|
272
|
+
factor?: "sms" | "backup_codes" | "totp" | undefined;
|
|
199
273
|
};
|
|
200
274
|
params: {};
|
|
201
275
|
query: unknown;
|
|
@@ -204,6 +278,8 @@ export declare const mfaRoutes: <UserType>(config: MfaRouteProps<UserType>) => E
|
|
|
204
278
|
400: string;
|
|
205
279
|
401: "SMS code expired" | "Too many attempts" | "SMS challenge is no longer active" | "No MFA challenge in progress" | "Invalid MFA code";
|
|
206
280
|
200: {
|
|
281
|
+
readonly factorId: string;
|
|
282
|
+
readonly phone: string;
|
|
207
283
|
readonly status: "sent";
|
|
208
284
|
} | {
|
|
209
285
|
readonly status: "authenticated";
|
package/dist/mfa/sms.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type VerificationCheckInput, type VerificationProvider } from '../verification/types';
|
|
2
2
|
import { type MfaRouteProps, type SmsCodeMessage } from './config';
|
|
3
|
-
import type
|
|
3
|
+
import { type MfaEnrollment, type MFAStore } from './types';
|
|
4
4
|
export declare class SmsChallengeConflictError extends Error {
|
|
5
5
|
name: string;
|
|
6
6
|
}
|
|
@@ -58,6 +58,7 @@ export declare const mfaSmsRoutes: <UserType>({ authSessionStore, getUserId, mfa
|
|
|
58
58
|
[x: string]: {
|
|
59
59
|
post: {
|
|
60
60
|
body: {
|
|
61
|
+
label?: string | undefined;
|
|
61
62
|
phone: string;
|
|
62
63
|
};
|
|
63
64
|
params: {};
|
|
@@ -68,6 +69,7 @@ export declare const mfaSmsRoutes: <UserType>({ authSessionStore, getUserId, mfa
|
|
|
68
69
|
401: "Recent authentication required" | "Authentication required";
|
|
69
70
|
501: "SMS MFA is not configured";
|
|
70
71
|
200: {
|
|
72
|
+
readonly factorId: string;
|
|
71
73
|
readonly phone: string;
|
|
72
74
|
};
|
|
73
75
|
422: {
|
|
@@ -95,12 +97,14 @@ export declare const mfaSmsRoutes: <UserType>({ authSessionStore, getUserId, mfa
|
|
|
95
97
|
}, "post", `/${string}`, import("elysia/types").IntersectIfObjectSchema<import("elysia").UnwrapRoute<{
|
|
96
98
|
body: import("typebox").TObject<{
|
|
97
99
|
code: import("typebox").TString;
|
|
100
|
+
factorId: import("typebox").TOptional<import("typebox").TString>;
|
|
98
101
|
}>;
|
|
99
102
|
cookie: import("elysia").TCookieObject<{
|
|
100
103
|
user_session_id: import("typebox").TOptional<import("typebox").TTemplateLiteral<"^.*-.*-.*-.*-.*$">>;
|
|
101
104
|
}>;
|
|
102
|
-
}, {}, `/${string}`>, import("elysia/types").MergeScopedSchemas<{}, {}, {}>>, {}, ({ body: { code }, cookie: { user_session_id }, status, store: { session } }: {
|
|
105
|
+
}, {}, `/${string}`>, import("elysia/types").MergeScopedSchemas<{}, {}, {}>>, {}, ({ body: { code, factorId }, cookie: { user_session_id }, status, store: { session } }: {
|
|
103
106
|
body: {
|
|
107
|
+
factorId?: string | undefined;
|
|
104
108
|
code: string;
|
|
105
109
|
};
|
|
106
110
|
query: Record<string, string | undefined>;
|
|
@@ -244,6 +248,7 @@ export declare const mfaSmsRoutes: <UserType>({ authSessionStore, getUserId, mfa
|
|
|
244
248
|
defer: (fn: import("elysia/types").AfterResponseHandler<NoInfer<import("elysia/types").IntersectIfObjectSchema<import("elysia").UnwrapRoute<{
|
|
245
249
|
body: import("typebox").TObject<{
|
|
246
250
|
code: import("typebox").TString;
|
|
251
|
+
factorId: import("typebox").TOptional<import("typebox").TString>;
|
|
247
252
|
}>;
|
|
248
253
|
cookie: import("elysia").TCookieObject<{
|
|
249
254
|
user_session_id: import("typebox").TOptional<import("typebox").TTemplateLiteral<"^.*-.*-.*-.*-.*$">>;
|
package/dist/mfa/totp.d.ts
CHANGED
|
@@ -12,13 +12,16 @@ export declare const mfaTotpRoutes: <UserType>({ authSessionStore, backupCodeCou
|
|
|
12
12
|
}, import("elysia/types").DefaultMetadata, {
|
|
13
13
|
[x: string]: {
|
|
14
14
|
post: {
|
|
15
|
-
body:
|
|
15
|
+
body: {
|
|
16
|
+
label?: string | undefined;
|
|
17
|
+
};
|
|
16
18
|
params: {};
|
|
17
19
|
query: unknown;
|
|
18
20
|
headers: unknown;
|
|
19
21
|
response: {
|
|
20
22
|
401: "Recent authentication required" | "Authentication required";
|
|
21
23
|
200: {
|
|
24
|
+
readonly factorId: string;
|
|
22
25
|
readonly secret: string;
|
|
23
26
|
readonly uri: string;
|
|
24
27
|
};
|
|
@@ -45,12 +48,14 @@ export declare const mfaTotpRoutes: <UserType>({ authSessionStore, backupCodeCou
|
|
|
45
48
|
}, "post", `/${string}`, import("elysia/types").IntersectIfObjectSchema<import("elysia").UnwrapRoute<{
|
|
46
49
|
body: import("typebox").TObject<{
|
|
47
50
|
code: import("typebox").TString;
|
|
51
|
+
factorId: import("typebox").TOptional<import("typebox").TString>;
|
|
48
52
|
}>;
|
|
49
53
|
cookie: import("elysia").TCookieObject<{
|
|
50
54
|
user_session_id: import("typebox").TOptional<import("typebox").TTemplateLiteral<"^.*-.*-.*-.*-.*$">>;
|
|
51
55
|
}>;
|
|
52
|
-
}, {}, `/${string}`>, import("elysia/types").MergeScopedSchemas<{}, {}, {}>>, {}, ({ body: { code }, cookie: { user_session_id }, status, store: { session } }: {
|
|
56
|
+
}, {}, `/${string}`>, import("elysia/types").MergeScopedSchemas<{}, {}, {}>>, {}, ({ body: { code, factorId }, cookie: { user_session_id }, status, store: { session } }: {
|
|
53
57
|
body: {
|
|
58
|
+
factorId?: string | undefined;
|
|
54
59
|
code: string;
|
|
55
60
|
};
|
|
56
61
|
query: Record<string, string | undefined>;
|
|
@@ -194,6 +199,7 @@ export declare const mfaTotpRoutes: <UserType>({ authSessionStore, backupCodeCou
|
|
|
194
199
|
defer: (fn: import("elysia/types").AfterResponseHandler<NoInfer<import("elysia/types").IntersectIfObjectSchema<import("elysia").UnwrapRoute<{
|
|
195
200
|
body: import("typebox").TObject<{
|
|
196
201
|
code: import("typebox").TString;
|
|
202
|
+
factorId: import("typebox").TOptional<import("typebox").TString>;
|
|
197
203
|
}>;
|
|
198
204
|
cookie: import("elysia").TCookieObject<{
|
|
199
205
|
user_session_id: import("typebox").TOptional<import("typebox").TTemplateLiteral<"^.*-.*-.*-.*-.*$">>;
|
package/dist/mfa/types.d.ts
CHANGED
|
@@ -1,13 +1,33 @@
|
|
|
1
1
|
import type { VerificationPurpose } from '../verification/types';
|
|
2
2
|
export type MfaFactorType = 'backup_codes' | 'sms' | 'totp';
|
|
3
|
+
export type SmsMfaFactor = {
|
|
4
|
+
id: string;
|
|
5
|
+
label: string;
|
|
6
|
+
phone: string;
|
|
7
|
+
type: 'sms';
|
|
8
|
+
verified: boolean;
|
|
9
|
+
};
|
|
10
|
+
export type TotpMfaFactor = {
|
|
11
|
+
id: string;
|
|
12
|
+
label: string;
|
|
13
|
+
secretCiphertext: string;
|
|
14
|
+
type: 'totp';
|
|
15
|
+
verified: boolean;
|
|
16
|
+
};
|
|
17
|
+
export type MfaFactor = SmsMfaFactor | TotpMfaFactor;
|
|
3
18
|
export type MfaEnrollment = {
|
|
4
19
|
backupCodeHashes: string[];
|
|
5
20
|
createdAt: number;
|
|
21
|
+
/** Labeled factor collection. Undefined means a legacy single-factor row that
|
|
22
|
+
* has not yet been rewritten by a management operation. */
|
|
23
|
+
factors?: MfaFactor[];
|
|
6
24
|
lastUsedAt?: number;
|
|
7
25
|
smsFailedAttempts?: number;
|
|
8
26
|
smsPendingCodeHash?: string;
|
|
9
27
|
smsPendingCodeExpiresAt?: number;
|
|
10
28
|
smsPendingPurpose?: VerificationPurpose;
|
|
29
|
+
/** Factor selected for the currently active SMS enrollment/challenge. */
|
|
30
|
+
smsPendingFactorId?: string;
|
|
11
31
|
smsProviderReference?: string;
|
|
12
32
|
smsCodeSentAt?: number;
|
|
13
33
|
/** Opaque generation binding every update to the currently active challenge. */
|
|
@@ -29,6 +49,7 @@ export type MFAStore = {
|
|
|
29
49
|
}) => Promise<boolean>;
|
|
30
50
|
completeSmsChallenge: (input: {
|
|
31
51
|
challengeId: string;
|
|
52
|
+
factors?: MfaFactor[];
|
|
32
53
|
lastUsedAt?: number;
|
|
33
54
|
smsVerified: boolean;
|
|
34
55
|
userId: string;
|
|
@@ -55,4 +76,30 @@ export type MFAStore = {
|
|
|
55
76
|
}) => Promise<void>;
|
|
56
77
|
saveEnrollment: (enrollment: MfaEnrollment) => Promise<void>;
|
|
57
78
|
};
|
|
79
|
+
export declare const getMfaFactors: (enrollment: MfaEnrollment) => MfaFactor[];
|
|
58
80
|
export declare const isMfaEnrolled: (enrollment: MfaEnrollment | undefined) => boolean;
|
|
81
|
+
/** Keep the historical single-factor columns synchronized for consumers that
|
|
82
|
+
* have not moved to the factor collection yet. */
|
|
83
|
+
export declare const withMfaFactors: (enrollment: MfaEnrollment, factors: MfaFactor[]) => {
|
|
84
|
+
factors: MfaFactor[];
|
|
85
|
+
smsPhone: string | undefined;
|
|
86
|
+
smsVerified: boolean;
|
|
87
|
+
totpSecretCiphertext: string | undefined;
|
|
88
|
+
totpVerified: boolean;
|
|
89
|
+
backupCodeHashes: string[];
|
|
90
|
+
createdAt: number;
|
|
91
|
+
lastUsedAt?: number;
|
|
92
|
+
smsFailedAttempts?: number;
|
|
93
|
+
smsPendingCodeHash?: string;
|
|
94
|
+
smsPendingCodeExpiresAt?: number;
|
|
95
|
+
smsPendingPurpose?: VerificationPurpose;
|
|
96
|
+
/** Factor selected for the currently active SMS enrollment/challenge. */
|
|
97
|
+
smsPendingFactorId?: string;
|
|
98
|
+
smsProviderReference?: string;
|
|
99
|
+
smsCodeSentAt?: number;
|
|
100
|
+
/** Opaque generation binding every update to the currently active challenge. */
|
|
101
|
+
smsChallengeId?: string;
|
|
102
|
+
totpFailedAttempts?: number;
|
|
103
|
+
updatedAt: number;
|
|
104
|
+
userId: string;
|
|
105
|
+
};
|