@spfn/auth 0.3.0-beta.26 → 0.3.0-beta.28
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/README.md +195 -9
- package/dist/client-proof.d.ts +8 -1
- package/dist/client-proof.js +58 -2
- package/dist/client-proof.js.map +1 -1
- package/dist/config.d.ts +6 -6
- package/dist/errors.d.ts +104 -2
- package/dist/errors.js +69 -0
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +504 -446
- package/dist/index.js +114 -40
- package/dist/index.js.map +1 -1
- package/dist/{machine-principals-CdEgxOB1.d.ts → machine-principals-B0bjs-0K.d.ts} +914 -285
- package/dist/server.d.ts +430 -175
- package/dist/server.js +1710 -655
- package/dist/server.js.map +1 -1
- package/migrations/20260926054221_friendly_hitman/migration.sql +28 -0
- package/migrations/20260926054221_friendly_hitman/snapshot.json +6691 -0
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { S as SessionBindingType, K as KeyAlgorithmType, h as KeyPlatformType, l as SocialProvider } from './types-CTdoTOxM.js';
|
|
2
1
|
import * as _simplewebauthn_server from '@simplewebauthn/server';
|
|
3
|
-
import {
|
|
2
|
+
import { AuthenticationResponseJSON, PublicKeyCredentialRequestOptionsJSON, RegistrationResponseJSON, PublicKeyCredentialCreationOptionsJSON } from '@simplewebauthn/server';
|
|
3
|
+
import { S as SessionBindingType, K as KeyAlgorithmType, h as KeyPlatformType, l as SocialProvider } from './types-CTdoTOxM.js';
|
|
4
4
|
import * as _spfn_core_route from '@spfn/core/route';
|
|
5
5
|
import * as _sinclair_typebox from '@sinclair/typebox';
|
|
6
6
|
import { Static } from '@sinclair/typebox';
|
|
@@ -10,85 +10,283 @@ import { Context } from 'hono';
|
|
|
10
10
|
import { User } from '@spfn/auth/server';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
* Permission information for client/API responses
|
|
13
|
+
* @spfn/auth - Passkeys Entity
|
|
14
|
+
*
|
|
15
|
+
* A WebAuthn credential the account owner enrolled on one of their devices.
|
|
16
|
+
* It is a *credential*, not a session: an assertion proves who is asking, and
|
|
17
|
+
* the ordinary device key in `user_public_keys` is what the request afterwards
|
|
18
|
+
* is signed with. The two tables therefore never stand in for each other.
|
|
19
|
+
*
|
|
20
|
+
* Nothing here is a bearer value, so nothing is hashed. `publicKey` is public by
|
|
21
|
+
* construction and `credentialId` is a handle the authenticator hands to any
|
|
22
|
+
* origin that asks — storing either in the clear costs nothing, and the lookup
|
|
23
|
+
* on `credentialId` has to be a plain equality match on an indexed column.
|
|
24
|
+
*
|
|
25
|
+
* Revocation is soft, and `credentialId` stays unique across live and revoked
|
|
26
|
+
* rows alike: a credential someone cut off must never become enrollable again,
|
|
27
|
+
* on this account or on another one.
|
|
29
28
|
*/
|
|
30
|
-
interface Permission {
|
|
31
|
-
id: number;
|
|
32
|
-
name: string;
|
|
33
|
-
displayName: string;
|
|
34
|
-
description: string | null;
|
|
35
|
-
category: string | null;
|
|
36
|
-
isBuiltin: boolean;
|
|
37
|
-
isSystem: boolean;
|
|
38
|
-
isActive: boolean;
|
|
39
|
-
metadata: Record<string, any> | null;
|
|
40
|
-
createdAt: Date;
|
|
41
|
-
updatedAt: Date;
|
|
42
|
-
}
|
|
43
|
-
interface AuthSession {
|
|
44
|
-
userId: number;
|
|
45
|
-
publicId: string;
|
|
46
|
-
email: string | null;
|
|
47
|
-
emailVerified: boolean;
|
|
48
|
-
phoneVerified: boolean;
|
|
49
|
-
hasPassword: boolean;
|
|
50
|
-
role: Role;
|
|
51
|
-
permissions: Permission[];
|
|
52
|
-
}
|
|
53
|
-
interface ProfileInfo {
|
|
54
|
-
profileId: number;
|
|
55
|
-
displayName: string | null;
|
|
56
|
-
firstName: string | null;
|
|
57
|
-
lastName: string | null;
|
|
58
|
-
avatarUrl: string | null;
|
|
59
|
-
bio: string | null;
|
|
60
|
-
locale: string;
|
|
61
|
-
timezone: string;
|
|
62
|
-
website: string | null;
|
|
63
|
-
location: string | null;
|
|
64
|
-
company: string | null;
|
|
65
|
-
jobTitle: string | null;
|
|
66
|
-
metadata: Record<string, any> | null;
|
|
67
|
-
createdAt: Date;
|
|
68
|
-
updatedAt: Date;
|
|
69
|
-
}
|
|
70
29
|
/**
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* Complete user data including:
|
|
74
|
-
* - User fields at top level (userId, email, etc.)
|
|
75
|
-
* - Profile data as nested field (optional)
|
|
30
|
+
* Whether the credential can leave the authenticator that minted it.
|
|
76
31
|
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
32
|
+
* `multiDevice` is a synced passkey (iCloud Keychain, Google Password Manager);
|
|
33
|
+
* `singleDevice` is bound to one authenticator. Reported by the authenticator at
|
|
34
|
+
* enrollment and shown in the management list, because "this one is only on that
|
|
35
|
+
* phone" is what the owner needs to know before revoking the other entry.
|
|
79
36
|
*/
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
37
|
+
declare const PASSKEY_DEVICE_TYPES: readonly ["singleDevice", "multiDevice"];
|
|
38
|
+
type PasskeyDeviceType = typeof PASSKEY_DEVICE_TYPES[number];
|
|
39
|
+
/** How long a label may be — the key list's `deviceName` bound, for the same reason. */
|
|
40
|
+
declare const PASSKEY_LABEL_MAX_LENGTH = 64;
|
|
41
|
+
declare const passkeys: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
42
|
+
name: "passkeys";
|
|
43
|
+
schema: string;
|
|
44
|
+
columns: {
|
|
45
|
+
createdAt: drizzle_orm_pg_core.PgBuildColumn<"passkeys", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>, {
|
|
46
|
+
name: string;
|
|
47
|
+
tableName: "passkeys";
|
|
48
|
+
dataType: "object date";
|
|
49
|
+
data: Date;
|
|
50
|
+
driverParam: string;
|
|
51
|
+
notNull: true;
|
|
52
|
+
hasDefault: true;
|
|
53
|
+
isPrimaryKey: false;
|
|
54
|
+
isAutoincrement: false;
|
|
55
|
+
hasRuntimeDefault: false;
|
|
56
|
+
enumValues: undefined;
|
|
57
|
+
identity: undefined;
|
|
58
|
+
generated: undefined;
|
|
59
|
+
}>;
|
|
60
|
+
updatedAt: drizzle_orm_pg_core.PgBuildColumn<"passkeys", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>>, {
|
|
61
|
+
name: string;
|
|
62
|
+
tableName: "passkeys";
|
|
63
|
+
dataType: "object date";
|
|
64
|
+
data: Date;
|
|
65
|
+
driverParam: string;
|
|
66
|
+
notNull: true;
|
|
67
|
+
hasDefault: true;
|
|
68
|
+
isPrimaryKey: false;
|
|
69
|
+
isAutoincrement: false;
|
|
70
|
+
hasRuntimeDefault: false;
|
|
71
|
+
enumValues: undefined;
|
|
72
|
+
identity: undefined;
|
|
73
|
+
generated: undefined;
|
|
74
|
+
}>;
|
|
75
|
+
id: drizzle_orm_pg_core.PgBuildColumn<"passkeys", drizzle_orm_pg_core.SetIsPrimaryKey<drizzle_orm_pg_core.PgBigSerial53Builder>, {
|
|
76
|
+
name: string;
|
|
77
|
+
tableName: "passkeys";
|
|
78
|
+
dataType: "number int53";
|
|
79
|
+
data: number;
|
|
80
|
+
driverParam: number;
|
|
81
|
+
notNull: true;
|
|
82
|
+
hasDefault: true;
|
|
83
|
+
isPrimaryKey: false;
|
|
84
|
+
isAutoincrement: false;
|
|
85
|
+
hasRuntimeDefault: false;
|
|
86
|
+
enumValues: undefined;
|
|
87
|
+
identity: undefined;
|
|
88
|
+
generated: undefined;
|
|
89
|
+
}>;
|
|
90
|
+
userId: drizzle_orm_pg_core.PgBuildColumn<"passkeys", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgBigInt53Builder>, {
|
|
91
|
+
name: string;
|
|
92
|
+
tableName: "passkeys";
|
|
93
|
+
dataType: "number int53";
|
|
94
|
+
data: number;
|
|
95
|
+
driverParam: string | number;
|
|
96
|
+
notNull: true;
|
|
97
|
+
hasDefault: false;
|
|
98
|
+
isPrimaryKey: false;
|
|
99
|
+
isAutoincrement: false;
|
|
100
|
+
hasRuntimeDefault: false;
|
|
101
|
+
enumValues: undefined;
|
|
102
|
+
identity: undefined;
|
|
103
|
+
generated: undefined;
|
|
104
|
+
}>;
|
|
105
|
+
credentialId: drizzle_orm_pg_core.PgBuildColumn<"passkeys", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
|
|
106
|
+
name: string;
|
|
107
|
+
tableName: "passkeys";
|
|
108
|
+
dataType: "string";
|
|
109
|
+
data: string;
|
|
110
|
+
driverParam: string;
|
|
111
|
+
notNull: true;
|
|
112
|
+
hasDefault: false;
|
|
113
|
+
isPrimaryKey: false;
|
|
114
|
+
isAutoincrement: false;
|
|
115
|
+
hasRuntimeDefault: false;
|
|
116
|
+
enumValues: undefined;
|
|
117
|
+
identity: undefined;
|
|
118
|
+
generated: undefined;
|
|
119
|
+
}>;
|
|
120
|
+
publicKey: drizzle_orm_pg_core.PgBuildColumn<"passkeys", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
|
|
121
|
+
name: string;
|
|
122
|
+
tableName: "passkeys";
|
|
123
|
+
dataType: "string";
|
|
124
|
+
data: string;
|
|
125
|
+
driverParam: string;
|
|
126
|
+
notNull: true;
|
|
127
|
+
hasDefault: false;
|
|
128
|
+
isPrimaryKey: false;
|
|
129
|
+
isAutoincrement: false;
|
|
130
|
+
hasRuntimeDefault: false;
|
|
131
|
+
enumValues: undefined;
|
|
132
|
+
identity: undefined;
|
|
133
|
+
generated: undefined;
|
|
134
|
+
}>;
|
|
135
|
+
counter: drizzle_orm_pg_core.PgBuildColumn<"passkeys", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgIntegerBuilder>>, {
|
|
136
|
+
name: string;
|
|
137
|
+
tableName: "passkeys";
|
|
138
|
+
dataType: "number int32";
|
|
139
|
+
data: number;
|
|
140
|
+
driverParam: string | number;
|
|
141
|
+
notNull: true;
|
|
142
|
+
hasDefault: true;
|
|
143
|
+
isPrimaryKey: false;
|
|
144
|
+
isAutoincrement: false;
|
|
145
|
+
hasRuntimeDefault: false;
|
|
146
|
+
enumValues: undefined;
|
|
147
|
+
identity: undefined;
|
|
148
|
+
generated: undefined;
|
|
149
|
+
}>;
|
|
150
|
+
transports: drizzle_orm_pg_core.PgBuildColumn<"passkeys", drizzle_orm_pg_core.SetDimensions<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, 1>, {
|
|
151
|
+
name: string;
|
|
152
|
+
tableName: "passkeys";
|
|
153
|
+
dataType: "string";
|
|
154
|
+
data: string[];
|
|
155
|
+
driverParam: string | string[];
|
|
156
|
+
notNull: false;
|
|
157
|
+
hasDefault: false;
|
|
158
|
+
isPrimaryKey: false;
|
|
159
|
+
isAutoincrement: false;
|
|
160
|
+
hasRuntimeDefault: false;
|
|
161
|
+
enumValues: undefined;
|
|
162
|
+
identity: undefined;
|
|
163
|
+
generated: undefined;
|
|
164
|
+
}>;
|
|
165
|
+
deviceType: drizzle_orm_pg_core.PgBuildColumn<"passkeys", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<["singleDevice", "multiDevice"] & [string, ...string[]]>>, {
|
|
166
|
+
name: string;
|
|
167
|
+
tableName: "passkeys";
|
|
168
|
+
dataType: "string enum";
|
|
169
|
+
data: "singleDevice" | "multiDevice";
|
|
170
|
+
driverParam: string;
|
|
171
|
+
notNull: true;
|
|
172
|
+
hasDefault: false;
|
|
173
|
+
isPrimaryKey: false;
|
|
174
|
+
isAutoincrement: false;
|
|
175
|
+
hasRuntimeDefault: false;
|
|
176
|
+
enumValues: ["singleDevice", "multiDevice"] & [string, ...string[]];
|
|
177
|
+
identity: undefined;
|
|
178
|
+
generated: undefined;
|
|
179
|
+
}>;
|
|
180
|
+
backedUp: drizzle_orm_pg_core.PgBuildColumn<"passkeys", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgBooleanBuilder>>, {
|
|
181
|
+
name: string;
|
|
182
|
+
tableName: "passkeys";
|
|
183
|
+
dataType: "boolean";
|
|
184
|
+
data: boolean;
|
|
185
|
+
driverParam: boolean;
|
|
186
|
+
notNull: true;
|
|
187
|
+
hasDefault: true;
|
|
188
|
+
isPrimaryKey: false;
|
|
189
|
+
isAutoincrement: false;
|
|
190
|
+
hasRuntimeDefault: false;
|
|
191
|
+
enumValues: undefined;
|
|
192
|
+
identity: undefined;
|
|
193
|
+
generated: undefined;
|
|
194
|
+
}>;
|
|
195
|
+
aaguid: drizzle_orm_pg_core.PgBuildColumn<"passkeys", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
|
|
196
|
+
name: string;
|
|
197
|
+
tableName: "passkeys";
|
|
198
|
+
dataType: "string";
|
|
199
|
+
data: string;
|
|
200
|
+
driverParam: string;
|
|
201
|
+
notNull: false;
|
|
202
|
+
hasDefault: false;
|
|
203
|
+
isPrimaryKey: false;
|
|
204
|
+
isAutoincrement: false;
|
|
205
|
+
hasRuntimeDefault: false;
|
|
206
|
+
enumValues: undefined;
|
|
207
|
+
identity: undefined;
|
|
208
|
+
generated: undefined;
|
|
209
|
+
}>;
|
|
210
|
+
label: drizzle_orm_pg_core.PgBuildColumn<"passkeys", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
|
|
211
|
+
name: string;
|
|
212
|
+
tableName: "passkeys";
|
|
213
|
+
dataType: "string";
|
|
214
|
+
data: string;
|
|
215
|
+
driverParam: string;
|
|
216
|
+
notNull: false;
|
|
217
|
+
hasDefault: false;
|
|
218
|
+
isPrimaryKey: false;
|
|
219
|
+
isAutoincrement: false;
|
|
220
|
+
hasRuntimeDefault: false;
|
|
221
|
+
enumValues: undefined;
|
|
222
|
+
identity: undefined;
|
|
223
|
+
generated: undefined;
|
|
224
|
+
}>;
|
|
225
|
+
secondFactor: drizzle_orm_pg_core.PgBuildColumn<"passkeys", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgBooleanBuilder>>, {
|
|
226
|
+
name: string;
|
|
227
|
+
tableName: "passkeys";
|
|
228
|
+
dataType: "boolean";
|
|
229
|
+
data: boolean;
|
|
230
|
+
driverParam: boolean;
|
|
231
|
+
notNull: true;
|
|
232
|
+
hasDefault: true;
|
|
233
|
+
isPrimaryKey: false;
|
|
234
|
+
isAutoincrement: false;
|
|
235
|
+
hasRuntimeDefault: false;
|
|
236
|
+
enumValues: undefined;
|
|
237
|
+
identity: undefined;
|
|
238
|
+
generated: undefined;
|
|
239
|
+
}>;
|
|
240
|
+
lastUsedAt: drizzle_orm_pg_core.PgBuildColumn<"passkeys", drizzle_orm_pg_core.PgTimestampBuilder, {
|
|
241
|
+
name: string;
|
|
242
|
+
tableName: "passkeys";
|
|
243
|
+
dataType: "object date";
|
|
244
|
+
data: Date;
|
|
245
|
+
driverParam: string;
|
|
246
|
+
notNull: false;
|
|
247
|
+
hasDefault: false;
|
|
248
|
+
isPrimaryKey: false;
|
|
249
|
+
isAutoincrement: false;
|
|
250
|
+
hasRuntimeDefault: false;
|
|
251
|
+
enumValues: undefined;
|
|
252
|
+
identity: undefined;
|
|
253
|
+
generated: undefined;
|
|
254
|
+
}>;
|
|
255
|
+
revokedAt: drizzle_orm_pg_core.PgBuildColumn<"passkeys", drizzle_orm_pg_core.PgTimestampBuilder, {
|
|
256
|
+
name: string;
|
|
257
|
+
tableName: "passkeys";
|
|
258
|
+
dataType: "object date";
|
|
259
|
+
data: Date;
|
|
260
|
+
driverParam: string;
|
|
261
|
+
notNull: false;
|
|
262
|
+
hasDefault: false;
|
|
263
|
+
isPrimaryKey: false;
|
|
264
|
+
isAutoincrement: false;
|
|
265
|
+
hasRuntimeDefault: false;
|
|
266
|
+
enumValues: undefined;
|
|
267
|
+
identity: undefined;
|
|
268
|
+
generated: undefined;
|
|
269
|
+
}>;
|
|
270
|
+
revokedReason: drizzle_orm_pg_core.PgBuildColumn<"passkeys", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
|
|
271
|
+
name: string;
|
|
272
|
+
tableName: "passkeys";
|
|
273
|
+
dataType: "string";
|
|
274
|
+
data: string;
|
|
275
|
+
driverParam: string;
|
|
276
|
+
notNull: false;
|
|
277
|
+
hasDefault: false;
|
|
278
|
+
isPrimaryKey: false;
|
|
279
|
+
isAutoincrement: false;
|
|
280
|
+
hasRuntimeDefault: false;
|
|
281
|
+
enumValues: undefined;
|
|
282
|
+
identity: undefined;
|
|
283
|
+
generated: undefined;
|
|
284
|
+
}>;
|
|
285
|
+
};
|
|
286
|
+
dialect: "pg";
|
|
287
|
+
}>;
|
|
288
|
+
type Passkey = typeof passkeys.$inferSelect;
|
|
289
|
+
type NewPasskey = typeof passkeys.$inferInsert;
|
|
92
290
|
|
|
93
291
|
/**
|
|
94
292
|
* @spfn/auth - What a sign-in answers with
|
|
@@ -218,31 +416,119 @@ interface ChangePasswordParams {
|
|
|
218
416
|
* adds no refusal for anybody who has not opted in.
|
|
219
417
|
*/
|
|
220
418
|
keyId: string;
|
|
221
|
-
currentPassword?: string;
|
|
222
|
-
newPassword: string;
|
|
223
|
-
passwordHash?: string;
|
|
419
|
+
currentPassword?: string;
|
|
420
|
+
newPassword: string;
|
|
421
|
+
passwordHash?: string;
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Register a new user account
|
|
425
|
+
*/
|
|
426
|
+
declare function registerService(params: RegisterParams): Promise<RegisterResult>;
|
|
427
|
+
/**
|
|
428
|
+
* Authenticate user and create session
|
|
429
|
+
*/
|
|
430
|
+
declare function loginService(params: LoginParams): Promise<LoginResult>;
|
|
431
|
+
/**
|
|
432
|
+
* Logout user (revoke current key)
|
|
433
|
+
*/
|
|
434
|
+
declare function logoutService(params: LogoutParams): Promise<void>;
|
|
435
|
+
/**
|
|
436
|
+
* Change user password
|
|
437
|
+
*
|
|
438
|
+
* An enrolled account steps up first (#95): a stolen session must not be able
|
|
439
|
+
* to take the account over by setting a new password. An unenrolled account is
|
|
440
|
+
* unaffected — including the OAuth-only account with no password and a key
|
|
441
|
+
* older than ten minutes, which still sets a first password and gets a 200.
|
|
442
|
+
*/
|
|
443
|
+
declare function changePasswordService(params: ChangePasswordParams): Promise<void>;
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* @spfn/auth - Session Renewal Service
|
|
447
|
+
*
|
|
448
|
+
* What a bound session does when its key runs out: prove, with a fresh WebAuthn
|
|
449
|
+
* assertion, that the person who enrolled the passkey is still at the machine,
|
|
450
|
+
* and get a new short-lived key sealed into the cookie.
|
|
451
|
+
*
|
|
452
|
+
* Neither step is public. The expiring key is named by `expiredKeyId`, and that
|
|
453
|
+
* value reaches the service from `authenticateForRenewal` — the `keyId` of a
|
|
454
|
+
* bearer JWT this very key signed — rather than from the request body, so a
|
|
455
|
+
* caller who does not hold the private half cannot name a key at all. The
|
|
456
|
+
* assertion still has to be signed by a passkey that key's owner enrolled: the
|
|
457
|
+
* signature proves the cookie, and the cookie is the thing that may have been
|
|
458
|
+
* copied.
|
|
459
|
+
*
|
|
460
|
+
* The admission below is run again here all the same. The middleware and the
|
|
461
|
+
* service ask the same four questions of the row, and a service that trusted its
|
|
462
|
+
* caller to have asked them would be one refactor away from not being asked at
|
|
463
|
+
* all.
|
|
464
|
+
*
|
|
465
|
+
* Every refusal is the same refusal. A key that never existed, a stranger's key,
|
|
466
|
+
* an unbound key, a revoked one, one past its grace, an inactive account, a spent
|
|
467
|
+
* challenge, an assertion that did not verify — all `SessionRenewalRefusedError`,
|
|
468
|
+
* with the same body, because anything finer would answer "is this key id live"
|
|
469
|
+
* to whoever asked.
|
|
470
|
+
*
|
|
471
|
+
* Renewal announces nothing. No `auth.login`, no `auth.device.registered`, and
|
|
472
|
+
* `lastLoginAt` does not move: this is the same person on the same device
|
|
473
|
+
* continuing the session they already had, and a subscriber mailing "new sign-in"
|
|
474
|
+
* once a day per device would train its reader to ignore the notice that matters.
|
|
475
|
+
* A `lastLoginAt` that moved every day would make dormant-account detection
|
|
476
|
+
* meaningless for exactly the accounts that turned this protection on.
|
|
477
|
+
*/
|
|
478
|
+
|
|
479
|
+
interface StartSessionRenewParams {
|
|
480
|
+
/** The key that ran out, read off the JWT the request was signed with. */
|
|
481
|
+
expiredKeyId: string;
|
|
482
|
+
}
|
|
483
|
+
interface FinishSessionRenewParams extends StartSessionRenewParams {
|
|
484
|
+
/** The assertion, from `navigator.credentials.get()`. */
|
|
485
|
+
response: AuthenticationResponseJSON;
|
|
486
|
+
/**
|
|
487
|
+
* The new key pair, in the vocabulary the Next.js login interceptor already
|
|
488
|
+
* writes: `renew/verify` is on that interceptor's path list, so these arrive
|
|
489
|
+
* exactly as they do on a login.
|
|
490
|
+
*/
|
|
491
|
+
keyId: string;
|
|
492
|
+
publicKey: string;
|
|
493
|
+
fingerprint: string;
|
|
494
|
+
algorithm?: KeyAlgorithmType;
|
|
224
495
|
}
|
|
225
496
|
/**
|
|
226
|
-
*
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
*
|
|
497
|
+
* What a completed renewal answers: a sign-in result, plus the new key's id.
|
|
498
|
+
*
|
|
499
|
+
* The id is the one thing a renewal has that a sign-in does not need to say —
|
|
500
|
+
* `renewSession()` promises it to the app, which has no other way to learn it
|
|
501
|
+
* (the key pair is minted in the proxy and the private half never leaves the
|
|
502
|
+
* cookie). It is not a contract operation, so nothing generated reads it.
|
|
231
503
|
*/
|
|
232
|
-
|
|
504
|
+
interface SessionRenewResult extends LoginResult {
|
|
505
|
+
/** The key this renewal registered, the one the session now signs with. */
|
|
506
|
+
keyId: string;
|
|
507
|
+
}
|
|
233
508
|
/**
|
|
234
|
-
*
|
|
509
|
+
* Step 1 — the challenge the authenticator signs.
|
|
510
|
+
*
|
|
511
|
+
* `allowCredentials` is empty and the account lives only on the challenge row.
|
|
512
|
+
* See `startRenewalCeremonyService`.
|
|
513
|
+
*
|
|
514
|
+
* @throws SessionRenewalRefusedError 갱신할 수 없는 키·계정일 때 (모든 사유 동일)
|
|
235
515
|
*/
|
|
236
|
-
declare function
|
|
516
|
+
declare function startSessionRenewService(params: StartSessionRenewParams): Promise<PublicKeyCredentialRequestOptionsJSON>;
|
|
237
517
|
/**
|
|
238
|
-
*
|
|
518
|
+
* Step 2 — verify the assertion, put a new bound key in place of the old one.
|
|
239
519
|
*
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
*
|
|
520
|
+
* The revocation runs first and its answer is the race winner: two verifies that
|
|
521
|
+
* both got past their own challenges meet at the same conditional UPDATE, and
|
|
522
|
+
* only the one that actually revoked the key goes on to register a replacement.
|
|
523
|
+
*
|
|
524
|
+
* The new key inherits the old row's provenance, so the device list keeps saying
|
|
525
|
+
* where this device first appeared rather than re-stamping itself every day. Its
|
|
526
|
+
* expiry is a fresh window from now — renewal is a renewal, not an extension of
|
|
527
|
+
* what the old key had.
|
|
528
|
+
*
|
|
529
|
+
* @throws SessionRenewalRefusedError 갱신할 수 없을 때 (증명 실패 포함, 모든 사유 동일)
|
|
244
530
|
*/
|
|
245
|
-
declare function
|
|
531
|
+
declare function finishSessionRenewService(params: FinishSessionRenewParams): Promise<SessionRenewResult>;
|
|
246
532
|
|
|
247
533
|
declare const EmailSchema: _sinclair_typebox.TString;
|
|
248
534
|
declare const PhoneSchema: _sinclair_typebox.TString;
|
|
@@ -289,6 +575,15 @@ declare const FingerprintSchema: _sinclair_typebox.TString;
|
|
|
289
575
|
* out the format — `USER_CODE_ALPHABET` is the only thing that can match a row.
|
|
290
576
|
*/
|
|
291
577
|
declare const UserCodeSchema: _sinclair_typebox.TString;
|
|
578
|
+
/**
|
|
579
|
+
* The issuer's handle on a device link, as `issue` returned it.
|
|
580
|
+
*
|
|
581
|
+
* A UUID today; bounded rather than patterned so the handle's format stays the
|
|
582
|
+
* server's to change. It authorizes nothing without the issuing key beside it.
|
|
583
|
+
*/
|
|
584
|
+
declare const LinkIdSchema: _sinclair_typebox.TString;
|
|
585
|
+
/** A number the issuer picked from the three `status` showed. */
|
|
586
|
+
declare const MatchChoiceSchema: _sinclair_typebox.TInteger;
|
|
292
587
|
/**
|
|
293
588
|
* What `POST /_auth/device/poll` answers with.
|
|
294
589
|
*
|
|
@@ -565,6 +860,41 @@ interface CompletePasswordResetParams {
|
|
|
565
860
|
*/
|
|
566
861
|
declare function completePasswordResetService(params: CompletePasswordResetParams): Promise<LoginResult>;
|
|
567
862
|
|
|
863
|
+
/**
|
|
864
|
+
* @spfn/auth - Device Registration Provenance
|
|
865
|
+
*
|
|
866
|
+
* What a registering request said about where it came from: the client address
|
|
867
|
+
* and the `user-agent` header, read once at the top of a route and carried into
|
|
868
|
+
* the key row so the account owner's device list can say "this one appeared
|
|
869
|
+
* from there".
|
|
870
|
+
*
|
|
871
|
+
* Both values are unauthenticated display material. `getClientIp` is documented
|
|
872
|
+
* as best-effort keying material and is spoofable on any request that is not
|
|
873
|
+
* proxy-verified, and a `user-agent` is whatever the caller typed — so nothing
|
|
874
|
+
* in this package decides anything by either one, and neither is ever compared.
|
|
875
|
+
*/
|
|
876
|
+
|
|
877
|
+
/** Where a device key registration came from, as the request stated it. */
|
|
878
|
+
interface DeviceProvenance {
|
|
879
|
+
ip?: string;
|
|
880
|
+
userAgent?: string;
|
|
881
|
+
/**
|
|
882
|
+
* Whether `proxy-guard` recognised this request as the trusted Next.js
|
|
883
|
+
* proxy's — the one fact here that a caller cannot state about itself.
|
|
884
|
+
*
|
|
885
|
+
* The two fields above are what the request claimed; this one is what the
|
|
886
|
+
* signature check concluded, so it is the only part of the provenance
|
|
887
|
+
* anything is allowed to decide by. Session binding decides by it: a key can
|
|
888
|
+
* be bound only on a request that reached the backend through the proxy that
|
|
889
|
+
* holds the session cookie, because nothing else can run the renewal.
|
|
890
|
+
*
|
|
891
|
+
* Optional so that a caller assembling provenance by hand — a test, a job
|
|
892
|
+
* replaying a request — can leave it out. Absent reads as "not the proxy",
|
|
893
|
+
* which is the conservative answer: the key is registered unbound.
|
|
894
|
+
*/
|
|
895
|
+
webProxy?: boolean;
|
|
896
|
+
}
|
|
897
|
+
|
|
568
898
|
/**
|
|
569
899
|
* @spfn/auth - Device Auth Service
|
|
570
900
|
*
|
|
@@ -591,6 +921,12 @@ declare function completePasswordResetService(params: CompletePasswordResetParam
|
|
|
591
921
|
* refuses the account's live records too, as `denied`, so they land in that row
|
|
592
922
|
* of the table. See `denyAllActiveByUserId`; the three callers are the three
|
|
593
923
|
* places that revoke every key at once.
|
|
924
|
+
*
|
|
925
|
+
* A poll may ask to wait (`waitMillis`). `waitForDeviceAuthAnswerService` then
|
|
926
|
+
* holds a pending record's request — before the route's transaction opens — until
|
|
927
|
+
* something answers it, the wait runs out, or the device hangs up, and only after
|
|
928
|
+
* that is the table above applied. A long poll answers exactly what a short poll
|
|
929
|
+
* sent at the same moment would, only sooner.
|
|
594
930
|
*/
|
|
595
931
|
|
|
596
932
|
interface StartDeviceAuthParams {
|
|
@@ -645,10 +981,21 @@ interface PollDeviceAuthParams {
|
|
|
645
981
|
* to run a WebAuthn ceremony in needs.
|
|
646
982
|
*/
|
|
647
983
|
webProxy?: boolean;
|
|
984
|
+
/**
|
|
985
|
+
* How long this request already waited on the server, from the long-poll
|
|
986
|
+
* middleware. A pending answer subtracts it from the interval: the device
|
|
987
|
+
* has done that much of its waiting here.
|
|
988
|
+
*/
|
|
989
|
+
waitedMillis?: number;
|
|
648
990
|
}
|
|
649
991
|
/** Nobody has answered yet. Not an error — the waiting device waits. */
|
|
650
992
|
interface DeviceAuthPendingResult {
|
|
651
993
|
status: 'pending';
|
|
994
|
+
/**
|
|
995
|
+
* How long to wait before polling again, less what the request already
|
|
996
|
+
* waited on the server — 0 once a long poll has waited at least the interval,
|
|
997
|
+
* so the device asks again at once.
|
|
998
|
+
*/
|
|
652
999
|
intervalMillis: number;
|
|
653
1000
|
}
|
|
654
1001
|
/**
|
|
@@ -716,41 +1063,47 @@ declare function denyDeviceAuthService(params: DenyDeviceAuthParams): Promise<vo
|
|
|
716
1063
|
declare function pollDeviceAuthService(params: PollDeviceAuthParams): Promise<PollDeviceAuthResult>;
|
|
717
1064
|
|
|
718
1065
|
/**
|
|
719
|
-
* @spfn/auth -
|
|
1066
|
+
* @spfn/auth - Device Links Entity
|
|
720
1067
|
*
|
|
721
|
-
*
|
|
722
|
-
*
|
|
723
|
-
*
|
|
724
|
-
*
|
|
1068
|
+
* Backs device link: the mirror image of device-code login. A device that is
|
|
1069
|
+
* already signed in (the issuer) asks for a short code and shows it; a new
|
|
1070
|
+
* device with no key on file reads it, parks its public key here, and shows a
|
|
1071
|
+
* two-digit match number; the issuer picks that number out of three, and the
|
|
1072
|
+
* new device's next poll registers the parked key under the issuer's account.
|
|
725
1073
|
*
|
|
726
|
-
*
|
|
727
|
-
*
|
|
728
|
-
*
|
|
729
|
-
*
|
|
1074
|
+
* A separate table from `device_authorizations` rather than a mode of it. The
|
|
1075
|
+
* two records are owned from opposite ends — a device authorization gains an
|
|
1076
|
+
* account only when someone approves it, a link has one from the moment it is
|
|
1077
|
+
* issued, and is bound to the issuing key as well — and folding them together
|
|
1078
|
+
* would leave every column meaning one thing in one flow and something else in
|
|
1079
|
+
* the other.
|
|
730
1080
|
*
|
|
731
|
-
*
|
|
732
|
-
*
|
|
733
|
-
*
|
|
1081
|
+
* The parked key material duplicates the `user_public_keys` columns for the
|
|
1082
|
+
* reason `device_authorizations` gives: a row there can sign requests, and
|
|
1083
|
+
* nothing here has been confirmed yet.
|
|
1084
|
+
*
|
|
1085
|
+
* Nothing sweeps this table. Rows are judged by `expiresAt` and by the issuing
|
|
1086
|
+
* key on read, so a stale row authorizes nothing.
|
|
734
1087
|
*/
|
|
735
1088
|
/**
|
|
736
|
-
*
|
|
1089
|
+
* Lifecycle of one device link.
|
|
737
1090
|
*
|
|
738
|
-
* `
|
|
739
|
-
* `
|
|
740
|
-
*
|
|
741
|
-
*
|
|
1091
|
+
* `issued -> redeemed -> approved -> consumed` is the only path to a registered
|
|
1092
|
+
* key. `denied` is a refusal the new device is told about — the issuer said no,
|
|
1093
|
+
* or picked the wrong number. `expired` is a link its issuer abandoned: a newer
|
|
1094
|
+
* one issued from the same key, a cancel, or a global revocation of the account.
|
|
1095
|
+
* A link whose TTL ran out, or whose issuing key was revoked, is judged expired
|
|
1096
|
+
* on read without the status having to say so.
|
|
742
1097
|
*/
|
|
743
|
-
declare const
|
|
744
|
-
type
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
declare const passkeys: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
748
|
-
name: "passkeys";
|
|
1098
|
+
declare const DEVICE_LINK_STATUSES: readonly ["issued", "redeemed", "approved", "denied", "consumed", "expired"];
|
|
1099
|
+
type DeviceLinkStatus = typeof DEVICE_LINK_STATUSES[number];
|
|
1100
|
+
declare const deviceLinks: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
1101
|
+
name: "device_links";
|
|
749
1102
|
schema: string;
|
|
750
1103
|
columns: {
|
|
751
|
-
createdAt: drizzle_orm_pg_core.PgBuildColumn<"
|
|
1104
|
+
createdAt: drizzle_orm_pg_core.PgBuildColumn<"device_links", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>, {
|
|
752
1105
|
name: string;
|
|
753
|
-
tableName: "
|
|
1106
|
+
tableName: "device_links";
|
|
754
1107
|
dataType: "object date";
|
|
755
1108
|
data: Date;
|
|
756
1109
|
driverParam: string;
|
|
@@ -763,9 +1116,9 @@ declare const passkeys: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
763
1116
|
identity: undefined;
|
|
764
1117
|
generated: undefined;
|
|
765
1118
|
}>;
|
|
766
|
-
updatedAt: drizzle_orm_pg_core.PgBuildColumn<"
|
|
1119
|
+
updatedAt: drizzle_orm_pg_core.PgBuildColumn<"device_links", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>>, {
|
|
767
1120
|
name: string;
|
|
768
|
-
tableName: "
|
|
1121
|
+
tableName: "device_links";
|
|
769
1122
|
dataType: "object date";
|
|
770
1123
|
data: Date;
|
|
771
1124
|
driverParam: string;
|
|
@@ -778,9 +1131,9 @@ declare const passkeys: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
778
1131
|
identity: undefined;
|
|
779
1132
|
generated: undefined;
|
|
780
1133
|
}>;
|
|
781
|
-
id: drizzle_orm_pg_core.PgBuildColumn<"
|
|
1134
|
+
id: drizzle_orm_pg_core.PgBuildColumn<"device_links", drizzle_orm_pg_core.SetIsPrimaryKey<drizzle_orm_pg_core.PgBigSerial53Builder>, {
|
|
782
1135
|
name: string;
|
|
783
|
-
tableName: "
|
|
1136
|
+
tableName: "device_links";
|
|
784
1137
|
dataType: "number int53";
|
|
785
1138
|
data: number;
|
|
786
1139
|
driverParam: number;
|
|
@@ -793,9 +1146,39 @@ declare const passkeys: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
793
1146
|
identity: undefined;
|
|
794
1147
|
generated: undefined;
|
|
795
1148
|
}>;
|
|
796
|
-
|
|
1149
|
+
linkId: drizzle_orm_pg_core.PgBuildColumn<"device_links", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
|
|
797
1150
|
name: string;
|
|
798
|
-
tableName: "
|
|
1151
|
+
tableName: "device_links";
|
|
1152
|
+
dataType: "string";
|
|
1153
|
+
data: string;
|
|
1154
|
+
driverParam: string;
|
|
1155
|
+
notNull: true;
|
|
1156
|
+
hasDefault: false;
|
|
1157
|
+
isPrimaryKey: false;
|
|
1158
|
+
isAutoincrement: false;
|
|
1159
|
+
hasRuntimeDefault: false;
|
|
1160
|
+
enumValues: undefined;
|
|
1161
|
+
identity: undefined;
|
|
1162
|
+
generated: undefined;
|
|
1163
|
+
}>;
|
|
1164
|
+
userCode: drizzle_orm_pg_core.PgBuildColumn<"device_links", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
|
|
1165
|
+
name: string;
|
|
1166
|
+
tableName: "device_links";
|
|
1167
|
+
dataType: "string";
|
|
1168
|
+
data: string;
|
|
1169
|
+
driverParam: string;
|
|
1170
|
+
notNull: true;
|
|
1171
|
+
hasDefault: false;
|
|
1172
|
+
isPrimaryKey: false;
|
|
1173
|
+
isAutoincrement: false;
|
|
1174
|
+
hasRuntimeDefault: false;
|
|
1175
|
+
enumValues: undefined;
|
|
1176
|
+
identity: undefined;
|
|
1177
|
+
generated: undefined;
|
|
1178
|
+
}>;
|
|
1179
|
+
issuerUserId: drizzle_orm_pg_core.PgBuildColumn<"device_links", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgBigInt53Builder>, {
|
|
1180
|
+
name: string;
|
|
1181
|
+
tableName: "device_links";
|
|
799
1182
|
dataType: "number int53";
|
|
800
1183
|
data: number;
|
|
801
1184
|
driverParam: string | number;
|
|
@@ -808,9 +1191,9 @@ declare const passkeys: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
808
1191
|
identity: undefined;
|
|
809
1192
|
generated: undefined;
|
|
810
1193
|
}>;
|
|
811
|
-
|
|
1194
|
+
issuerKeyId: drizzle_orm_pg_core.PgBuildColumn<"device_links", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
|
|
812
1195
|
name: string;
|
|
813
|
-
tableName: "
|
|
1196
|
+
tableName: "device_links";
|
|
814
1197
|
dataType: "string";
|
|
815
1198
|
data: string;
|
|
816
1199
|
driverParam: string;
|
|
@@ -823,13 +1206,13 @@ declare const passkeys: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
823
1206
|
identity: undefined;
|
|
824
1207
|
generated: undefined;
|
|
825
1208
|
}>;
|
|
826
|
-
|
|
1209
|
+
deviceCodeHash: drizzle_orm_pg_core.PgBuildColumn<"device_links", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
|
|
827
1210
|
name: string;
|
|
828
|
-
tableName: "
|
|
1211
|
+
tableName: "device_links";
|
|
829
1212
|
dataType: "string";
|
|
830
1213
|
data: string;
|
|
831
1214
|
driverParam: string;
|
|
832
|
-
notNull:
|
|
1215
|
+
notNull: false;
|
|
833
1216
|
hasDefault: false;
|
|
834
1217
|
isPrimaryKey: false;
|
|
835
1218
|
isAutoincrement: false;
|
|
@@ -838,14 +1221,14 @@ declare const passkeys: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
838
1221
|
identity: undefined;
|
|
839
1222
|
generated: undefined;
|
|
840
1223
|
}>;
|
|
841
|
-
|
|
1224
|
+
publicKey: drizzle_orm_pg_core.PgBuildColumn<"device_links", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
|
|
842
1225
|
name: string;
|
|
843
|
-
tableName: "
|
|
844
|
-
dataType: "
|
|
845
|
-
data:
|
|
846
|
-
driverParam: string
|
|
847
|
-
notNull:
|
|
848
|
-
hasDefault:
|
|
1226
|
+
tableName: "device_links";
|
|
1227
|
+
dataType: "string";
|
|
1228
|
+
data: string;
|
|
1229
|
+
driverParam: string;
|
|
1230
|
+
notNull: false;
|
|
1231
|
+
hasDefault: false;
|
|
849
1232
|
isPrimaryKey: false;
|
|
850
1233
|
isAutoincrement: false;
|
|
851
1234
|
hasRuntimeDefault: false;
|
|
@@ -853,12 +1236,12 @@ declare const passkeys: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
853
1236
|
identity: undefined;
|
|
854
1237
|
generated: undefined;
|
|
855
1238
|
}>;
|
|
856
|
-
|
|
1239
|
+
keyId: drizzle_orm_pg_core.PgBuildColumn<"device_links", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
|
|
857
1240
|
name: string;
|
|
858
|
-
tableName: "
|
|
1241
|
+
tableName: "device_links";
|
|
859
1242
|
dataType: "string";
|
|
860
|
-
data: string
|
|
861
|
-
driverParam: string
|
|
1243
|
+
data: string;
|
|
1244
|
+
driverParam: string;
|
|
862
1245
|
notNull: false;
|
|
863
1246
|
hasDefault: false;
|
|
864
1247
|
isPrimaryKey: false;
|
|
@@ -868,39 +1251,39 @@ declare const passkeys: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
868
1251
|
identity: undefined;
|
|
869
1252
|
generated: undefined;
|
|
870
1253
|
}>;
|
|
871
|
-
|
|
1254
|
+
fingerprint: drizzle_orm_pg_core.PgBuildColumn<"device_links", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
|
|
872
1255
|
name: string;
|
|
873
|
-
tableName: "
|
|
874
|
-
dataType: "string
|
|
875
|
-
data:
|
|
1256
|
+
tableName: "device_links";
|
|
1257
|
+
dataType: "string";
|
|
1258
|
+
data: string;
|
|
876
1259
|
driverParam: string;
|
|
877
|
-
notNull:
|
|
1260
|
+
notNull: false;
|
|
878
1261
|
hasDefault: false;
|
|
879
1262
|
isPrimaryKey: false;
|
|
880
1263
|
isAutoincrement: false;
|
|
881
1264
|
hasRuntimeDefault: false;
|
|
882
|
-
enumValues:
|
|
1265
|
+
enumValues: undefined;
|
|
883
1266
|
identity: undefined;
|
|
884
1267
|
generated: undefined;
|
|
885
1268
|
}>;
|
|
886
|
-
|
|
1269
|
+
algorithm: drizzle_orm_pg_core.PgBuildColumn<"device_links", drizzle_orm_pg_core.PgTextBuilder<["ES256", "RS256"] & [string, ...string[]]>, {
|
|
887
1270
|
name: string;
|
|
888
|
-
tableName: "
|
|
889
|
-
dataType: "
|
|
890
|
-
data:
|
|
891
|
-
driverParam:
|
|
892
|
-
notNull:
|
|
893
|
-
hasDefault:
|
|
1271
|
+
tableName: "device_links";
|
|
1272
|
+
dataType: "string enum";
|
|
1273
|
+
data: "ES256" | "RS256";
|
|
1274
|
+
driverParam: string;
|
|
1275
|
+
notNull: false;
|
|
1276
|
+
hasDefault: false;
|
|
894
1277
|
isPrimaryKey: false;
|
|
895
1278
|
isAutoincrement: false;
|
|
896
1279
|
hasRuntimeDefault: false;
|
|
897
|
-
enumValues:
|
|
1280
|
+
enumValues: ["ES256", "RS256"] & [string, ...string[]];
|
|
898
1281
|
identity: undefined;
|
|
899
1282
|
generated: undefined;
|
|
900
1283
|
}>;
|
|
901
|
-
|
|
1284
|
+
deviceName: drizzle_orm_pg_core.PgBuildColumn<"device_links", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
|
|
902
1285
|
name: string;
|
|
903
|
-
tableName: "
|
|
1286
|
+
tableName: "device_links";
|
|
904
1287
|
dataType: "string";
|
|
905
1288
|
data: string;
|
|
906
1289
|
driverParam: string;
|
|
@@ -913,39 +1296,84 @@ declare const passkeys: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
913
1296
|
identity: undefined;
|
|
914
1297
|
generated: undefined;
|
|
915
1298
|
}>;
|
|
916
|
-
|
|
1299
|
+
platform: drizzle_orm_pg_core.PgBuildColumn<"device_links", drizzle_orm_pg_core.PgTextBuilder<["ios", "android", "web", "desktop"] & [string, ...string[]]>, {
|
|
917
1300
|
name: string;
|
|
918
|
-
tableName: "
|
|
919
|
-
dataType: "string";
|
|
920
|
-
data:
|
|
1301
|
+
tableName: "device_links";
|
|
1302
|
+
dataType: "string enum";
|
|
1303
|
+
data: "ios" | "android" | "web" | "desktop";
|
|
921
1304
|
driverParam: string;
|
|
922
1305
|
notNull: false;
|
|
923
1306
|
hasDefault: false;
|
|
924
1307
|
isPrimaryKey: false;
|
|
925
1308
|
isAutoincrement: false;
|
|
926
1309
|
hasRuntimeDefault: false;
|
|
1310
|
+
enumValues: ["ios", "android", "web", "desktop"] & [string, ...string[]];
|
|
1311
|
+
identity: undefined;
|
|
1312
|
+
generated: undefined;
|
|
1313
|
+
}>;
|
|
1314
|
+
matchNumber: drizzle_orm_pg_core.PgBuildColumn<"device_links", drizzle_orm_pg_core.PgIntegerBuilder, {
|
|
1315
|
+
name: string;
|
|
1316
|
+
tableName: "device_links";
|
|
1317
|
+
dataType: "number int32";
|
|
1318
|
+
data: number;
|
|
1319
|
+
driverParam: string | number;
|
|
1320
|
+
notNull: false;
|
|
1321
|
+
hasDefault: false;
|
|
1322
|
+
isPrimaryKey: false;
|
|
1323
|
+
isAutoincrement: false;
|
|
1324
|
+
hasRuntimeDefault: false;
|
|
927
1325
|
enumValues: undefined;
|
|
928
1326
|
identity: undefined;
|
|
929
1327
|
generated: undefined;
|
|
930
1328
|
}>;
|
|
931
|
-
|
|
1329
|
+
choices: drizzle_orm_pg_core.PgBuildColumn<"device_links", drizzle_orm_pg_core.SetDimensions<drizzle_orm_pg_core.PgIntegerBuilder, 1>, {
|
|
932
1330
|
name: string;
|
|
933
|
-
tableName: "
|
|
934
|
-
dataType: "
|
|
935
|
-
data:
|
|
936
|
-
driverParam:
|
|
1331
|
+
tableName: "device_links";
|
|
1332
|
+
dataType: "number int32";
|
|
1333
|
+
data: number[];
|
|
1334
|
+
driverParam: string | (string | number)[];
|
|
1335
|
+
notNull: false;
|
|
1336
|
+
hasDefault: false;
|
|
1337
|
+
isPrimaryKey: false;
|
|
1338
|
+
isAutoincrement: false;
|
|
1339
|
+
hasRuntimeDefault: false;
|
|
1340
|
+
enumValues: undefined;
|
|
1341
|
+
identity: undefined;
|
|
1342
|
+
generated: undefined;
|
|
1343
|
+
}>;
|
|
1344
|
+
status: drizzle_orm_pg_core.PgBuildColumn<"device_links", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<["issued", "redeemed", "approved", "denied", "consumed", "expired"] & [string, ...string[]]>>>, {
|
|
1345
|
+
name: string;
|
|
1346
|
+
tableName: "device_links";
|
|
1347
|
+
dataType: "string enum";
|
|
1348
|
+
data: "expired" | "approved" | "denied" | "consumed" | "issued" | "redeemed";
|
|
1349
|
+
driverParam: string;
|
|
937
1350
|
notNull: true;
|
|
938
1351
|
hasDefault: true;
|
|
939
1352
|
isPrimaryKey: false;
|
|
940
1353
|
isAutoincrement: false;
|
|
941
1354
|
hasRuntimeDefault: false;
|
|
1355
|
+
enumValues: ["issued", "redeemed", "approved", "denied", "consumed", "expired"] & [string, ...string[]];
|
|
1356
|
+
identity: undefined;
|
|
1357
|
+
generated: undefined;
|
|
1358
|
+
}>;
|
|
1359
|
+
expiresAt: drizzle_orm_pg_core.PgBuildColumn<"device_links", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTimestampBuilder>, {
|
|
1360
|
+
name: string;
|
|
1361
|
+
tableName: "device_links";
|
|
1362
|
+
dataType: "object date";
|
|
1363
|
+
data: Date;
|
|
1364
|
+
driverParam: string;
|
|
1365
|
+
notNull: true;
|
|
1366
|
+
hasDefault: false;
|
|
1367
|
+
isPrimaryKey: false;
|
|
1368
|
+
isAutoincrement: false;
|
|
1369
|
+
hasRuntimeDefault: false;
|
|
942
1370
|
enumValues: undefined;
|
|
943
1371
|
identity: undefined;
|
|
944
1372
|
generated: undefined;
|
|
945
1373
|
}>;
|
|
946
|
-
|
|
1374
|
+
redeemedAt: drizzle_orm_pg_core.PgBuildColumn<"device_links", drizzle_orm_pg_core.PgTimestampBuilder, {
|
|
947
1375
|
name: string;
|
|
948
|
-
tableName: "
|
|
1376
|
+
tableName: "device_links";
|
|
949
1377
|
dataType: "object date";
|
|
950
1378
|
data: Date;
|
|
951
1379
|
driverParam: string;
|
|
@@ -958,9 +1386,9 @@ declare const passkeys: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
958
1386
|
identity: undefined;
|
|
959
1387
|
generated: undefined;
|
|
960
1388
|
}>;
|
|
961
|
-
|
|
1389
|
+
approvedAt: drizzle_orm_pg_core.PgBuildColumn<"device_links", drizzle_orm_pg_core.PgTimestampBuilder, {
|
|
962
1390
|
name: string;
|
|
963
|
-
tableName: "
|
|
1391
|
+
tableName: "device_links";
|
|
964
1392
|
dataType: "object date";
|
|
965
1393
|
data: Date;
|
|
966
1394
|
driverParam: string;
|
|
@@ -973,11 +1401,11 @@ declare const passkeys: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
973
1401
|
identity: undefined;
|
|
974
1402
|
generated: undefined;
|
|
975
1403
|
}>;
|
|
976
|
-
|
|
1404
|
+
consumedAt: drizzle_orm_pg_core.PgBuildColumn<"device_links", drizzle_orm_pg_core.PgTimestampBuilder, {
|
|
977
1405
|
name: string;
|
|
978
|
-
tableName: "
|
|
979
|
-
dataType: "
|
|
980
|
-
data:
|
|
1406
|
+
tableName: "device_links";
|
|
1407
|
+
dataType: "object date";
|
|
1408
|
+
data: Date;
|
|
981
1409
|
driverParam: string;
|
|
982
1410
|
notNull: false;
|
|
983
1411
|
hasDefault: false;
|
|
@@ -991,8 +1419,162 @@ declare const passkeys: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
991
1419
|
};
|
|
992
1420
|
dialect: "pg";
|
|
993
1421
|
}>;
|
|
994
|
-
type
|
|
995
|
-
type
|
|
1422
|
+
type DeviceLink = typeof deviceLinks.$inferSelect;
|
|
1423
|
+
type NewDeviceLink = typeof deviceLinks.$inferInsert;
|
|
1424
|
+
|
|
1425
|
+
/**
|
|
1426
|
+
* @spfn/auth - Device Link Service
|
|
1427
|
+
*
|
|
1428
|
+
* Device link: the mirror image of device-code login. A device that is already
|
|
1429
|
+
* signed in (the issuer) asks for a short code and shows it; a new device with no
|
|
1430
|
+
* key on file reads it, redeems it with its public key, and shows a two-digit
|
|
1431
|
+
* match number; the issuer is shown the device and three numbers, and picks the
|
|
1432
|
+
* one on the new device's screen. The new device's next poll registers its key
|
|
1433
|
+
* under the issuer's account and answers exactly what `loginService` answers —
|
|
1434
|
+
* the same completion device-code login uses, so the two are indistinguishable.
|
|
1435
|
+
*
|
|
1436
|
+
* | state ↓ op → | redeem | status | confirm | deny | cancel | poll |
|
|
1437
|
+
* | --- | --- | --- | --- | --- | --- | --- |
|
|
1438
|
+
* | issued | device parked, → redeemed | issued | NotRedeemed | NotRedeemed | → expired | — |
|
|
1439
|
+
* | redeemed | NotFound | device + choices | match: → approved; else → denied, WrongMatch | → denied | → expired | pending |
|
|
1440
|
+
* | approved | NotFound | approved | AlreadyHandled | AlreadyHandled | AlreadyHandled | key registered, → consumed |
|
|
1441
|
+
* | denied | NotFound | denied | AlreadyHandled | AlreadyHandled | AlreadyHandled | Denied |
|
|
1442
|
+
* | consumed | NotFound | consumed | AlreadyHandled | AlreadyHandled | AlreadyHandled | NotFound |
|
|
1443
|
+
* | dead | Expired | Expired | Expired | Expired | Expired | Expired |
|
|
1444
|
+
* | unknown | NotFound | NotFound | NotFound | NotFound | NotFound | NotFound |
|
|
1445
|
+
*
|
|
1446
|
+
* "Dead" is a link past its TTL, one its issuer cancelled or replaced or a global
|
|
1447
|
+
* revocation expired (status `expired`), and one whose issuing key has since been
|
|
1448
|
+
* revoked or run out — whatever state it was in. Expiry outranks state, with one
|
|
1449
|
+
* exception on the new device's side, for device-code login's reason: a spent
|
|
1450
|
+
* link answers redeem and poll as unknown even once its TTL has run out.
|
|
1451
|
+
*
|
|
1452
|
+
* Status, confirm, deny and cancel belong to the issuing key alone. A link
|
|
1453
|
+
* another key issued — another account's, or another device of the same
|
|
1454
|
+
* account's — answers NotFound to them before anything else is judged, so a
|
|
1455
|
+
* device that did not issue a link cannot learn it exists.
|
|
1456
|
+
*
|
|
1457
|
+
* Both parties can long-poll: the issuer's `status` while the link waits on the
|
|
1458
|
+
* new device (issued, or approved and not yet collected), the new device's `poll`
|
|
1459
|
+
* while it waits on the issuer (redeemed). The waits run before the routes'
|
|
1460
|
+
* transactions open, as device-code login's does, and every transition wakes
|
|
1461
|
+
* both after commit.
|
|
1462
|
+
*
|
|
1463
|
+
* Nothing here logs a user code, a device code, a match number or a key.
|
|
1464
|
+
*/
|
|
1465
|
+
|
|
1466
|
+
/** The signed-in device a link belongs to, read from the request's principal. Never from a body. */
|
|
1467
|
+
interface DeviceLinkIssuer {
|
|
1468
|
+
userId: number;
|
|
1469
|
+
/** The key that signed the request. */
|
|
1470
|
+
keyId: string;
|
|
1471
|
+
}
|
|
1472
|
+
interface IssueDeviceLinkResult {
|
|
1473
|
+
/** The issuer's handle on the link, for status, confirm, deny and cancel. */
|
|
1474
|
+
linkId: string;
|
|
1475
|
+
/** `XXXX-XXXX`, for the issuer's screen — as text and in the QR the client draws. */
|
|
1476
|
+
userCode: string;
|
|
1477
|
+
/** For the countdown on the issuer's screen. Display only; the server decides by its own clock. */
|
|
1478
|
+
expiresAtMillis: number;
|
|
1479
|
+
}
|
|
1480
|
+
interface RedeemDeviceLinkParams {
|
|
1481
|
+
userCode: string;
|
|
1482
|
+
publicKey: string;
|
|
1483
|
+
keyId: string;
|
|
1484
|
+
fingerprint: string;
|
|
1485
|
+
algorithm?: KeyAlgorithmType;
|
|
1486
|
+
/** Device label shown to the issuer. Display only — nothing is authorized by it. */
|
|
1487
|
+
deviceName?: string;
|
|
1488
|
+
platform?: KeyPlatformType;
|
|
1489
|
+
}
|
|
1490
|
+
interface RedeemDeviceLinkResult {
|
|
1491
|
+
/** Returned once. The new device polls with it; the server stores only its hash. */
|
|
1492
|
+
deviceCode: string;
|
|
1493
|
+
/** The number the new device shows, 10–99, for the issuer to pick out of three. */
|
|
1494
|
+
matchNumber: number;
|
|
1495
|
+
expiresAtMillis: number;
|
|
1496
|
+
/** Milliseconds the new device should wait between polls. */
|
|
1497
|
+
intervalMillis: number;
|
|
1498
|
+
}
|
|
1499
|
+
interface DeviceLinkParams {
|
|
1500
|
+
linkId: string;
|
|
1501
|
+
issuer: DeviceLinkIssuer;
|
|
1502
|
+
}
|
|
1503
|
+
interface ConfirmDeviceLinkParams extends DeviceLinkParams {
|
|
1504
|
+
/** The number the issuer picked. */
|
|
1505
|
+
choice: number;
|
|
1506
|
+
}
|
|
1507
|
+
/**
|
|
1508
|
+
* The issuer's view of its link, which every issuer operation answers with.
|
|
1509
|
+
*
|
|
1510
|
+
* The device fields appear once a device has redeemed the code; `choices` only
|
|
1511
|
+
* while the link waits on the issuer's pick (`redeemed`), since that is the only
|
|
1512
|
+
* moment a number means anything. `expired` is only ever cancel's answer: a
|
|
1513
|
+
* link that is dead when it is asked about answers `DeviceLinkExpiredError`.
|
|
1514
|
+
*/
|
|
1515
|
+
interface DeviceLinkStatusResult {
|
|
1516
|
+
status: DeviceLinkStatus;
|
|
1517
|
+
expiresAtMillis: number;
|
|
1518
|
+
deviceName?: string;
|
|
1519
|
+
platform?: KeyPlatformType;
|
|
1520
|
+
/** First bytes of the redeeming key's fingerprint, as the device list truncates it. */
|
|
1521
|
+
fingerprintPrefix?: string;
|
|
1522
|
+
redeemedAtMillis?: number;
|
|
1523
|
+
/** The match and two decoys, in the order drawn at redeem. */
|
|
1524
|
+
choices?: number[];
|
|
1525
|
+
}
|
|
1526
|
+
interface PollDeviceLinkParams extends DeviceProvenance {
|
|
1527
|
+
deviceCode: string;
|
|
1528
|
+
/** How long this request already waited on the server, from the long-poll middleware. */
|
|
1529
|
+
waitedMillis?: number;
|
|
1530
|
+
}
|
|
1531
|
+
/**
|
|
1532
|
+
* Issue a link from the signed-in device that asked, replacing any link that
|
|
1533
|
+
* device still has in play — one live link per issuing key, so a screen that was
|
|
1534
|
+
* closed without cancelling leaves nothing behind that a later redeem could use.
|
|
1535
|
+
*/
|
|
1536
|
+
declare function issueDeviceLinkService(issuer: DeviceLinkIssuer): Promise<IssueDeviceLinkResult>;
|
|
1537
|
+
/**
|
|
1538
|
+
* Park a new device's key on the link its code names, and hand back what that
|
|
1539
|
+
* device shows and polls with.
|
|
1540
|
+
*
|
|
1541
|
+
* Public by definition: the caller has no key yet. The code is the only thing it
|
|
1542
|
+
* holds, so every refusal that could tell a guesser the code was real — someone
|
|
1543
|
+
* redeemed it first, it was already used — is the same NotFound as a code never
|
|
1544
|
+
* issued. Only a code that died of age answers Expired, as the state table asks.
|
|
1545
|
+
*/
|
|
1546
|
+
declare function redeemDeviceLinkService(params: RedeemDeviceLinkParams): Promise<RedeemDeviceLinkResult>;
|
|
1547
|
+
/** The issuer asking where its link stands. */
|
|
1548
|
+
declare function getDeviceLinkStatusService(params: DeviceLinkParams): Promise<DeviceLinkStatusResult>;
|
|
1549
|
+
/**
|
|
1550
|
+
* The issuer picked a number.
|
|
1551
|
+
*
|
|
1552
|
+
* The right one approves the link; any other denies it on the spot, and the
|
|
1553
|
+
* refusal is committed before the error answers — there is no second pick. The
|
|
1554
|
+
* route is not wrapped in `Transactional()` for exactly that reason: a rollback
|
|
1555
|
+
* would undo the denial and hand the issuer another guess.
|
|
1556
|
+
*
|
|
1557
|
+
* The match number is fixed once a link is redeemed, so comparing against the
|
|
1558
|
+
* value read is safe; the transitions still name `redeemed`, so a deny or a
|
|
1559
|
+
* cancel landing in between wins or loses cleanly.
|
|
1560
|
+
*/
|
|
1561
|
+
declare function confirmDeviceLinkService(params: ConfirmDeviceLinkParams): Promise<DeviceLinkStatusResult>;
|
|
1562
|
+
/** The issuer refused the device, so it is told no instead of timing out. */
|
|
1563
|
+
declare function denyDeviceLinkService(params: DeviceLinkParams): Promise<DeviceLinkStatusResult>;
|
|
1564
|
+
/**
|
|
1565
|
+
* The issuer closed its screen before letting anyone in. The link is expired, so
|
|
1566
|
+
* a device holding its code — or already showing a match number — is told so.
|
|
1567
|
+
*/
|
|
1568
|
+
declare function cancelDeviceLinkService(params: DeviceLinkParams): Promise<DeviceLinkStatusResult>;
|
|
1569
|
+
/**
|
|
1570
|
+
* The new device asking whether the issuer has answered.
|
|
1571
|
+
*
|
|
1572
|
+
* Approved is the one branch with a side effect, and it is device-code login's
|
|
1573
|
+
* one-shot: the link is spent by a conditional update naming `approved` — and the
|
|
1574
|
+
* issuing key, so a link whose issuer signed out after confirming registers
|
|
1575
|
+
* nothing — and of two polls arriving together exactly one registers the key.
|
|
1576
|
+
*/
|
|
1577
|
+
declare function pollDeviceLinkService(params: PollDeviceLinkParams): Promise<PollDeviceAuthResult>;
|
|
996
1578
|
|
|
997
1579
|
/**
|
|
998
1580
|
* @spfn/auth - Passkey Service
|
|
@@ -1236,7 +1818,7 @@ declare const authLoginEvent: _spfn_core_event.EventDef<{
|
|
|
1236
1818
|
email?: string | undefined;
|
|
1237
1819
|
phone?: string | undefined;
|
|
1238
1820
|
userId: string;
|
|
1239
|
-
provider: "
|
|
1821
|
+
provider: "passkey" | "google" | "apple" | "github" | "kakao" | "naver" | "superself" | "email" | "phone" | "device";
|
|
1240
1822
|
mfaEnrolled: boolean;
|
|
1241
1823
|
}>;
|
|
1242
1824
|
/**
|
|
@@ -1248,15 +1830,15 @@ declare const authLoginEvent: _spfn_core_event.EventDef<{
|
|
|
1248
1830
|
* both arrive at `createVerifiedAccount`, so the two name themselves there;
|
|
1249
1831
|
* `'invitation'` is the one path that stores a key without the key service.
|
|
1250
1832
|
*/
|
|
1251
|
-
declare const DeviceRegistrationChannelSchema: _sinclair_typebox.TUnion<[_sinclair_typebox.TLiteral<"register">, _sinclair_typebox.TLiteral<"signup-link">, _sinclair_typebox.TLiteral<"invitation">, _sinclair_typebox.TLiteral<"password">, _sinclair_typebox.TLiteral<"oauth">, _sinclair_typebox.TLiteral<"oauth-native">, _sinclair_typebox.TLiteral<"device-code">, _sinclair_typebox.TLiteral<"password-reset">, _sinclair_typebox.TLiteral<"passkey">, _sinclair_typebox.TLiteral<"renewal">]>;
|
|
1252
|
-
/** The
|
|
1833
|
+
declare const DeviceRegistrationChannelSchema: _sinclair_typebox.TUnion<[_sinclair_typebox.TLiteral<"register">, _sinclair_typebox.TLiteral<"signup-link">, _sinclair_typebox.TLiteral<"invitation">, _sinclair_typebox.TLiteral<"password">, _sinclair_typebox.TLiteral<"oauth">, _sinclair_typebox.TLiteral<"oauth-native">, _sinclair_typebox.TLiteral<"device-code">, _sinclair_typebox.TLiteral<"device-link">, _sinclair_typebox.TLiteral<"password-reset">, _sinclair_typebox.TLiteral<"passkey">, _sinclair_typebox.TLiteral<"renewal">]>;
|
|
1834
|
+
/** The eleven doors a device key is registered through. */
|
|
1253
1835
|
type DeviceRegistrationChannel = Static<typeof DeviceRegistrationChannelSchema>;
|
|
1254
1836
|
/**
|
|
1255
1837
|
* auth.device.registered — a new device key was added to an account
|
|
1256
1838
|
*
|
|
1257
1839
|
* 발행 시점:
|
|
1258
1840
|
* - a key row was created for an account and the transaction that created it
|
|
1259
|
-
* committed, on every one of the
|
|
1841
|
+
* committed, on every one of the eleven channels above
|
|
1260
1842
|
*
|
|
1261
1843
|
* This is the notice an account owner needs and could not get before: a stolen
|
|
1262
1844
|
* password used to sign in on a new device was silent, because a login event
|
|
@@ -1288,13 +1870,13 @@ declare const authDeviceRegisteredEvent: _spfn_core_event.EventDef<{
|
|
|
1288
1870
|
platform?: string | undefined;
|
|
1289
1871
|
ip?: string | undefined;
|
|
1290
1872
|
userAgent?: string | undefined;
|
|
1873
|
+
userId: string;
|
|
1291
1874
|
keyId: string;
|
|
1292
1875
|
algorithm: string;
|
|
1293
|
-
userId: string;
|
|
1294
1876
|
mfaEnrolled: boolean;
|
|
1295
1877
|
fingerprintPrefix: string;
|
|
1296
1878
|
createdAtMillis: number;
|
|
1297
|
-
channel: "
|
|
1879
|
+
channel: "passkey" | "renewal" | "register" | "signup-link" | "invitation" | "password" | "oauth" | "oauth-native" | "device-code" | "device-link" | "password-reset";
|
|
1298
1880
|
}>;
|
|
1299
1881
|
/**
|
|
1300
1882
|
* auth.register - 회원가입 성공 이벤트
|
|
@@ -1317,7 +1899,7 @@ declare const authRegisterEvent: _spfn_core_event.EventDef<{
|
|
|
1317
1899
|
[x: string]: unknown;
|
|
1318
1900
|
} | undefined;
|
|
1319
1901
|
userId: string;
|
|
1320
|
-
provider: "
|
|
1902
|
+
provider: "google" | "apple" | "github" | "kakao" | "naver" | "superself" | "email" | "phone";
|
|
1321
1903
|
}>;
|
|
1322
1904
|
/**
|
|
1323
1905
|
* auth.invitation.created - 초대 생성 이벤트
|
|
@@ -1344,11 +1926,11 @@ declare const invitationCreatedEvent: _spfn_core_event.EventDef<{
|
|
|
1344
1926
|
[x: string]: unknown;
|
|
1345
1927
|
} | undefined;
|
|
1346
1928
|
email: string;
|
|
1347
|
-
token: string;
|
|
1348
|
-
expiresAt: string;
|
|
1349
1929
|
roleId: number;
|
|
1350
|
-
|
|
1930
|
+
expiresAt: string;
|
|
1351
1931
|
invitationId: string;
|
|
1932
|
+
token: string;
|
|
1933
|
+
invitedBy: string;
|
|
1352
1934
|
isResend: boolean;
|
|
1353
1935
|
}>;
|
|
1354
1936
|
/**
|
|
@@ -1368,11 +1950,11 @@ declare const invitationAcceptedEvent: _spfn_core_event.EventDef<{
|
|
|
1368
1950
|
metadata?: {
|
|
1369
1951
|
[x: string]: unknown;
|
|
1370
1952
|
} | undefined;
|
|
1371
|
-
email: string;
|
|
1372
1953
|
userId: string;
|
|
1954
|
+
email: string;
|
|
1373
1955
|
roleId: number;
|
|
1374
|
-
invitedBy: string;
|
|
1375
1956
|
invitationId: string;
|
|
1957
|
+
invitedBy: string;
|
|
1376
1958
|
}>;
|
|
1377
1959
|
/**
|
|
1378
1960
|
* auth.deletion.requested - 계정 탈퇴 요청 이벤트
|
|
@@ -1391,7 +1973,7 @@ declare const authDeletionRequestedEvent: _spfn_core_event.EventDef<{
|
|
|
1391
1973
|
userId: string;
|
|
1392
1974
|
purgeScheduledAt: string;
|
|
1393
1975
|
userPublicId: string;
|
|
1394
|
-
requestedBy: "
|
|
1976
|
+
requestedBy: "self" | "admin";
|
|
1395
1977
|
}>;
|
|
1396
1978
|
/**
|
|
1397
1979
|
* auth.deletion.cancelled - 계정 탈퇴 복구 이벤트
|
|
@@ -1436,7 +2018,7 @@ declare const authDeletionCompletedEvent: _spfn_core_event.EventDef<{
|
|
|
1436
2018
|
declare const oauthUnlinkedEvent: _spfn_core_event.EventDef<{
|
|
1437
2019
|
reason?: string | undefined;
|
|
1438
2020
|
userId: string;
|
|
1439
|
-
provider: "
|
|
2021
|
+
provider: "google" | "apple" | "github" | "kakao" | "naver" | "superself" | "email" | "phone";
|
|
1440
2022
|
providerUserId: string;
|
|
1441
2023
|
}>;
|
|
1442
2024
|
/**
|
|
@@ -1458,8 +2040,8 @@ declare const oauthUnlinkedEvent: _spfn_core_event.EventDef<{
|
|
|
1458
2040
|
* ```
|
|
1459
2041
|
*/
|
|
1460
2042
|
declare const authPasswordResetEvent: _spfn_core_event.EventDef<{
|
|
1461
|
-
email: string;
|
|
1462
2043
|
userId: string;
|
|
2044
|
+
email: string;
|
|
1463
2045
|
}>;
|
|
1464
2046
|
/**
|
|
1465
2047
|
* Auth event payload types
|
|
@@ -1607,7 +2189,7 @@ declare const mfaChallenges: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
1607
2189
|
name: string;
|
|
1608
2190
|
tableName: "mfa_challenges";
|
|
1609
2191
|
dataType: "string enum";
|
|
1610
|
-
data: "password" | "oauth
|
|
2192
|
+
data: "password" | "oauth" | "oauth-native" | "password-reset";
|
|
1611
2193
|
driverParam: string;
|
|
1612
2194
|
notNull: true;
|
|
1613
2195
|
hasDefault: false;
|
|
@@ -3094,94 +3676,6 @@ declare function disableSessionBindingService(params: DisableSessionBindingParam
|
|
|
3094
3676
|
/** The challenge the disabling ceremony signs. Same ceremony renewal uses. */
|
|
3095
3677
|
declare function startSessionBindingDisableService(userId: number): Promise<PublicKeyCredentialRequestOptionsJSON>;
|
|
3096
3678
|
|
|
3097
|
-
/**
|
|
3098
|
-
* @spfn/auth - Session Renewal Service
|
|
3099
|
-
*
|
|
3100
|
-
* What a bound session does when its key runs out: prove, with a fresh WebAuthn
|
|
3101
|
-
* assertion, that the person who enrolled the passkey is still at the machine,
|
|
3102
|
-
* and get a new short-lived key sealed into the cookie.
|
|
3103
|
-
*
|
|
3104
|
-
* Neither step is public. The expiring key is named by `expiredKeyId`, and that
|
|
3105
|
-
* value reaches the service from `authenticateForRenewal` — the `keyId` of a
|
|
3106
|
-
* bearer JWT this very key signed — rather than from the request body, so a
|
|
3107
|
-
* caller who does not hold the private half cannot name a key at all. The
|
|
3108
|
-
* assertion still has to be signed by a passkey that key's owner enrolled: the
|
|
3109
|
-
* signature proves the cookie, and the cookie is the thing that may have been
|
|
3110
|
-
* copied.
|
|
3111
|
-
*
|
|
3112
|
-
* The admission below is run again here all the same. The middleware and the
|
|
3113
|
-
* service ask the same four questions of the row, and a service that trusted its
|
|
3114
|
-
* caller to have asked them would be one refactor away from not being asked at
|
|
3115
|
-
* all.
|
|
3116
|
-
*
|
|
3117
|
-
* Every refusal is the same refusal. A key that never existed, a stranger's key,
|
|
3118
|
-
* an unbound key, a revoked one, one past its grace, an inactive account, a spent
|
|
3119
|
-
* challenge, an assertion that did not verify — all `SessionRenewalRefusedError`,
|
|
3120
|
-
* with the same body, because anything finer would answer "is this key id live"
|
|
3121
|
-
* to whoever asked.
|
|
3122
|
-
*
|
|
3123
|
-
* Renewal announces nothing. No `auth.login`, no `auth.device.registered`, and
|
|
3124
|
-
* `lastLoginAt` does not move: this is the same person on the same device
|
|
3125
|
-
* continuing the session they already had, and a subscriber mailing "new sign-in"
|
|
3126
|
-
* once a day per device would train its reader to ignore the notice that matters.
|
|
3127
|
-
* A `lastLoginAt` that moved every day would make dormant-account detection
|
|
3128
|
-
* meaningless for exactly the accounts that turned this protection on.
|
|
3129
|
-
*/
|
|
3130
|
-
|
|
3131
|
-
interface StartSessionRenewParams {
|
|
3132
|
-
/** The key that ran out, read off the JWT the request was signed with. */
|
|
3133
|
-
expiredKeyId: string;
|
|
3134
|
-
}
|
|
3135
|
-
interface FinishSessionRenewParams extends StartSessionRenewParams {
|
|
3136
|
-
/** The assertion, from `navigator.credentials.get()`. */
|
|
3137
|
-
response: AuthenticationResponseJSON;
|
|
3138
|
-
/**
|
|
3139
|
-
* The new key pair, in the vocabulary the Next.js login interceptor already
|
|
3140
|
-
* writes: `renew/verify` is on that interceptor's path list, so these arrive
|
|
3141
|
-
* exactly as they do on a login.
|
|
3142
|
-
*/
|
|
3143
|
-
keyId: string;
|
|
3144
|
-
publicKey: string;
|
|
3145
|
-
fingerprint: string;
|
|
3146
|
-
algorithm?: KeyAlgorithmType;
|
|
3147
|
-
}
|
|
3148
|
-
/**
|
|
3149
|
-
* What a completed renewal answers: a sign-in result, plus the new key's id.
|
|
3150
|
-
*
|
|
3151
|
-
* The id is the one thing a renewal has that a sign-in does not need to say —
|
|
3152
|
-
* `renewSession()` promises it to the app, which has no other way to learn it
|
|
3153
|
-
* (the key pair is minted in the proxy and the private half never leaves the
|
|
3154
|
-
* cookie). It is not a contract operation, so nothing generated reads it.
|
|
3155
|
-
*/
|
|
3156
|
-
interface SessionRenewResult extends LoginResult {
|
|
3157
|
-
/** The key this renewal registered, the one the session now signs with. */
|
|
3158
|
-
keyId: string;
|
|
3159
|
-
}
|
|
3160
|
-
/**
|
|
3161
|
-
* Step 1 — the challenge the authenticator signs.
|
|
3162
|
-
*
|
|
3163
|
-
* `allowCredentials` is empty and the account lives only on the challenge row.
|
|
3164
|
-
* See `startRenewalCeremonyService`.
|
|
3165
|
-
*
|
|
3166
|
-
* @throws SessionRenewalRefusedError 갱신할 수 없는 키·계정일 때 (모든 사유 동일)
|
|
3167
|
-
*/
|
|
3168
|
-
declare function startSessionRenewService(params: StartSessionRenewParams): Promise<PublicKeyCredentialRequestOptionsJSON>;
|
|
3169
|
-
/**
|
|
3170
|
-
* Step 2 — verify the assertion, put a new bound key in place of the old one.
|
|
3171
|
-
*
|
|
3172
|
-
* The revocation runs first and its answer is the race winner: two verifies that
|
|
3173
|
-
* both got past their own challenges meet at the same conditional UPDATE, and
|
|
3174
|
-
* only the one that actually revoked the key goes on to register a replacement.
|
|
3175
|
-
*
|
|
3176
|
-
* The new key inherits the old row's provenance, so the device list keeps saying
|
|
3177
|
-
* where this device first appeared rather than re-stamping itself every day. Its
|
|
3178
|
-
* expiry is a fresh window from now — renewal is a renewal, not an extension of
|
|
3179
|
-
* what the old key had.
|
|
3180
|
-
*
|
|
3181
|
-
* @throws SessionRenewalRefusedError 갱신할 수 없을 때 (증명 실패 포함, 모든 사유 동일)
|
|
3182
|
-
*/
|
|
3183
|
-
declare function finishSessionRenewService(params: FinishSessionRenewParams): Promise<SessionRenewResult>;
|
|
3184
|
-
|
|
3185
3679
|
/**
|
|
3186
3680
|
* What `POST /_auth/oauth/finalize` answers with, in both of its branches.
|
|
3187
3681
|
*
|
|
@@ -3203,6 +3697,87 @@ interface OAuthFinalizeResponse {
|
|
|
3203
3697
|
keyExpiresAtMillis?: number;
|
|
3204
3698
|
}
|
|
3205
3699
|
|
|
3700
|
+
/**
|
|
3701
|
+
* Role information for client/API responses
|
|
3702
|
+
*/
|
|
3703
|
+
interface Role {
|
|
3704
|
+
id: number;
|
|
3705
|
+
name: string;
|
|
3706
|
+
displayName: string;
|
|
3707
|
+
description: string | null;
|
|
3708
|
+
isBuiltin: boolean;
|
|
3709
|
+
isSystem: boolean;
|
|
3710
|
+
isActive: boolean;
|
|
3711
|
+
priority: number;
|
|
3712
|
+
createdAt: Date;
|
|
3713
|
+
updatedAt: Date;
|
|
3714
|
+
}
|
|
3715
|
+
/**
|
|
3716
|
+
* Permission information for client/API responses
|
|
3717
|
+
*/
|
|
3718
|
+
interface Permission {
|
|
3719
|
+
id: number;
|
|
3720
|
+
name: string;
|
|
3721
|
+
displayName: string;
|
|
3722
|
+
description: string | null;
|
|
3723
|
+
category: string | null;
|
|
3724
|
+
isBuiltin: boolean;
|
|
3725
|
+
isSystem: boolean;
|
|
3726
|
+
isActive: boolean;
|
|
3727
|
+
metadata: Record<string, any> | null;
|
|
3728
|
+
createdAt: Date;
|
|
3729
|
+
updatedAt: Date;
|
|
3730
|
+
}
|
|
3731
|
+
interface AuthSession {
|
|
3732
|
+
userId: number;
|
|
3733
|
+
publicId: string;
|
|
3734
|
+
email: string | null;
|
|
3735
|
+
emailVerified: boolean;
|
|
3736
|
+
phoneVerified: boolean;
|
|
3737
|
+
hasPassword: boolean;
|
|
3738
|
+
role: Role;
|
|
3739
|
+
permissions: Permission[];
|
|
3740
|
+
}
|
|
3741
|
+
interface ProfileInfo {
|
|
3742
|
+
profileId: number;
|
|
3743
|
+
displayName: string | null;
|
|
3744
|
+
firstName: string | null;
|
|
3745
|
+
lastName: string | null;
|
|
3746
|
+
avatarUrl: string | null;
|
|
3747
|
+
bio: string | null;
|
|
3748
|
+
locale: string;
|
|
3749
|
+
timezone: string;
|
|
3750
|
+
website: string | null;
|
|
3751
|
+
location: string | null;
|
|
3752
|
+
company: string | null;
|
|
3753
|
+
jobTitle: string | null;
|
|
3754
|
+
metadata: Record<string, any> | null;
|
|
3755
|
+
createdAt: Date;
|
|
3756
|
+
updatedAt: Date;
|
|
3757
|
+
}
|
|
3758
|
+
/**
|
|
3759
|
+
* User Profile Response
|
|
3760
|
+
*
|
|
3761
|
+
* Complete user data including:
|
|
3762
|
+
* - User fields at top level (userId, email, etc.)
|
|
3763
|
+
* - Profile data as nested field (optional)
|
|
3764
|
+
*
|
|
3765
|
+
* Excludes:
|
|
3766
|
+
* - Role and permissions (use auth session API)
|
|
3767
|
+
*/
|
|
3768
|
+
interface UserProfile {
|
|
3769
|
+
userId: number;
|
|
3770
|
+
publicId: string;
|
|
3771
|
+
email: string | null;
|
|
3772
|
+
username: string | null;
|
|
3773
|
+
emailVerified: boolean;
|
|
3774
|
+
phoneVerified: boolean;
|
|
3775
|
+
lastLoginAt: Date | null;
|
|
3776
|
+
createdAt: Date;
|
|
3777
|
+
updatedAt: Date;
|
|
3778
|
+
profile: ProfileInfo | null;
|
|
3779
|
+
}
|
|
3780
|
+
|
|
3206
3781
|
/**
|
|
3207
3782
|
* @spfn/auth - Main Router
|
|
3208
3783
|
*
|
|
@@ -3341,6 +3916,7 @@ declare const mainAuthRouter: _spfn_core_route.Router<{
|
|
|
3341
3916
|
pollDeviceAuth: _spfn_core_route.RouteDef<{
|
|
3342
3917
|
body: _sinclair_typebox.TObject<{
|
|
3343
3918
|
deviceCode: _sinclair_typebox.TString;
|
|
3919
|
+
waitMillis: _sinclair_typebox.TOptional<_sinclair_typebox.TInteger>;
|
|
3344
3920
|
}>;
|
|
3345
3921
|
}, {}, {
|
|
3346
3922
|
status: "pending";
|
|
@@ -3371,6 +3947,59 @@ declare const mainAuthRouter: _spfn_core_route.Router<{
|
|
|
3371
3947
|
userCode: _sinclair_typebox.TString;
|
|
3372
3948
|
}>;
|
|
3373
3949
|
}, {}, void>;
|
|
3950
|
+
issueDeviceLink: _spfn_core_route.RouteDef<{}, {}, IssueDeviceLinkResult>;
|
|
3951
|
+
redeemDeviceLink: _spfn_core_route.RouteDef<{
|
|
3952
|
+
body: _sinclair_typebox.TObject<{
|
|
3953
|
+
userCode: _sinclair_typebox.TString;
|
|
3954
|
+
publicKey: _sinclair_typebox.TString;
|
|
3955
|
+
keyId: _sinclair_typebox.TString;
|
|
3956
|
+
fingerprint: _sinclair_typebox.TString;
|
|
3957
|
+
algorithm: _sinclair_typebox.TOptional<_sinclair_typebox.TUnion<_sinclair_typebox.TLiteral<"ES256" | "RS256">[]>>;
|
|
3958
|
+
deviceName: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3959
|
+
platform: _sinclair_typebox.TOptional<_sinclair_typebox.TUnion<_sinclair_typebox.TLiteral<"ios" | "android" | "web" | "desktop">[]>>;
|
|
3960
|
+
}>;
|
|
3961
|
+
}, {}, RedeemDeviceLinkResult>;
|
|
3962
|
+
getDeviceLinkStatus: _spfn_core_route.RouteDef<{
|
|
3963
|
+
body: _sinclair_typebox.TObject<{
|
|
3964
|
+
linkId: _sinclair_typebox.TString;
|
|
3965
|
+
waitMillis: _sinclair_typebox.TOptional<_sinclair_typebox.TInteger>;
|
|
3966
|
+
}>;
|
|
3967
|
+
}, {}, DeviceLinkStatusResult>;
|
|
3968
|
+
confirmDeviceLink: _spfn_core_route.RouteDef<{
|
|
3969
|
+
body: _sinclair_typebox.TObject<{
|
|
3970
|
+
linkId: _sinclair_typebox.TString;
|
|
3971
|
+
choice: _sinclair_typebox.TInteger;
|
|
3972
|
+
}>;
|
|
3973
|
+
}, {}, DeviceLinkStatusResult>;
|
|
3974
|
+
denyDeviceLink: _spfn_core_route.RouteDef<{
|
|
3975
|
+
body: _sinclair_typebox.TObject<{
|
|
3976
|
+
linkId: _sinclair_typebox.TString;
|
|
3977
|
+
}>;
|
|
3978
|
+
}, {}, DeviceLinkStatusResult>;
|
|
3979
|
+
cancelDeviceLink: _spfn_core_route.RouteDef<{
|
|
3980
|
+
body: _sinclair_typebox.TObject<{
|
|
3981
|
+
linkId: _sinclair_typebox.TString;
|
|
3982
|
+
}>;
|
|
3983
|
+
}, {}, DeviceLinkStatusResult>;
|
|
3984
|
+
pollDeviceLink: _spfn_core_route.RouteDef<{
|
|
3985
|
+
body: _sinclair_typebox.TObject<{
|
|
3986
|
+
deviceCode: _sinclair_typebox.TString;
|
|
3987
|
+
waitMillis: _sinclair_typebox.TOptional<_sinclair_typebox.TInteger>;
|
|
3988
|
+
}>;
|
|
3989
|
+
}, {}, {
|
|
3990
|
+
status: "pending";
|
|
3991
|
+
intervalMillis: number;
|
|
3992
|
+
} | {
|
|
3993
|
+
email?: string | undefined;
|
|
3994
|
+
phone?: string | undefined;
|
|
3995
|
+
sessionBinding?: "none" | "passkey" | undefined;
|
|
3996
|
+
keyExpiresAtMillis?: number | undefined;
|
|
3997
|
+
status: "approved";
|
|
3998
|
+
mfaRequired: boolean;
|
|
3999
|
+
userId: string;
|
|
4000
|
+
publicId: string;
|
|
4001
|
+
passwordChangeRequired: boolean;
|
|
4002
|
+
}>;
|
|
3374
4003
|
passkeyRegisterOptions: _spfn_core_route.RouteDef<{
|
|
3375
4004
|
body: _sinclair_typebox.TObject<{
|
|
3376
4005
|
currentPassword: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
@@ -3531,7 +4160,7 @@ declare const mainAuthRouter: _spfn_core_route.Router<{
|
|
|
3531
4160
|
id: number;
|
|
3532
4161
|
name: string;
|
|
3533
4162
|
displayName: string;
|
|
3534
|
-
category: "
|
|
4163
|
+
category: "custom" | "user" | "auth" | "rbac" | "system" | undefined;
|
|
3535
4164
|
}[];
|
|
3536
4165
|
userId: number;
|
|
3537
4166
|
publicId: string;
|
|
@@ -3856,8 +4485,8 @@ declare const mainAuthRouter: _spfn_core_route.Router<{
|
|
|
3856
4485
|
}, {}, {
|
|
3857
4486
|
roles: {
|
|
3858
4487
|
description: string | null;
|
|
3859
|
-
name: string;
|
|
3860
4488
|
id: number;
|
|
4489
|
+
name: string;
|
|
3861
4490
|
displayName: string;
|
|
3862
4491
|
isBuiltin: boolean;
|
|
3863
4492
|
isSystem: boolean;
|
|
@@ -3878,8 +4507,8 @@ declare const mainAuthRouter: _spfn_core_route.Router<{
|
|
|
3878
4507
|
}, {}, {
|
|
3879
4508
|
role: {
|
|
3880
4509
|
description: string | null;
|
|
3881
|
-
name: string;
|
|
3882
4510
|
id: number;
|
|
4511
|
+
name: string;
|
|
3883
4512
|
displayName: string;
|
|
3884
4513
|
isBuiltin: boolean;
|
|
3885
4514
|
isSystem: boolean;
|
|
@@ -3902,8 +4531,8 @@ declare const mainAuthRouter: _spfn_core_route.Router<{
|
|
|
3902
4531
|
}, {}, {
|
|
3903
4532
|
role: {
|
|
3904
4533
|
description: string | null;
|
|
3905
|
-
name: string;
|
|
3906
4534
|
id: number;
|
|
4535
|
+
name: string;
|
|
3907
4536
|
displayName: string;
|
|
3908
4537
|
isBuiltin: boolean;
|
|
3909
4538
|
isSystem: boolean;
|
|
@@ -4754,4 +5383,4 @@ declare const machineAuth: _spfn_core_route.NamedMiddleware<"machineAuth">;
|
|
|
4754
5383
|
*/
|
|
4755
5384
|
declare const requireMachineScope: _spfn_core_route.NamedMiddlewareFactory<"machineScope", string[]>;
|
|
4756
5385
|
|
|
4757
|
-
export { type
|
|
5386
|
+
export { type UserPublicKey as $, type AuthInitOptions as A, type VerificationPurpose as B, type ConfirmSignupLinkResult as C, type DeviceAuthInfoResult as D, type VerificationTargetType as E, type FinishPasskeyEnrollmentResult as F, type OAuthProvider as G, type NewPasskey as H, type IssueDeviceLinkResult as I, type Passkey as J, type KeySummary as K, type LoginResult as L, type MfaStatus as M, type NewUserPublicKey as N, type OAuthStartResult as O, type PermissionConfig as P, type MfaVerification as Q, type RoleConfig as R, type SendVerificationCodeResult as S, type TotpEnrolmentResult as T, type UserProfile as U, type VerifyMfaChallengeResult as V, type MfaVerificationMethod as W, type NewMfaChallenge as X, type MfaChallenge as Y, type DeviceLink as Z, type NewDeviceLink as _, type RegisterResult as a, type OAuthCodeExchangeOptions as a$, type AuthContext as a0, type ApproveDeviceAuthParams as a1, type AssertStepUpParams as a2, type AuthDeletionCancelledPayload as a3, type AuthDeletionCompletedPayload as a4, type AuthDeletionRequestedPayload as a5, type AuthDeviceRegisteredPayload as a6, type AuthLoginPayload as a7, type AuthPasswordResetPayload as a8, type AuthProfileOutcome as a9, FingerprintSchema as aA, type FinishPasskeyEnrollmentParams as aB, type FinishPasskeyLoginParams as aC, type FinishSessionRenewParams as aD, type InvitationAcceptedPayload as aE, type InvitationCreatedPayload as aF, KEY_FINGERPRINT_PREFIX_LENGTH as aG, KeyIdSchema as aH, LinkIdSchema as aI, type LoginParams as aJ, type LogoutParams as aK, MFA_CHALLENGE_ATTEMPT_LIMIT as aL, MFA_CHALLENGE_CHANNELS as aM, MFA_VERIFICATION_METHODS as aN, type MachinePrincipal as aO, type MachineVerifierRegistration as aP, type MarkPasskeyParams as aQ, MatchChoiceSchema as aR, type MfaChallengeChannel as aS, type MfaChallengeHandle as aT, type NativeVerifyOptions as aU, type NewMfaVerification as aV, type NormalizedIdentity as aW, type OAuth2AuthorizeParams as aX, type OAuth2ScopeDescription as aY, type OAuthCallbackParams as aZ, type OAuthCallbackResult as a_, type AuthProfileVerifier as aa, AuthProviderSchema as ab, type AuthRegisterPayload as ac, type BearerOutcome as ad, type BearerRefusal as ae, type ChangePasswordParams as af, type CompletePasswordResetParams as ag, type CompleteSignupParams as ah, type ConfirmDeviceLinkParams as ai, type ConfirmPasswordResetParams as aj, type ConfirmSignupLinkParams as ak, type ConfirmTotpParams as al, DEVICE_LINK_STATUSES as am, type DeferredLoginEvent as an, type DenyDeviceAuthParams as ao, type DeviceAuthApprovedResult as ap, type DeviceAuthInfoParams as aq, type DeviceAuthPendingResult as ar, DeviceAuthPollResponseSchema as as, type DeviceLinkIssuer as at, type DeviceLinkParams as au, type DeviceLinkStatus as av, DeviceNameSchema as aw, type DeviceRegistrationChannel as ax, type DisableSessionBindingParams as ay, EmailSchema as az, type StartDeviceAuthResult as b, completePasswordResetService as b$, type OAuthNativeParams as b0, type OAuthStartParams as b1, type OAuthTokens as b2, type OAuthUnlinkedPayload as b3, type OpenStepUpChallengeParams as b4, PASSKEY_DEVICE_TYPES as b5, PASSKEY_LABEL_MAX_LENGTH as b6, type PasskeyDeviceType as b7, PasswordSchema as b8, PhoneSchema as b9, type UnlinkNotifyRequest as bA, type UnlinkNotifyResult as bB, UserCodeSchema as bC, VerificationPurposeSchema as bD, type VerifyCodeParams as bE, type VerifyCodeResult as bF, type VerifyMfaChallengeParams as bG, admitBearerKey as bH, approveDeviceAuthService as bI, approveOAuth2AuthorizeService as bJ, assertNotLastRecoveryCredential as bK, assertRecentAuthentication as bL, assertStepUp as bM, attemptSecondFactor as bN, authDeletionCancelledEvent as bO, authDeletionCompletedEvent as bP, authDeletionRequestedEvent as bQ, authDeviceRegisteredEvent as bR, authLoginEvent as bS, authPasswordResetEvent as bT, authRegisterEvent as bU, authenticate as bV, bearerAuthContext as bW, buildOAuthErrorUrl as bX, cancelDeviceLinkService as bY, carryStepUpVerification as bZ, changePasswordService as b_, PlatformSchema as ba, type PollDeviceAuthParams as bb, type PollDeviceAuthResult as bc, type PollDeviceLinkParams as bd, PublicKeySchema as be, type RecentAuthenticationParams as bf, type RedeemDeviceLinkParams as bg, type RegisterParams as bh, type RegisterPublicKeyParams as bi, type RegisterPublicKeyResult as bj, type RenamePasskeyParams as bk, type RequestPasswordResetParams as bl, type RequestSignupLinkParams as bm, type RevokeAllKeysParams as bn, type RevokeKeyParams as bo, type RevokePasskeyParams as bp, type RotateKeyParams as bq, type SendVerificationCodeParams as br, type SessionBindingParams as bs, type StartDeviceAuthParams as bt, type StartPasskeyEnrollmentParams as bu, type StartSessionRenewParams as bv, type StepUpParams as bw, TargetTypeSchema as bx, type UnlinkNotification as by, UnlinkNotifyRejection as bz, type RedeemDeviceLinkResult as c, requireMachineScope as c$, completeSignupService as c0, confirmDeviceLinkService as c1, confirmPasswordResetService as c2, confirmSignupLinkService as c3, confirmTotpEnrolmentService as c4, denyDeviceAuthService as c5, denyDeviceLinkService as c6, denyOAuth2AuthorizeService as c7, describeOAuth2AuthorizeRequestService as c8, deviceLinks as c9, markPasskeySecondFactorService as cA, mfaChallenges as cB, mfaEnrolledForUser as cC, mfaStatusService as cD, mfaVerifications as cE, oauthCallbackService as cF, oauthNativeService as cG, oauthStartService as cH, oauthUnlinkNotifyService as cI, oauthUnlinkedEvent as cJ, openStepUpChallengeService as cK, optionalAuth as cL, passkeys as cM, pollDeviceAuthService as cN, pollDeviceLinkService as cO, redeemDeviceLinkService as cP, regenerateRecoveryCodesService as cQ, registerAuthProfile as cR, registerMachineVerifier as cS, registerOAuthProvider as cT, registerPublicKeyService as cU, registerService as cV, registeredBinding as cW, renamePasskeyService as cX, requestPasswordResetService as cY, requestSignupLinkService as cZ, requireEnabledProvider as c_, disableMfaService as ca, disableSessionBindingService as cb, enableSessionBindingService as cc, finishPasskeyEnrollmentService as cd, finishPasskeyLoginService as ce, finishSessionRenewService as cf, getDeviceAuthInfoService as cg, getDeviceLinkStatusService as ch, getEnabledOAuthProviders as ci, getGoogleAccessToken as cj, getMachinePrincipal as ck, getOAuthProvider as cl, getRegisteredProviders as cm, getSessionBindingService as cn, invitationAcceptedEvent as co, invitationCreatedEvent as cp, isOAuthProviderEnabled as cq, issueDeviceLinkService as cr, issueOneTimeTokenService as cs, keySessionBindingService as ct, listKeysService as cu, listOAuth2GrantsService as cv, listPasskeysService as cw, loginService as cx, logoutService as cy, machineAuth as cz, type DeviceLinkStatusResult as d, resolveAuthenticatedUser as d0, resumeStepUpChallengeService as d1, revokeAllKeysService as d2, revokeAllOAuth2GrantsForUser as d3, revokeKeyService as d4, revokeOAuth2GrantService as d5, revokePasskeyService as d6, rotateKeyService as d7, runAuthProfile as d8, selectAuthProfile as d9, sendVerificationCodeService as da, startDeviceAuthService as db, startMfaChallengeAssertionService as dc, startPasskeyEnrollmentService as dd, startPasskeyLoginService as de, startSessionBindingDisableService as df, startSessionRenewService as dg, startStepUpService as dh, startTotpEnrolmentService as di, stepUpService as dj, sweepMfaChallengesService as dk, sweepUnconfirmedMfaService as dl, userPublicKeys as dm, verifyCodeService as dn, verifyMfaChallengeService as dp, verifyOneTimeTokenService as dq, verifySecondFactor as dr, type RotateKeyResult as e, type RevokeAllKeysResult as f, type IssueOneTimeTokenResult as g, type ProfileInfo as h, type OAuthFinalizeResponse as i, type OAuthNativeResult as j, type RequestSignupLinkResult as k, type RequestPasswordResetResult as l, mainAuthRouter as m, type ConfirmPasswordResetResult as n, type PasskeySummary as o, type ConfirmTotpResult as p, type SessionBindingResult as q, type SessionRenewResult as r, type OAuth2ConsentView as s, type OAuth2AuthorizationCodeIssued as t, type OAuth2GrantSummary as u, type AuthSession as v, PERMISSION_CATEGORIES as w, type PermissionCategory as x, VERIFICATION_PURPOSES as y, VERIFICATION_TARGET_TYPES as z };
|