@spfn/auth 0.3.0-beta.27 → 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 +146 -2
- package/dist/client-proof.d.ts +8 -1
- package/dist/client-proof.js +56 -1
- 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 +389 -336
- package/dist/index.js +74 -0
- package/dist/index.js.map +1 -1
- package/dist/{machine-principals-BvRw8b8t.d.ts → machine-principals-B0bjs-0K.d.ts} +896 -285
- package/dist/server.d.ts +396 -174
- package/dist/server.js +1557 -679
- 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
|
-
*
|
|
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.
|
|
14
28
|
*/
|
|
15
|
-
interface Role {
|
|
16
|
-
id: number;
|
|
17
|
-
name: string;
|
|
18
|
-
displayName: string;
|
|
19
|
-
description: string | null;
|
|
20
|
-
isBuiltin: boolean;
|
|
21
|
-
isSystem: boolean;
|
|
22
|
-
isActive: boolean;
|
|
23
|
-
priority: number;
|
|
24
|
-
createdAt: Date;
|
|
25
|
-
updatedAt: Date;
|
|
26
|
-
}
|
|
27
29
|
/**
|
|
28
|
-
*
|
|
29
|
-
*/
|
|
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
|
-
/**
|
|
71
|
-
* User Profile Response
|
|
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
|
*
|
|
@@ -733,41 +1063,47 @@ declare function denyDeviceAuthService(params: DenyDeviceAuthParams): Promise<vo
|
|
|
733
1063
|
declare function pollDeviceAuthService(params: PollDeviceAuthParams): Promise<PollDeviceAuthResult>;
|
|
734
1064
|
|
|
735
1065
|
/**
|
|
736
|
-
* @spfn/auth -
|
|
1066
|
+
* @spfn/auth - Device Links Entity
|
|
737
1067
|
*
|
|
738
|
-
*
|
|
739
|
-
*
|
|
740
|
-
*
|
|
741
|
-
*
|
|
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.
|
|
742
1073
|
*
|
|
743
|
-
*
|
|
744
|
-
*
|
|
745
|
-
*
|
|
746
|
-
*
|
|
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.
|
|
747
1080
|
*
|
|
748
|
-
*
|
|
749
|
-
*
|
|
750
|
-
*
|
|
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.
|
|
751
1087
|
*/
|
|
752
1088
|
/**
|
|
753
|
-
*
|
|
1089
|
+
* Lifecycle of one device link.
|
|
754
1090
|
*
|
|
755
|
-
* `
|
|
756
|
-
* `
|
|
757
|
-
*
|
|
758
|
-
*
|
|
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.
|
|
759
1097
|
*/
|
|
760
|
-
declare const
|
|
761
|
-
type
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
declare const passkeys: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
765
|
-
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";
|
|
766
1102
|
schema: string;
|
|
767
1103
|
columns: {
|
|
768
|
-
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>>, {
|
|
769
1105
|
name: string;
|
|
770
|
-
tableName: "
|
|
1106
|
+
tableName: "device_links";
|
|
771
1107
|
dataType: "object date";
|
|
772
1108
|
data: Date;
|
|
773
1109
|
driverParam: string;
|
|
@@ -780,9 +1116,9 @@ declare const passkeys: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
780
1116
|
identity: undefined;
|
|
781
1117
|
generated: undefined;
|
|
782
1118
|
}>;
|
|
783
|
-
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>>>, {
|
|
784
1120
|
name: string;
|
|
785
|
-
tableName: "
|
|
1121
|
+
tableName: "device_links";
|
|
786
1122
|
dataType: "object date";
|
|
787
1123
|
data: Date;
|
|
788
1124
|
driverParam: string;
|
|
@@ -795,9 +1131,9 @@ declare const passkeys: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
795
1131
|
identity: undefined;
|
|
796
1132
|
generated: undefined;
|
|
797
1133
|
}>;
|
|
798
|
-
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>, {
|
|
799
1135
|
name: string;
|
|
800
|
-
tableName: "
|
|
1136
|
+
tableName: "device_links";
|
|
801
1137
|
dataType: "number int53";
|
|
802
1138
|
data: number;
|
|
803
1139
|
driverParam: number;
|
|
@@ -810,9 +1146,39 @@ declare const passkeys: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
810
1146
|
identity: undefined;
|
|
811
1147
|
generated: undefined;
|
|
812
1148
|
}>;
|
|
813
|
-
|
|
1149
|
+
linkId: drizzle_orm_pg_core.PgBuildColumn<"device_links", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
|
|
814
1150
|
name: string;
|
|
815
|
-
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";
|
|
816
1182
|
dataType: "number int53";
|
|
817
1183
|
data: number;
|
|
818
1184
|
driverParam: string | number;
|
|
@@ -825,9 +1191,9 @@ declare const passkeys: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
825
1191
|
identity: undefined;
|
|
826
1192
|
generated: undefined;
|
|
827
1193
|
}>;
|
|
828
|
-
|
|
1194
|
+
issuerKeyId: drizzle_orm_pg_core.PgBuildColumn<"device_links", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
|
|
829
1195
|
name: string;
|
|
830
|
-
tableName: "
|
|
1196
|
+
tableName: "device_links";
|
|
831
1197
|
dataType: "string";
|
|
832
1198
|
data: string;
|
|
833
1199
|
driverParam: string;
|
|
@@ -840,13 +1206,13 @@ declare const passkeys: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
840
1206
|
identity: undefined;
|
|
841
1207
|
generated: undefined;
|
|
842
1208
|
}>;
|
|
843
|
-
|
|
1209
|
+
deviceCodeHash: drizzle_orm_pg_core.PgBuildColumn<"device_links", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
|
|
844
1210
|
name: string;
|
|
845
|
-
tableName: "
|
|
1211
|
+
tableName: "device_links";
|
|
846
1212
|
dataType: "string";
|
|
847
1213
|
data: string;
|
|
848
1214
|
driverParam: string;
|
|
849
|
-
notNull:
|
|
1215
|
+
notNull: false;
|
|
850
1216
|
hasDefault: false;
|
|
851
1217
|
isPrimaryKey: false;
|
|
852
1218
|
isAutoincrement: false;
|
|
@@ -855,14 +1221,14 @@ declare const passkeys: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
855
1221
|
identity: undefined;
|
|
856
1222
|
generated: undefined;
|
|
857
1223
|
}>;
|
|
858
|
-
|
|
1224
|
+
publicKey: drizzle_orm_pg_core.PgBuildColumn<"device_links", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
|
|
859
1225
|
name: string;
|
|
860
|
-
tableName: "
|
|
861
|
-
dataType: "
|
|
862
|
-
data:
|
|
863
|
-
driverParam: string
|
|
864
|
-
notNull:
|
|
865
|
-
hasDefault:
|
|
1226
|
+
tableName: "device_links";
|
|
1227
|
+
dataType: "string";
|
|
1228
|
+
data: string;
|
|
1229
|
+
driverParam: string;
|
|
1230
|
+
notNull: false;
|
|
1231
|
+
hasDefault: false;
|
|
866
1232
|
isPrimaryKey: false;
|
|
867
1233
|
isAutoincrement: false;
|
|
868
1234
|
hasRuntimeDefault: false;
|
|
@@ -870,12 +1236,12 @@ declare const passkeys: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
870
1236
|
identity: undefined;
|
|
871
1237
|
generated: undefined;
|
|
872
1238
|
}>;
|
|
873
|
-
|
|
1239
|
+
keyId: drizzle_orm_pg_core.PgBuildColumn<"device_links", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
|
|
874
1240
|
name: string;
|
|
875
|
-
tableName: "
|
|
1241
|
+
tableName: "device_links";
|
|
876
1242
|
dataType: "string";
|
|
877
|
-
data: string
|
|
878
|
-
driverParam: string
|
|
1243
|
+
data: string;
|
|
1244
|
+
driverParam: string;
|
|
879
1245
|
notNull: false;
|
|
880
1246
|
hasDefault: false;
|
|
881
1247
|
isPrimaryKey: false;
|
|
@@ -885,39 +1251,39 @@ declare const passkeys: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
885
1251
|
identity: undefined;
|
|
886
1252
|
generated: undefined;
|
|
887
1253
|
}>;
|
|
888
|
-
|
|
1254
|
+
fingerprint: drizzle_orm_pg_core.PgBuildColumn<"device_links", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
|
|
889
1255
|
name: string;
|
|
890
|
-
tableName: "
|
|
891
|
-
dataType: "string
|
|
892
|
-
data:
|
|
1256
|
+
tableName: "device_links";
|
|
1257
|
+
dataType: "string";
|
|
1258
|
+
data: string;
|
|
893
1259
|
driverParam: string;
|
|
894
|
-
notNull:
|
|
1260
|
+
notNull: false;
|
|
895
1261
|
hasDefault: false;
|
|
896
1262
|
isPrimaryKey: false;
|
|
897
1263
|
isAutoincrement: false;
|
|
898
1264
|
hasRuntimeDefault: false;
|
|
899
|
-
enumValues:
|
|
1265
|
+
enumValues: undefined;
|
|
900
1266
|
identity: undefined;
|
|
901
1267
|
generated: undefined;
|
|
902
1268
|
}>;
|
|
903
|
-
|
|
1269
|
+
algorithm: drizzle_orm_pg_core.PgBuildColumn<"device_links", drizzle_orm_pg_core.PgTextBuilder<["ES256", "RS256"] & [string, ...string[]]>, {
|
|
904
1270
|
name: string;
|
|
905
|
-
tableName: "
|
|
906
|
-
dataType: "
|
|
907
|
-
data:
|
|
908
|
-
driverParam:
|
|
909
|
-
notNull:
|
|
910
|
-
hasDefault:
|
|
1271
|
+
tableName: "device_links";
|
|
1272
|
+
dataType: "string enum";
|
|
1273
|
+
data: "ES256" | "RS256";
|
|
1274
|
+
driverParam: string;
|
|
1275
|
+
notNull: false;
|
|
1276
|
+
hasDefault: false;
|
|
911
1277
|
isPrimaryKey: false;
|
|
912
1278
|
isAutoincrement: false;
|
|
913
1279
|
hasRuntimeDefault: false;
|
|
914
|
-
enumValues:
|
|
1280
|
+
enumValues: ["ES256", "RS256"] & [string, ...string[]];
|
|
915
1281
|
identity: undefined;
|
|
916
1282
|
generated: undefined;
|
|
917
1283
|
}>;
|
|
918
|
-
|
|
1284
|
+
deviceName: drizzle_orm_pg_core.PgBuildColumn<"device_links", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
|
|
919
1285
|
name: string;
|
|
920
|
-
tableName: "
|
|
1286
|
+
tableName: "device_links";
|
|
921
1287
|
dataType: "string";
|
|
922
1288
|
data: string;
|
|
923
1289
|
driverParam: string;
|
|
@@ -930,39 +1296,84 @@ declare const passkeys: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
930
1296
|
identity: undefined;
|
|
931
1297
|
generated: undefined;
|
|
932
1298
|
}>;
|
|
933
|
-
|
|
1299
|
+
platform: drizzle_orm_pg_core.PgBuildColumn<"device_links", drizzle_orm_pg_core.PgTextBuilder<["ios", "android", "web", "desktop"] & [string, ...string[]]>, {
|
|
934
1300
|
name: string;
|
|
935
|
-
tableName: "
|
|
936
|
-
dataType: "string";
|
|
937
|
-
data:
|
|
1301
|
+
tableName: "device_links";
|
|
1302
|
+
dataType: "string enum";
|
|
1303
|
+
data: "ios" | "android" | "web" | "desktop";
|
|
938
1304
|
driverParam: string;
|
|
939
1305
|
notNull: false;
|
|
940
1306
|
hasDefault: false;
|
|
941
1307
|
isPrimaryKey: false;
|
|
942
1308
|
isAutoincrement: false;
|
|
943
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;
|
|
944
1325
|
enumValues: undefined;
|
|
945
1326
|
identity: undefined;
|
|
946
1327
|
generated: undefined;
|
|
947
1328
|
}>;
|
|
948
|
-
|
|
1329
|
+
choices: drizzle_orm_pg_core.PgBuildColumn<"device_links", drizzle_orm_pg_core.SetDimensions<drizzle_orm_pg_core.PgIntegerBuilder, 1>, {
|
|
949
1330
|
name: string;
|
|
950
|
-
tableName: "
|
|
951
|
-
dataType: "
|
|
952
|
-
data:
|
|
953
|
-
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;
|
|
954
1350
|
notNull: true;
|
|
955
1351
|
hasDefault: true;
|
|
956
1352
|
isPrimaryKey: false;
|
|
957
1353
|
isAutoincrement: false;
|
|
958
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;
|
|
959
1370
|
enumValues: undefined;
|
|
960
1371
|
identity: undefined;
|
|
961
1372
|
generated: undefined;
|
|
962
1373
|
}>;
|
|
963
|
-
|
|
1374
|
+
redeemedAt: drizzle_orm_pg_core.PgBuildColumn<"device_links", drizzle_orm_pg_core.PgTimestampBuilder, {
|
|
964
1375
|
name: string;
|
|
965
|
-
tableName: "
|
|
1376
|
+
tableName: "device_links";
|
|
966
1377
|
dataType: "object date";
|
|
967
1378
|
data: Date;
|
|
968
1379
|
driverParam: string;
|
|
@@ -975,9 +1386,9 @@ declare const passkeys: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
975
1386
|
identity: undefined;
|
|
976
1387
|
generated: undefined;
|
|
977
1388
|
}>;
|
|
978
|
-
|
|
1389
|
+
approvedAt: drizzle_orm_pg_core.PgBuildColumn<"device_links", drizzle_orm_pg_core.PgTimestampBuilder, {
|
|
979
1390
|
name: string;
|
|
980
|
-
tableName: "
|
|
1391
|
+
tableName: "device_links";
|
|
981
1392
|
dataType: "object date";
|
|
982
1393
|
data: Date;
|
|
983
1394
|
driverParam: string;
|
|
@@ -990,11 +1401,11 @@ declare const passkeys: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
990
1401
|
identity: undefined;
|
|
991
1402
|
generated: undefined;
|
|
992
1403
|
}>;
|
|
993
|
-
|
|
1404
|
+
consumedAt: drizzle_orm_pg_core.PgBuildColumn<"device_links", drizzle_orm_pg_core.PgTimestampBuilder, {
|
|
994
1405
|
name: string;
|
|
995
|
-
tableName: "
|
|
996
|
-
dataType: "
|
|
997
|
-
data:
|
|
1406
|
+
tableName: "device_links";
|
|
1407
|
+
dataType: "object date";
|
|
1408
|
+
data: Date;
|
|
998
1409
|
driverParam: string;
|
|
999
1410
|
notNull: false;
|
|
1000
1411
|
hasDefault: false;
|
|
@@ -1008,8 +1419,162 @@ declare const passkeys: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
1008
1419
|
};
|
|
1009
1420
|
dialect: "pg";
|
|
1010
1421
|
}>;
|
|
1011
|
-
type
|
|
1012
|
-
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>;
|
|
1013
1578
|
|
|
1014
1579
|
/**
|
|
1015
1580
|
* @spfn/auth - Passkey Service
|
|
@@ -1253,7 +1818,7 @@ declare const authLoginEvent: _spfn_core_event.EventDef<{
|
|
|
1253
1818
|
email?: string | undefined;
|
|
1254
1819
|
phone?: string | undefined;
|
|
1255
1820
|
userId: string;
|
|
1256
|
-
provider: "
|
|
1821
|
+
provider: "passkey" | "google" | "apple" | "github" | "kakao" | "naver" | "superself" | "email" | "phone" | "device";
|
|
1257
1822
|
mfaEnrolled: boolean;
|
|
1258
1823
|
}>;
|
|
1259
1824
|
/**
|
|
@@ -1265,15 +1830,15 @@ declare const authLoginEvent: _spfn_core_event.EventDef<{
|
|
|
1265
1830
|
* both arrive at `createVerifiedAccount`, so the two name themselves there;
|
|
1266
1831
|
* `'invitation'` is the one path that stores a key without the key service.
|
|
1267
1832
|
*/
|
|
1268
|
-
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">]>;
|
|
1269
|
-
/** 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. */
|
|
1270
1835
|
type DeviceRegistrationChannel = Static<typeof DeviceRegistrationChannelSchema>;
|
|
1271
1836
|
/**
|
|
1272
1837
|
* auth.device.registered — a new device key was added to an account
|
|
1273
1838
|
*
|
|
1274
1839
|
* 발행 시점:
|
|
1275
1840
|
* - a key row was created for an account and the transaction that created it
|
|
1276
|
-
* committed, on every one of the
|
|
1841
|
+
* committed, on every one of the eleven channels above
|
|
1277
1842
|
*
|
|
1278
1843
|
* This is the notice an account owner needs and could not get before: a stolen
|
|
1279
1844
|
* password used to sign in on a new device was silent, because a login event
|
|
@@ -1305,13 +1870,13 @@ declare const authDeviceRegisteredEvent: _spfn_core_event.EventDef<{
|
|
|
1305
1870
|
platform?: string | undefined;
|
|
1306
1871
|
ip?: string | undefined;
|
|
1307
1872
|
userAgent?: string | undefined;
|
|
1873
|
+
userId: string;
|
|
1308
1874
|
keyId: string;
|
|
1309
1875
|
algorithm: string;
|
|
1310
|
-
userId: string;
|
|
1311
1876
|
mfaEnrolled: boolean;
|
|
1312
1877
|
fingerprintPrefix: string;
|
|
1313
1878
|
createdAtMillis: number;
|
|
1314
|
-
channel: "
|
|
1879
|
+
channel: "passkey" | "renewal" | "register" | "signup-link" | "invitation" | "password" | "oauth" | "oauth-native" | "device-code" | "device-link" | "password-reset";
|
|
1315
1880
|
}>;
|
|
1316
1881
|
/**
|
|
1317
1882
|
* auth.register - 회원가입 성공 이벤트
|
|
@@ -1334,7 +1899,7 @@ declare const authRegisterEvent: _spfn_core_event.EventDef<{
|
|
|
1334
1899
|
[x: string]: unknown;
|
|
1335
1900
|
} | undefined;
|
|
1336
1901
|
userId: string;
|
|
1337
|
-
provider: "
|
|
1902
|
+
provider: "google" | "apple" | "github" | "kakao" | "naver" | "superself" | "email" | "phone";
|
|
1338
1903
|
}>;
|
|
1339
1904
|
/**
|
|
1340
1905
|
* auth.invitation.created - 초대 생성 이벤트
|
|
@@ -1361,11 +1926,11 @@ declare const invitationCreatedEvent: _spfn_core_event.EventDef<{
|
|
|
1361
1926
|
[x: string]: unknown;
|
|
1362
1927
|
} | undefined;
|
|
1363
1928
|
email: string;
|
|
1364
|
-
token: string;
|
|
1365
|
-
expiresAt: string;
|
|
1366
1929
|
roleId: number;
|
|
1367
|
-
|
|
1930
|
+
expiresAt: string;
|
|
1368
1931
|
invitationId: string;
|
|
1932
|
+
token: string;
|
|
1933
|
+
invitedBy: string;
|
|
1369
1934
|
isResend: boolean;
|
|
1370
1935
|
}>;
|
|
1371
1936
|
/**
|
|
@@ -1385,11 +1950,11 @@ declare const invitationAcceptedEvent: _spfn_core_event.EventDef<{
|
|
|
1385
1950
|
metadata?: {
|
|
1386
1951
|
[x: string]: unknown;
|
|
1387
1952
|
} | undefined;
|
|
1388
|
-
email: string;
|
|
1389
1953
|
userId: string;
|
|
1954
|
+
email: string;
|
|
1390
1955
|
roleId: number;
|
|
1391
|
-
invitedBy: string;
|
|
1392
1956
|
invitationId: string;
|
|
1957
|
+
invitedBy: string;
|
|
1393
1958
|
}>;
|
|
1394
1959
|
/**
|
|
1395
1960
|
* auth.deletion.requested - 계정 탈퇴 요청 이벤트
|
|
@@ -1408,7 +1973,7 @@ declare const authDeletionRequestedEvent: _spfn_core_event.EventDef<{
|
|
|
1408
1973
|
userId: string;
|
|
1409
1974
|
purgeScheduledAt: string;
|
|
1410
1975
|
userPublicId: string;
|
|
1411
|
-
requestedBy: "
|
|
1976
|
+
requestedBy: "self" | "admin";
|
|
1412
1977
|
}>;
|
|
1413
1978
|
/**
|
|
1414
1979
|
* auth.deletion.cancelled - 계정 탈퇴 복구 이벤트
|
|
@@ -1453,7 +2018,7 @@ declare const authDeletionCompletedEvent: _spfn_core_event.EventDef<{
|
|
|
1453
2018
|
declare const oauthUnlinkedEvent: _spfn_core_event.EventDef<{
|
|
1454
2019
|
reason?: string | undefined;
|
|
1455
2020
|
userId: string;
|
|
1456
|
-
provider: "
|
|
2021
|
+
provider: "google" | "apple" | "github" | "kakao" | "naver" | "superself" | "email" | "phone";
|
|
1457
2022
|
providerUserId: string;
|
|
1458
2023
|
}>;
|
|
1459
2024
|
/**
|
|
@@ -1475,8 +2040,8 @@ declare const oauthUnlinkedEvent: _spfn_core_event.EventDef<{
|
|
|
1475
2040
|
* ```
|
|
1476
2041
|
*/
|
|
1477
2042
|
declare const authPasswordResetEvent: _spfn_core_event.EventDef<{
|
|
1478
|
-
email: string;
|
|
1479
2043
|
userId: string;
|
|
2044
|
+
email: string;
|
|
1480
2045
|
}>;
|
|
1481
2046
|
/**
|
|
1482
2047
|
* Auth event payload types
|
|
@@ -1624,7 +2189,7 @@ declare const mfaChallenges: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
1624
2189
|
name: string;
|
|
1625
2190
|
tableName: "mfa_challenges";
|
|
1626
2191
|
dataType: "string enum";
|
|
1627
|
-
data: "password" | "oauth
|
|
2192
|
+
data: "password" | "oauth" | "oauth-native" | "password-reset";
|
|
1628
2193
|
driverParam: string;
|
|
1629
2194
|
notNull: true;
|
|
1630
2195
|
hasDefault: false;
|
|
@@ -3111,94 +3676,6 @@ declare function disableSessionBindingService(params: DisableSessionBindingParam
|
|
|
3111
3676
|
/** The challenge the disabling ceremony signs. Same ceremony renewal uses. */
|
|
3112
3677
|
declare function startSessionBindingDisableService(userId: number): Promise<PublicKeyCredentialRequestOptionsJSON>;
|
|
3113
3678
|
|
|
3114
|
-
/**
|
|
3115
|
-
* @spfn/auth - Session Renewal Service
|
|
3116
|
-
*
|
|
3117
|
-
* What a bound session does when its key runs out: prove, with a fresh WebAuthn
|
|
3118
|
-
* assertion, that the person who enrolled the passkey is still at the machine,
|
|
3119
|
-
* and get a new short-lived key sealed into the cookie.
|
|
3120
|
-
*
|
|
3121
|
-
* Neither step is public. The expiring key is named by `expiredKeyId`, and that
|
|
3122
|
-
* value reaches the service from `authenticateForRenewal` — the `keyId` of a
|
|
3123
|
-
* bearer JWT this very key signed — rather than from the request body, so a
|
|
3124
|
-
* caller who does not hold the private half cannot name a key at all. The
|
|
3125
|
-
* assertion still has to be signed by a passkey that key's owner enrolled: the
|
|
3126
|
-
* signature proves the cookie, and the cookie is the thing that may have been
|
|
3127
|
-
* copied.
|
|
3128
|
-
*
|
|
3129
|
-
* The admission below is run again here all the same. The middleware and the
|
|
3130
|
-
* service ask the same four questions of the row, and a service that trusted its
|
|
3131
|
-
* caller to have asked them would be one refactor away from not being asked at
|
|
3132
|
-
* all.
|
|
3133
|
-
*
|
|
3134
|
-
* Every refusal is the same refusal. A key that never existed, a stranger's key,
|
|
3135
|
-
* an unbound key, a revoked one, one past its grace, an inactive account, a spent
|
|
3136
|
-
* challenge, an assertion that did not verify — all `SessionRenewalRefusedError`,
|
|
3137
|
-
* with the same body, because anything finer would answer "is this key id live"
|
|
3138
|
-
* to whoever asked.
|
|
3139
|
-
*
|
|
3140
|
-
* Renewal announces nothing. No `auth.login`, no `auth.device.registered`, and
|
|
3141
|
-
* `lastLoginAt` does not move: this is the same person on the same device
|
|
3142
|
-
* continuing the session they already had, and a subscriber mailing "new sign-in"
|
|
3143
|
-
* once a day per device would train its reader to ignore the notice that matters.
|
|
3144
|
-
* A `lastLoginAt` that moved every day would make dormant-account detection
|
|
3145
|
-
* meaningless for exactly the accounts that turned this protection on.
|
|
3146
|
-
*/
|
|
3147
|
-
|
|
3148
|
-
interface StartSessionRenewParams {
|
|
3149
|
-
/** The key that ran out, read off the JWT the request was signed with. */
|
|
3150
|
-
expiredKeyId: string;
|
|
3151
|
-
}
|
|
3152
|
-
interface FinishSessionRenewParams extends StartSessionRenewParams {
|
|
3153
|
-
/** The assertion, from `navigator.credentials.get()`. */
|
|
3154
|
-
response: AuthenticationResponseJSON;
|
|
3155
|
-
/**
|
|
3156
|
-
* The new key pair, in the vocabulary the Next.js login interceptor already
|
|
3157
|
-
* writes: `renew/verify` is on that interceptor's path list, so these arrive
|
|
3158
|
-
* exactly as they do on a login.
|
|
3159
|
-
*/
|
|
3160
|
-
keyId: string;
|
|
3161
|
-
publicKey: string;
|
|
3162
|
-
fingerprint: string;
|
|
3163
|
-
algorithm?: KeyAlgorithmType;
|
|
3164
|
-
}
|
|
3165
|
-
/**
|
|
3166
|
-
* What a completed renewal answers: a sign-in result, plus the new key's id.
|
|
3167
|
-
*
|
|
3168
|
-
* The id is the one thing a renewal has that a sign-in does not need to say —
|
|
3169
|
-
* `renewSession()` promises it to the app, which has no other way to learn it
|
|
3170
|
-
* (the key pair is minted in the proxy and the private half never leaves the
|
|
3171
|
-
* cookie). It is not a contract operation, so nothing generated reads it.
|
|
3172
|
-
*/
|
|
3173
|
-
interface SessionRenewResult extends LoginResult {
|
|
3174
|
-
/** The key this renewal registered, the one the session now signs with. */
|
|
3175
|
-
keyId: string;
|
|
3176
|
-
}
|
|
3177
|
-
/**
|
|
3178
|
-
* Step 1 — the challenge the authenticator signs.
|
|
3179
|
-
*
|
|
3180
|
-
* `allowCredentials` is empty and the account lives only on the challenge row.
|
|
3181
|
-
* See `startRenewalCeremonyService`.
|
|
3182
|
-
*
|
|
3183
|
-
* @throws SessionRenewalRefusedError 갱신할 수 없는 키·계정일 때 (모든 사유 동일)
|
|
3184
|
-
*/
|
|
3185
|
-
declare function startSessionRenewService(params: StartSessionRenewParams): Promise<PublicKeyCredentialRequestOptionsJSON>;
|
|
3186
|
-
/**
|
|
3187
|
-
* Step 2 — verify the assertion, put a new bound key in place of the old one.
|
|
3188
|
-
*
|
|
3189
|
-
* The revocation runs first and its answer is the race winner: two verifies that
|
|
3190
|
-
* both got past their own challenges meet at the same conditional UPDATE, and
|
|
3191
|
-
* only the one that actually revoked the key goes on to register a replacement.
|
|
3192
|
-
*
|
|
3193
|
-
* The new key inherits the old row's provenance, so the device list keeps saying
|
|
3194
|
-
* where this device first appeared rather than re-stamping itself every day. Its
|
|
3195
|
-
* expiry is a fresh window from now — renewal is a renewal, not an extension of
|
|
3196
|
-
* what the old key had.
|
|
3197
|
-
*
|
|
3198
|
-
* @throws SessionRenewalRefusedError 갱신할 수 없을 때 (증명 실패 포함, 모든 사유 동일)
|
|
3199
|
-
*/
|
|
3200
|
-
declare function finishSessionRenewService(params: FinishSessionRenewParams): Promise<SessionRenewResult>;
|
|
3201
|
-
|
|
3202
3679
|
/**
|
|
3203
3680
|
* What `POST /_auth/oauth/finalize` answers with, in both of its branches.
|
|
3204
3681
|
*
|
|
@@ -3220,6 +3697,87 @@ interface OAuthFinalizeResponse {
|
|
|
3220
3697
|
keyExpiresAtMillis?: number;
|
|
3221
3698
|
}
|
|
3222
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
|
+
|
|
3223
3781
|
/**
|
|
3224
3782
|
* @spfn/auth - Main Router
|
|
3225
3783
|
*
|
|
@@ -3389,6 +3947,59 @@ declare const mainAuthRouter: _spfn_core_route.Router<{
|
|
|
3389
3947
|
userCode: _sinclair_typebox.TString;
|
|
3390
3948
|
}>;
|
|
3391
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
|
+
}>;
|
|
3392
4003
|
passkeyRegisterOptions: _spfn_core_route.RouteDef<{
|
|
3393
4004
|
body: _sinclair_typebox.TObject<{
|
|
3394
4005
|
currentPassword: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
@@ -3549,7 +4160,7 @@ declare const mainAuthRouter: _spfn_core_route.Router<{
|
|
|
3549
4160
|
id: number;
|
|
3550
4161
|
name: string;
|
|
3551
4162
|
displayName: string;
|
|
3552
|
-
category: "
|
|
4163
|
+
category: "custom" | "user" | "auth" | "rbac" | "system" | undefined;
|
|
3553
4164
|
}[];
|
|
3554
4165
|
userId: number;
|
|
3555
4166
|
publicId: string;
|
|
@@ -3874,8 +4485,8 @@ declare const mainAuthRouter: _spfn_core_route.Router<{
|
|
|
3874
4485
|
}, {}, {
|
|
3875
4486
|
roles: {
|
|
3876
4487
|
description: string | null;
|
|
3877
|
-
name: string;
|
|
3878
4488
|
id: number;
|
|
4489
|
+
name: string;
|
|
3879
4490
|
displayName: string;
|
|
3880
4491
|
isBuiltin: boolean;
|
|
3881
4492
|
isSystem: boolean;
|
|
@@ -3896,8 +4507,8 @@ declare const mainAuthRouter: _spfn_core_route.Router<{
|
|
|
3896
4507
|
}, {}, {
|
|
3897
4508
|
role: {
|
|
3898
4509
|
description: string | null;
|
|
3899
|
-
name: string;
|
|
3900
4510
|
id: number;
|
|
4511
|
+
name: string;
|
|
3901
4512
|
displayName: string;
|
|
3902
4513
|
isBuiltin: boolean;
|
|
3903
4514
|
isSystem: boolean;
|
|
@@ -3920,8 +4531,8 @@ declare const mainAuthRouter: _spfn_core_route.Router<{
|
|
|
3920
4531
|
}, {}, {
|
|
3921
4532
|
role: {
|
|
3922
4533
|
description: string | null;
|
|
3923
|
-
name: string;
|
|
3924
4534
|
id: number;
|
|
4535
|
+
name: string;
|
|
3925
4536
|
displayName: string;
|
|
3926
4537
|
isBuiltin: boolean;
|
|
3927
4538
|
isSystem: boolean;
|
|
@@ -4772,4 +5383,4 @@ declare const machineAuth: _spfn_core_route.NamedMiddleware<"machineAuth">;
|
|
|
4772
5383
|
*/
|
|
4773
5384
|
declare const requireMachineScope: _spfn_core_route.NamedMiddlewareFactory<"machineScope", string[]>;
|
|
4774
5385
|
|
|
4775
|
-
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 };
|