@spfn/auth 0.3.0-beta.23 → 0.3.0-beta.24
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 +165 -2
- package/dist/client-proof.js +13 -5
- package/dist/client-proof.js.map +1 -1
- package/dist/client.d.ts +52 -1
- package/dist/client.js +40 -0
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +82 -0
- package/dist/config.js +37 -0
- package/dist/config.js.map +1 -1
- package/dist/crypto.d.ts +1 -1
- package/dist/errors.d.ts +113 -3
- package/dist/errors.js +68 -1
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +38 -5
- package/dist/index.js +73 -2
- package/dist/index.js.map +1 -1
- package/dist/{machine-principals-CaEFq61K.d.ts → machine-principals-ZJd9anVT.d.ts} +1667 -900
- package/dist/nextjs/api.js +156 -6
- package/dist/nextjs/api.js.map +1 -1
- package/dist/nextjs/server.d.ts +42 -24
- package/dist/nextjs/server.js +72 -5
- package/dist/nextjs/server.js.map +1 -1
- package/dist/server.d.ts +448 -525
- package/dist/server.js +1389 -653
- package/dist/server.js.map +1 -1
- package/dist/{session-Dfwu5g2W.d.ts → session-BbhAGZtA.d.ts} +57 -1
- package/dist/{types-DYyhze28.d.ts → types-CTdoTOxM.d.ts} +24 -1
- package/migrations/20260918184037_happy_mordo/migration.sql +4 -0
- package/migrations/20260918184037_happy_mordo/snapshot.json +6000 -0
- package/migrations/20260918184152_dear_rictor/migration.sql +3 -0
- package/migrations/20260918184152_dear_rictor/snapshot.json +6039 -0
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as _simplewebauthn_server from '@simplewebauthn/server';
|
|
2
|
-
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';
|
|
3
4
|
import * as _spfn_core_route from '@spfn/core/route';
|
|
4
|
-
import { K as KeyAlgorithmType, h as KeyPlatformType, j as SocialProvider } from './types-DYyhze28.js';
|
|
5
5
|
import * as _sinclair_typebox from '@sinclair/typebox';
|
|
6
6
|
import { Static } from '@sinclair/typebox';
|
|
7
7
|
import * as drizzle_orm_pg_core from 'drizzle-orm/pg-core';
|
|
@@ -91,561 +91,29 @@ interface UserProfile {
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
/**
|
|
94
|
-
* @spfn/auth -
|
|
95
|
-
*
|
|
96
|
-
* Core authentication logic: registration, login, logout, password management
|
|
97
|
-
*/
|
|
98
|
-
|
|
99
|
-
interface RegisterParams {
|
|
100
|
-
email?: string;
|
|
101
|
-
phone?: string;
|
|
102
|
-
verificationToken: string;
|
|
103
|
-
password: string;
|
|
104
|
-
publicKey: string;
|
|
105
|
-
keyId: string;
|
|
106
|
-
fingerprint: string;
|
|
107
|
-
algorithm?: KeyAlgorithmType;
|
|
108
|
-
deviceName?: string;
|
|
109
|
-
platform?: KeyPlatformType;
|
|
110
|
-
metadata?: Record<string, unknown>;
|
|
111
|
-
/** Client address of the request, from `deviceProvenance` at the route. */
|
|
112
|
-
ip?: string;
|
|
113
|
-
/** `user-agent` of the request, already truncated at the route. */
|
|
114
|
-
userAgent?: string;
|
|
115
|
-
}
|
|
116
|
-
interface RegisterResult {
|
|
117
|
-
userId: string;
|
|
118
|
-
publicId: string;
|
|
119
|
-
email?: string;
|
|
120
|
-
phone?: string;
|
|
121
|
-
}
|
|
122
|
-
interface LoginParams {
|
|
123
|
-
email?: string;
|
|
124
|
-
phone?: string;
|
|
125
|
-
password: string;
|
|
126
|
-
publicKey: string;
|
|
127
|
-
keyId: string;
|
|
128
|
-
fingerprint: string;
|
|
129
|
-
oldKeyId?: string;
|
|
130
|
-
algorithm?: KeyAlgorithmType;
|
|
131
|
-
deviceName?: string;
|
|
132
|
-
platform?: KeyPlatformType;
|
|
133
|
-
/** Client address of the request, from `deviceProvenance` at the route. */
|
|
134
|
-
ip?: string;
|
|
135
|
-
/** `user-agent` of the request, already truncated at the route. */
|
|
136
|
-
userAgent?: string;
|
|
137
|
-
}
|
|
138
|
-
interface LoginResult {
|
|
139
|
-
userId: string;
|
|
140
|
-
publicId: string;
|
|
141
|
-
email?: string;
|
|
142
|
-
phone?: string;
|
|
143
|
-
passwordChangeRequired: boolean;
|
|
144
|
-
}
|
|
145
|
-
interface LogoutParams {
|
|
146
|
-
userId: number;
|
|
147
|
-
keyId: string;
|
|
148
|
-
}
|
|
149
|
-
interface ChangePasswordParams {
|
|
150
|
-
userId: number;
|
|
151
|
-
/**
|
|
152
|
-
* The device key this request is signed with.
|
|
153
|
-
*
|
|
154
|
-
* Only read to measure the second-factor window of an enrolled account —
|
|
155
|
-
* an account with nothing enrolled is answered exactly as before, so this
|
|
156
|
-
* adds no refusal for anybody who has not opted in.
|
|
157
|
-
*/
|
|
158
|
-
keyId: string;
|
|
159
|
-
currentPassword?: string;
|
|
160
|
-
newPassword: string;
|
|
161
|
-
passwordHash?: string;
|
|
162
|
-
}
|
|
163
|
-
/**
|
|
164
|
-
* Register a new user account
|
|
165
|
-
*/
|
|
166
|
-
declare function registerService(params: RegisterParams): Promise<RegisterResult>;
|
|
167
|
-
/**
|
|
168
|
-
* Authenticate user and create session
|
|
169
|
-
*/
|
|
170
|
-
declare function loginService(params: LoginParams): Promise<LoginResult>;
|
|
171
|
-
/**
|
|
172
|
-
* Logout user (revoke current key)
|
|
173
|
-
*/
|
|
174
|
-
declare function logoutService(params: LogoutParams): Promise<void>;
|
|
175
|
-
/**
|
|
176
|
-
* Change user password
|
|
177
|
-
*
|
|
178
|
-
* An enrolled account steps up first (#95): a stolen session must not be able
|
|
179
|
-
* to take the account over by setting a new password. An unenrolled account is
|
|
180
|
-
* unaffected — including the OAuth-only account with no password and a key
|
|
181
|
-
* older than ten minutes, which still sets a first password and gets a 200.
|
|
182
|
-
*/
|
|
183
|
-
declare function changePasswordService(params: ChangePasswordParams): Promise<void>;
|
|
184
|
-
|
|
185
|
-
declare const EmailSchema: _sinclair_typebox.TString;
|
|
186
|
-
declare const PhoneSchema: _sinclair_typebox.TString;
|
|
187
|
-
/**
|
|
188
|
-
* Optional device labels a client may send when registering a key.
|
|
189
|
-
*
|
|
190
|
-
* Display only: the key list uses them to tell one device from another, and
|
|
191
|
-
* nothing is authorized or refused by either value, so a client that lies about
|
|
192
|
-
* them gains nothing. Both are omitted by every key registered before they
|
|
193
|
-
* existed, hence optional rather than defaulted.
|
|
194
|
-
*/
|
|
195
|
-
declare const DeviceNameSchema: _sinclair_typebox.TString;
|
|
196
|
-
declare const PlatformSchema: _sinclair_typebox.TUnion<_sinclair_typebox.TLiteral<"ios" | "android" | "web" | "desktop">[]>;
|
|
197
|
-
/**
|
|
198
|
-
* Key material as a device sends it, bounded.
|
|
199
|
-
*
|
|
200
|
-
* The bounds exist for the one route that takes this material from a caller who
|
|
201
|
-
* has not authenticated and cannot: `POST /_auth/device/start` persists what it
|
|
202
|
-
* is given, and a correctly fingerprinted megabyte of base64 would sit in
|
|
203
|
-
* `device_authorizations` until something swept it — and nothing sweeps it.
|
|
204
|
-
*
|
|
205
|
-
* The numbers are what real key material measures, with room to spare. The
|
|
206
|
-
* package's own generators produce SPKI DER in base64: 124 characters for
|
|
207
|
-
* ES256 (P-256), 392 for RS256 (RSA-2048). An RSA-4096 key would be 736, an
|
|
208
|
-
* RSA-8192 key about 1400, and the same 4096-bit key PEM-armoured about 800 —
|
|
209
|
-
* so 2048 admits every shape of key anyone could reasonably present, while a
|
|
210
|
-
* megabyte is refused three orders of magnitude before it reaches a row.
|
|
211
|
-
*
|
|
212
|
-
* `keyId` is a UUID (36) everywhere this package generates one; 64 leaves room
|
|
213
|
-
* for a client that prefixes or namespaces its own. `fingerprint` is SHA-256
|
|
214
|
-
* hex, exactly 64, and nothing else can ever verify against the public key —
|
|
215
|
-
* 128 is the length a longer digest would need, and no more.
|
|
216
|
-
*/
|
|
217
|
-
declare const PublicKeySchema: _sinclair_typebox.TString;
|
|
218
|
-
declare const KeyIdSchema: _sinclair_typebox.TString;
|
|
219
|
-
declare const FingerprintSchema: _sinclair_typebox.TString;
|
|
220
|
-
/**
|
|
221
|
-
* The code a person reads off the waiting device and types on their own.
|
|
222
|
-
*
|
|
223
|
-
* Loose on purpose: 8 characters plus an optional dash is what is shown, but the
|
|
224
|
-
* server folds whitespace, dashes and lower case away before looking anything up,
|
|
225
|
-
* so refusing those spellings here would refuse a code that is on screen. The
|
|
226
|
-
* bounds exist to stop an unbounded string reaching the database, not to spell
|
|
227
|
-
* out the format — `USER_CODE_ALPHABET` is the only thing that can match a row.
|
|
228
|
-
*/
|
|
229
|
-
declare const UserCodeSchema: _sinclair_typebox.TString;
|
|
230
|
-
/**
|
|
231
|
-
* What `POST /_auth/device/poll` answers with.
|
|
232
|
-
*
|
|
233
|
-
* A union, because the two answers are different kinds of thing rather than one
|
|
234
|
-
* shape with optional fields: pending says "ask again in this long", approved is
|
|
235
|
-
* a completed login carrying exactly what `/_auth/login` returns. `status` is the
|
|
236
|
-
* discriminant, so a generated client narrows on it instead of testing which
|
|
237
|
-
* fields happen to be present.
|
|
94
|
+
* @spfn/auth - Passkeys Entity
|
|
238
95
|
*
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
* and a count of milliseconds never needed one.
|
|
96
|
+
* A WebAuthn credential the account owner enrolled on one of their devices.
|
|
97
|
+
* It is a *credential*, not a session: an assertion proves who is asking, and
|
|
98
|
+
* the ordinary device key in `user_public_keys` is what the request afterwards
|
|
99
|
+
* is signed with. The two tables therefore never stand in for each other.
|
|
244
100
|
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
* that
|
|
248
|
-
*
|
|
249
|
-
* exported declaration, so writing `Type.Number` here fails the suite instead of
|
|
250
|
-
* publishing an integer the server does not send.
|
|
251
|
-
*/
|
|
252
|
-
declare const DeviceAuthPollResponseSchema: _sinclair_typebox.TUnion<[_sinclair_typebox.TObject<{
|
|
253
|
-
status: _sinclair_typebox.TLiteral<"pending">;
|
|
254
|
-
intervalMillis: _sinclair_typebox.TInteger;
|
|
255
|
-
}>, _sinclair_typebox.TObject<{
|
|
256
|
-
status: _sinclair_typebox.TLiteral<"approved">;
|
|
257
|
-
userId: _sinclair_typebox.TString;
|
|
258
|
-
publicId: _sinclair_typebox.TString;
|
|
259
|
-
email: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
260
|
-
phone: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
261
|
-
passwordChangeRequired: _sinclair_typebox.TBoolean;
|
|
262
|
-
}>]>;
|
|
263
|
-
declare const PasswordSchema: _sinclair_typebox.TString;
|
|
264
|
-
declare const TargetTypeSchema: _sinclair_typebox.TUnion<[_sinclair_typebox.TLiteral<"email">, _sinclair_typebox.TLiteral<"phone">]>;
|
|
265
|
-
type VerificationTargetType = Static<typeof TargetTypeSchema>;
|
|
266
|
-
declare const VERIFICATION_TARGET_TYPES: readonly ["email", "phone"];
|
|
267
|
-
declare const VerificationPurposeSchema: _sinclair_typebox.TUnion<[_sinclair_typebox.TLiteral<"registration">, _sinclair_typebox.TLiteral<"login">, _sinclair_typebox.TLiteral<"password_reset">, _sinclair_typebox.TLiteral<"email_change">, _sinclair_typebox.TLiteral<"phone_change">, _sinclair_typebox.TLiteral<"account_deletion">]>;
|
|
268
|
-
type VerificationPurpose = Static<typeof VerificationPurposeSchema>;
|
|
269
|
-
declare const VERIFICATION_PURPOSES: readonly ["registration", "login", "password_reset", "email_change", "phone_change", "account_deletion"];
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
* @spfn/auth - Verification Service
|
|
101
|
+
* Nothing here is a bearer value, so nothing is hashed. `publicKey` is public by
|
|
102
|
+
* construction and `credentialId` is a handle the authenticator hands to any
|
|
103
|
+
* origin that asks — storing either in the clear costs nothing, and the lookup
|
|
104
|
+
* on `credentialId` has to be a plain equality match on an indexed column.
|
|
273
105
|
*
|
|
274
|
-
*
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
interface SendVerificationCodeParams {
|
|
278
|
-
target: string;
|
|
279
|
-
targetType: VerificationTargetType;
|
|
280
|
-
purpose: VerificationPurpose;
|
|
281
|
-
}
|
|
282
|
-
interface SendVerificationCodeResult {
|
|
283
|
-
success: boolean;
|
|
284
|
-
expiresAt: string;
|
|
285
|
-
}
|
|
286
|
-
interface VerifyCodeParams {
|
|
287
|
-
target: string;
|
|
288
|
-
targetType: VerificationTargetType;
|
|
289
|
-
code: string;
|
|
290
|
-
purpose: VerificationPurpose;
|
|
291
|
-
}
|
|
292
|
-
interface VerifyCodeResult {
|
|
293
|
-
valid: boolean;
|
|
294
|
-
verificationToken: string;
|
|
295
|
-
}
|
|
296
|
-
/**
|
|
297
|
-
* Send verification code via email or SMS
|
|
298
|
-
*/
|
|
299
|
-
declare function sendVerificationCodeService(params: SendVerificationCodeParams): Promise<SendVerificationCodeResult>;
|
|
300
|
-
/**
|
|
301
|
-
* Verify OTP code and return verification token
|
|
106
|
+
* Revocation is soft, and `credentialId` stays unique across live and revoked
|
|
107
|
+
* rows alike: a credential someone cut off must never become enrollable again,
|
|
108
|
+
* on this account or on another one.
|
|
302
109
|
*/
|
|
303
|
-
declare function verifyCodeService(params: VerifyCodeParams): Promise<{
|
|
304
|
-
valid: boolean;
|
|
305
|
-
verificationToken: string;
|
|
306
|
-
}>;
|
|
307
|
-
|
|
308
110
|
/**
|
|
309
|
-
*
|
|
310
|
-
*
|
|
311
|
-
* A signup where the address is proven before a password exists:
|
|
312
|
-
*
|
|
313
|
-
* request -> a one-time link is emailed
|
|
314
|
-
* confirm -> the link is exchanged for a short-lived password-setup session
|
|
315
|
-
* password -> the account is created, the device registered, the user signed in
|
|
316
|
-
*
|
|
317
|
-
* The link token and the setup secret are bearer credentials, so neither is ever
|
|
318
|
-
* stored. Only their SHA-256 hashes are, and lookup is by hash. A database dump
|
|
319
|
-
* therefore yields nothing that can be presented to either step.
|
|
111
|
+
* Whether the credential can leave the authenticator that minted it.
|
|
320
112
|
*
|
|
321
|
-
*
|
|
322
|
-
* is
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
interface RequestSignupLinkParams {
|
|
326
|
-
email: string;
|
|
327
|
-
returnPath?: string;
|
|
328
|
-
}
|
|
329
|
-
interface RequestSignupLinkResult {
|
|
330
|
-
success: boolean;
|
|
331
|
-
expiresAt: string;
|
|
332
|
-
}
|
|
333
|
-
/**
|
|
334
|
-
* Step 1 — issue a confirmation link for an address.
|
|
335
|
-
*
|
|
336
|
-
* Answers identically whether or not the address already has an account. When it
|
|
337
|
-
* does, the owner gets a notice instead of a usable link, through the same
|
|
338
|
-
* dedupe window the six-digit-code path uses.
|
|
339
|
-
*
|
|
340
|
-
* Requesting again is how a resend works: every live link for the address is
|
|
341
|
-
* superseded first, so the newest link is the only one that opens, and any setup
|
|
342
|
-
* session already opened from an older link dies with it.
|
|
343
|
-
*
|
|
344
|
-
* Neither branch sends mail: both hand it to `auth.link-mail`, so the answer
|
|
345
|
-
* costs the same database work whichever one ran. With no pg-boss initialised
|
|
346
|
-
* the mail still goes out on this request — see `lib/link-mail-delivery.ts`.
|
|
347
|
-
*/
|
|
348
|
-
declare function requestSignupLinkService(params: RequestSignupLinkParams): Promise<RequestSignupLinkResult>;
|
|
349
|
-
interface ConfirmSignupLinkParams {
|
|
350
|
-
token: string;
|
|
351
|
-
}
|
|
352
|
-
interface ConfirmSignupLinkResult {
|
|
353
|
-
email: string;
|
|
354
|
-
returnPath: string | null;
|
|
355
|
-
/** Handed to the proxy interceptor, which moves it into an HttpOnly cookie. */
|
|
356
|
-
setupSecret: string;
|
|
357
|
-
setupExpiresAt: string;
|
|
358
|
-
}
|
|
359
|
-
/**
|
|
360
|
-
* Step 2 — exchange a link for a password-setup session.
|
|
361
|
-
*
|
|
362
|
-
* Nothing binds the row to a device or a browser, which is what lets someone
|
|
363
|
-
* request the link on a laptop and open it on a phone.
|
|
364
|
-
*/
|
|
365
|
-
declare function confirmSignupLinkService(params: ConfirmSignupLinkParams): Promise<ConfirmSignupLinkResult>;
|
|
366
|
-
interface CompleteSignupParams {
|
|
367
|
-
setupSecret?: string;
|
|
368
|
-
password: string;
|
|
369
|
-
publicKey: string;
|
|
370
|
-
keyId: string;
|
|
371
|
-
fingerprint: string;
|
|
372
|
-
algorithm?: KeyAlgorithmType;
|
|
373
|
-
deviceName?: string;
|
|
374
|
-
platform?: KeyPlatformType;
|
|
375
|
-
metadata?: Record<string, unknown>;
|
|
376
|
-
/** Client address of the request, from `deviceProvenance` at the route. */
|
|
377
|
-
ip?: string;
|
|
378
|
-
/** `user-agent` of the request, already truncated at the route. */
|
|
379
|
-
userAgent?: string;
|
|
380
|
-
}
|
|
381
|
-
/**
|
|
382
|
-
* Step 3 — set the password, which is what creates the account.
|
|
383
|
-
*
|
|
384
|
-
* Run under `Transactional()`: the user row, the device key and the completion
|
|
385
|
-
* mark commit together. A device-key failure must not leave an account nobody
|
|
386
|
-
* can sign into, and a completion mark must not survive a rolled-back account.
|
|
387
|
-
*
|
|
388
|
-
* A refusal that is the user's to fix — a weak password, an app policy that
|
|
389
|
-
* rejects the registration — leaves the setup session usable, so the fix is
|
|
390
|
-
* retyping the password rather than requesting a fresh email.
|
|
391
|
-
*/
|
|
392
|
-
declare function completeSignupService(params: CompleteSignupParams): Promise<RegisterResult>;
|
|
393
|
-
|
|
394
|
-
/**
|
|
395
|
-
* @spfn/auth - Password Reset Service
|
|
396
|
-
*
|
|
397
|
-
* Getting back into an account whose password is gone, using the address the
|
|
398
|
-
* account already proved:
|
|
399
|
-
*
|
|
400
|
-
* request -> a one-time link is emailed
|
|
401
|
-
* confirm -> the link is exchanged for a short-lived password-setup session
|
|
402
|
-
* complete -> the new password is written, everything else is signed out,
|
|
403
|
-
* and the browser that reset is signed in on a fresh device key
|
|
404
|
-
*
|
|
405
|
-
* Mirrors the verified-email signup slice deliberately — same credentials, same
|
|
406
|
-
* hashing, same supersede-on-resend, same interceptor moves — with two
|
|
407
|
-
* differences that matter.
|
|
408
|
-
*
|
|
409
|
-
* First, the request answers identically for *every* input and sends mail only
|
|
410
|
-
* to an account that can be reset. Signup can afford to tell an existing owner
|
|
411
|
-
* "you already have an account"; a reset cannot send anything to a stranger's
|
|
412
|
-
* mailbox, because the mail itself would be the answer to "does this address
|
|
413
|
-
* have an account here".
|
|
414
|
-
*
|
|
415
|
-
* Second, completing it is a credential change on a live account, so it carries
|
|
416
|
-
* the same blast radius as `changePasswordService`: pending device
|
|
417
|
-
* authorizations are denied and every active key is revoked. Whoever was signed
|
|
418
|
-
* in on the old password is signed out, including the attacker the reset was
|
|
419
|
-
* needed for.
|
|
420
|
-
*/
|
|
421
|
-
|
|
422
|
-
interface RequestPasswordResetParams {
|
|
423
|
-
email: string;
|
|
424
|
-
returnPath?: string;
|
|
425
|
-
}
|
|
426
|
-
interface RequestPasswordResetResult {
|
|
427
|
-
success: boolean;
|
|
428
|
-
expiresAt: string;
|
|
429
|
-
}
|
|
430
|
-
/**
|
|
431
|
-
* Step 1 — issue a reset link for an address.
|
|
432
|
-
*
|
|
433
|
-
* Answers identically for every input: the same status, the same two fields, and
|
|
434
|
-
* an `expiresAt` computed the same way whether or not a row was written. An
|
|
435
|
-
* address with no account, an account that cannot be reset, and an account that
|
|
436
|
-
* can are indistinguishable to the caller — only the first of the three gets
|
|
437
|
-
* mail, and it goes to the owner.
|
|
438
|
-
*
|
|
439
|
-
* Requesting again is how a resend works: every live link for the account is
|
|
440
|
-
* superseded first, so the newest link is the only one that opens, and any setup
|
|
441
|
-
* session already opened from an older link dies with it.
|
|
442
|
-
*
|
|
443
|
-
* The eligible branch does not send the mail either — it hands it to
|
|
444
|
-
* `auth.link-mail` — so the two branches differ by a few database writes and not
|
|
445
|
-
* by a mail provider's round trip. With no pg-boss initialised the mail still
|
|
446
|
-
* goes out on this request; see `lib/link-mail-delivery.ts`.
|
|
447
|
-
*/
|
|
448
|
-
declare function requestPasswordResetService(params: RequestPasswordResetParams): Promise<RequestPasswordResetResult>;
|
|
449
|
-
interface ConfirmPasswordResetParams {
|
|
450
|
-
token: string;
|
|
451
|
-
}
|
|
452
|
-
interface ConfirmPasswordResetResult {
|
|
453
|
-
email: string;
|
|
454
|
-
returnPath: string | null;
|
|
455
|
-
/** Handed to the proxy interceptor, which moves it into an HttpOnly cookie. */
|
|
456
|
-
setupSecret: string;
|
|
457
|
-
setupExpiresAt: string;
|
|
458
|
-
}
|
|
459
|
-
/**
|
|
460
|
-
* Step 2 — exchange a link for a password-setup session.
|
|
461
|
-
*
|
|
462
|
-
* Nothing binds the row to a device or a browser, which is what lets someone ask
|
|
463
|
-
* for the link on a laptop and open it on a phone.
|
|
464
|
-
*/
|
|
465
|
-
declare function confirmPasswordResetService(params: ConfirmPasswordResetParams): Promise<ConfirmPasswordResetResult>;
|
|
466
|
-
interface CompletePasswordResetParams {
|
|
467
|
-
setupSecret?: string;
|
|
468
|
-
password: string;
|
|
469
|
-
publicKey: string;
|
|
470
|
-
keyId: string;
|
|
471
|
-
fingerprint: string;
|
|
472
|
-
algorithm?: KeyAlgorithmType;
|
|
473
|
-
deviceName?: string;
|
|
474
|
-
platform?: KeyPlatformType;
|
|
475
|
-
/** Client address of the request, from `deviceProvenance` at the route. */
|
|
476
|
-
ip?: string;
|
|
477
|
-
/** `user-agent` of the request, already truncated at the route. */
|
|
478
|
-
userAgent?: string;
|
|
479
|
-
}
|
|
480
|
-
/**
|
|
481
|
-
* Step 3 — set the new password, which is what completes the reset.
|
|
482
|
-
*
|
|
483
|
-
* Run under `Transactional()`: the password, the revocations, the new device key
|
|
484
|
-
* and the completion mark commit together. A key registration that failed after
|
|
485
|
-
* the revoke-all would otherwise leave an account with a new password and
|
|
486
|
-
* nothing signed in.
|
|
487
|
-
*
|
|
488
|
-
* A refusal that is the user's to fix — a password the policy rejects, a body
|
|
489
|
-
* with no device key — leaves the setup session usable, so the fix is retyping
|
|
490
|
-
* the password rather than asking for a fresh email.
|
|
491
|
-
*/
|
|
492
|
-
declare function completePasswordResetService(params: CompletePasswordResetParams): Promise<RegisterResult>;
|
|
493
|
-
|
|
494
|
-
/**
|
|
495
|
-
* @spfn/auth - Device Auth Service
|
|
496
|
-
*
|
|
497
|
-
* Device-code login: a device with no key on file yet shows a short code, the
|
|
498
|
-
* account owner types that code on a device that is already signed in, and the
|
|
499
|
-
* waiting device's key is registered on approval.
|
|
500
|
-
*
|
|
501
|
-
* There is no token to hand over. Every request in this system is signed by the
|
|
502
|
-
* calling device's own key, so "logging a device in" means one thing — getting
|
|
503
|
-
* its public key into `user_public_keys` under the right account. That is what
|
|
504
|
-
* the poll does, and it is why the poll returns exactly what `loginService`
|
|
505
|
-
* returns: from the client's side the two ways in are indistinguishable.
|
|
506
|
-
*
|
|
507
|
-
* | state ↓ op → | info | approve | deny | poll |
|
|
508
|
-
* | --- | --- | --- | --- | --- |
|
|
509
|
-
* | pending | device details | → approved | → denied | pending |
|
|
510
|
-
* | approved | AlreadyHandled | AlreadyHandled | AlreadyHandled | key registered, → consumed |
|
|
511
|
-
* | denied | AlreadyHandled | AlreadyHandled | AlreadyHandled | Denied |
|
|
512
|
-
* | consumed | NotFound | NotFound | NotFound | NotFound |
|
|
513
|
-
* | expired | Expired | Expired | Expired | Expired |
|
|
514
|
-
* | unknown | NotFound | NotFound | NotFound | NotFound |
|
|
515
|
-
*
|
|
516
|
-
* A global revocation — revoke-all, a password change, a deletion request —
|
|
517
|
-
* refuses the account's live records too, as `denied`, so they land in that row
|
|
518
|
-
* of the table. See `denyAllActiveByUserId`; the three callers are the three
|
|
519
|
-
* places that revoke every key at once.
|
|
520
|
-
*/
|
|
521
|
-
|
|
522
|
-
interface StartDeviceAuthParams {
|
|
523
|
-
publicKey: string;
|
|
524
|
-
keyId: string;
|
|
525
|
-
fingerprint: string;
|
|
526
|
-
algorithm?: KeyAlgorithmType;
|
|
527
|
-
/** Device label shown to the approver. Display only — nothing is authorized by it. */
|
|
528
|
-
deviceName?: string;
|
|
529
|
-
platform?: KeyPlatformType;
|
|
530
|
-
}
|
|
531
|
-
interface StartDeviceAuthResult {
|
|
532
|
-
/** Returned once. The waiting device polls with it; the server stores only its hash. */
|
|
533
|
-
deviceCode: string;
|
|
534
|
-
/** `XXXX-XXXX`, for the waiting device's screen and nowhere else. */
|
|
535
|
-
userCode: string;
|
|
536
|
-
expiresAtMillis: number;
|
|
537
|
-
/** Milliseconds the waiting device should wait between polls. */
|
|
538
|
-
intervalMillis: number;
|
|
539
|
-
}
|
|
540
|
-
interface DeviceAuthInfoParams {
|
|
541
|
-
userCode: string;
|
|
542
|
-
}
|
|
543
|
-
/** What the approver is shown about the device asking to be let in. */
|
|
544
|
-
interface DeviceAuthInfoResult {
|
|
545
|
-
deviceName?: string;
|
|
546
|
-
/** One of `KEY_PLATFORM`, which is what the route accepts and the column stores. */
|
|
547
|
-
platform?: KeyPlatformType;
|
|
548
|
-
/** First bytes of the pending key's fingerprint, as the device list truncates it. */
|
|
549
|
-
fingerprintPrefix: string;
|
|
550
|
-
requestedAtMillis: number;
|
|
551
|
-
expiresAtMillis: number;
|
|
552
|
-
}
|
|
553
|
-
interface ApproveDeviceAuthParams {
|
|
554
|
-
userCode: string;
|
|
555
|
-
/** The approver, read from their session. Never from a request body. */
|
|
556
|
-
userId: number;
|
|
557
|
-
}
|
|
558
|
-
interface DenyDeviceAuthParams {
|
|
559
|
-
userCode: string;
|
|
560
|
-
}
|
|
561
|
-
interface PollDeviceAuthParams {
|
|
562
|
-
deviceCode: string;
|
|
563
|
-
/** Client address of the request, from `deviceProvenance` at the route. */
|
|
564
|
-
ip?: string;
|
|
565
|
-
/** `user-agent` of the request, already truncated at the route. */
|
|
566
|
-
userAgent?: string;
|
|
567
|
-
}
|
|
568
|
-
/** Nobody has answered yet. Not an error — the waiting device waits. */
|
|
569
|
-
interface DeviceAuthPendingResult {
|
|
570
|
-
status: 'pending';
|
|
571
|
-
intervalMillis: number;
|
|
572
|
-
}
|
|
573
|
-
/** Approved and spent: the key is registered and this is the login it produced. */
|
|
574
|
-
type DeviceAuthApprovedResult = {
|
|
575
|
-
status: 'approved';
|
|
576
|
-
} & LoginResult;
|
|
577
|
-
type PollDeviceAuthResult = DeviceAuthPendingResult | DeviceAuthApprovedResult;
|
|
578
|
-
/**
|
|
579
|
-
* Park a new device's key and hand back the codes it needs.
|
|
580
|
-
*
|
|
581
|
-
* The caller is unauthenticated by definition — this is what a device does before
|
|
582
|
-
* it has any way to prove anything — so nothing here is attributed to an account.
|
|
583
|
-
* The record gains an owner only when someone approves it.
|
|
584
|
-
*/
|
|
585
|
-
declare function startDeviceAuthService(params: StartDeviceAuthParams): Promise<StartDeviceAuthResult>;
|
|
586
|
-
/**
|
|
587
|
-
* What the approver sees before deciding.
|
|
588
|
-
*
|
|
589
|
-
* This is the whole defence against being talked into approving someone else's
|
|
590
|
-
* device: the answer names the device that is waiting, so the person holding the
|
|
591
|
-
* phone can see that it is not theirs. An approval screen that showed only the
|
|
592
|
-
* code would be asking them to confirm a number they were just told.
|
|
593
|
-
*/
|
|
594
|
-
declare function getDeviceAuthInfoService(params: DeviceAuthInfoParams): Promise<DeviceAuthInfoResult>;
|
|
595
|
-
/**
|
|
596
|
-
* Bind the record to the approving account.
|
|
597
|
-
*
|
|
598
|
-
* The key is not registered here. The waiting device may never come back, and a
|
|
599
|
-
* key registered for a device that stopped listening is a signing credential
|
|
600
|
-
* nobody asked for — so approval records the decision and the poll acts on it.
|
|
601
|
-
*
|
|
602
|
-
* Answers with the same device description `info` returns, so a client that let
|
|
603
|
-
* a user approve without looking first can still show them what they just let
|
|
604
|
-
* in — which is the moment someone talked into approving an attacker's device
|
|
605
|
-
* has to notice and revoke it.
|
|
606
|
-
*/
|
|
607
|
-
declare function approveDeviceAuthService(params: ApproveDeviceAuthParams): Promise<DeviceAuthInfoResult>;
|
|
608
|
-
/**
|
|
609
|
-
* Refuse the record, so the waiting device is told no instead of timing out.
|
|
610
|
-
*
|
|
611
|
-
* Denying binds no user: the point of refusing is that the account owner wants
|
|
612
|
-
* nothing to do with the request.
|
|
613
|
-
*/
|
|
614
|
-
declare function denyDeviceAuthService(params: DenyDeviceAuthParams): Promise<void>;
|
|
615
|
-
/**
|
|
616
|
-
* The waiting device asking whether anyone has answered.
|
|
617
|
-
*
|
|
618
|
-
* Approved is the one branch with a side effect, and it is a one-shot: the record
|
|
619
|
-
* is spent by a conditional update that names `approved`, so of two polls that
|
|
620
|
-
* arrive together exactly one registers the key. The loser matches nothing and is
|
|
621
|
-
* answered as if the code were unknown — which by then it is.
|
|
622
|
-
*/
|
|
623
|
-
declare function pollDeviceAuthService(params: PollDeviceAuthParams): Promise<PollDeviceAuthResult>;
|
|
624
|
-
|
|
625
|
-
/**
|
|
626
|
-
* @spfn/auth - Passkeys Entity
|
|
627
|
-
*
|
|
628
|
-
* A WebAuthn credential the account owner enrolled on one of their devices.
|
|
629
|
-
* It is a *credential*, not a session: an assertion proves who is asking, and
|
|
630
|
-
* the ordinary device key in `user_public_keys` is what the request afterwards
|
|
631
|
-
* is signed with. The two tables therefore never stand in for each other.
|
|
632
|
-
*
|
|
633
|
-
* Nothing here is a bearer value, so nothing is hashed. `publicKey` is public by
|
|
634
|
-
* construction and `credentialId` is a handle the authenticator hands to any
|
|
635
|
-
* origin that asks — storing either in the clear costs nothing, and the lookup
|
|
636
|
-
* on `credentialId` has to be a plain equality match on an indexed column.
|
|
637
|
-
*
|
|
638
|
-
* Revocation is soft, and `credentialId` stays unique across live and revoked
|
|
639
|
-
* rows alike: a credential someone cut off must never become enrollable again,
|
|
640
|
-
* on this account or on another one.
|
|
641
|
-
*/
|
|
642
|
-
/**
|
|
643
|
-
* Whether the credential can leave the authenticator that minted it.
|
|
644
|
-
*
|
|
645
|
-
* `multiDevice` is a synced passkey (iCloud Keychain, Google Password Manager);
|
|
646
|
-
* `singleDevice` is bound to one authenticator. Reported by the authenticator at
|
|
647
|
-
* enrollment and shown in the management list, because "this one is only on that
|
|
648
|
-
* phone" is what the owner needs to know before revoking the other entry.
|
|
113
|
+
* `multiDevice` is a synced passkey (iCloud Keychain, Google Password Manager);
|
|
114
|
+
* `singleDevice` is bound to one authenticator. Reported by the authenticator at
|
|
115
|
+
* enrollment and shown in the management list, because "this one is only on that
|
|
116
|
+
* phone" is what the owner needs to know before revoking the other entry.
|
|
649
117
|
*/
|
|
650
118
|
declare const PASSKEY_DEVICE_TYPES: readonly ["singleDevice", "multiDevice"];
|
|
651
119
|
type PasskeyDeviceType = typeof PASSKEY_DEVICE_TYPES[number];
|
|
@@ -898,8 +366,677 @@ declare const passkeys: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
898
366
|
};
|
|
899
367
|
dialect: "pg";
|
|
900
368
|
}>;
|
|
901
|
-
type Passkey = typeof passkeys.$inferSelect;
|
|
902
|
-
type NewPasskey = typeof passkeys.$inferInsert;
|
|
369
|
+
type Passkey = typeof passkeys.$inferSelect;
|
|
370
|
+
type NewPasskey = typeof passkeys.$inferInsert;
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* @spfn/auth - Auth Service
|
|
374
|
+
*
|
|
375
|
+
* Core authentication logic: registration, login, logout, password management
|
|
376
|
+
*/
|
|
377
|
+
|
|
378
|
+
interface RegisterParams {
|
|
379
|
+
email?: string;
|
|
380
|
+
phone?: string;
|
|
381
|
+
verificationToken: string;
|
|
382
|
+
password: string;
|
|
383
|
+
publicKey: string;
|
|
384
|
+
keyId: string;
|
|
385
|
+
fingerprint: string;
|
|
386
|
+
algorithm?: KeyAlgorithmType;
|
|
387
|
+
deviceName?: string;
|
|
388
|
+
platform?: KeyPlatformType;
|
|
389
|
+
metadata?: Record<string, unknown>;
|
|
390
|
+
/** Client address of the request, from `deviceProvenance` at the route. */
|
|
391
|
+
ip?: string;
|
|
392
|
+
/** `user-agent` of the request, already truncated at the route. */
|
|
393
|
+
userAgent?: string;
|
|
394
|
+
/**
|
|
395
|
+
* Whether proxy-guard recognised the trusted Next.js proxy, from the same
|
|
396
|
+
* helper. Carried for shape rather than effect: a brand-new account is on the
|
|
397
|
+
* default `session_binding: 'none'`, so its first key is never bound.
|
|
398
|
+
*/
|
|
399
|
+
webProxy?: boolean;
|
|
400
|
+
}
|
|
401
|
+
interface RegisterResult {
|
|
402
|
+
userId: string;
|
|
403
|
+
publicId: string;
|
|
404
|
+
email?: string;
|
|
405
|
+
phone?: string;
|
|
406
|
+
}
|
|
407
|
+
interface LoginParams {
|
|
408
|
+
email?: string;
|
|
409
|
+
phone?: string;
|
|
410
|
+
password: string;
|
|
411
|
+
publicKey: string;
|
|
412
|
+
keyId: string;
|
|
413
|
+
fingerprint: string;
|
|
414
|
+
oldKeyId?: string;
|
|
415
|
+
algorithm?: KeyAlgorithmType;
|
|
416
|
+
deviceName?: string;
|
|
417
|
+
platform?: KeyPlatformType;
|
|
418
|
+
/** Client address of the request, from `deviceProvenance` at the route. */
|
|
419
|
+
ip?: string;
|
|
420
|
+
/** `user-agent` of the request, already truncated at the route. */
|
|
421
|
+
userAgent?: string;
|
|
422
|
+
/** Whether proxy-guard recognised the trusted Next.js proxy, from the same helper. */
|
|
423
|
+
webProxy?: boolean;
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* What a sign-in answers with, on every path that starts a session.
|
|
427
|
+
*
|
|
428
|
+
* The last two fields are the carrier #97 needed. The Next.js proxy generated
|
|
429
|
+
* the device key and sealed the cookie, but only the backend knows whether the
|
|
430
|
+
* account asked for a bound session and when the key it just registered runs
|
|
431
|
+
* out — so the sign-in says it here and the interceptor copies both into
|
|
432
|
+
* `SessionData`. A response without them seals an unbound session, which is what
|
|
433
|
+
* every account that did not opt in gets and what every path predating this
|
|
434
|
+
* change keeps getting.
|
|
435
|
+
*/
|
|
436
|
+
interface LoginResult {
|
|
437
|
+
userId: string;
|
|
438
|
+
publicId: string;
|
|
439
|
+
email?: string;
|
|
440
|
+
phone?: string;
|
|
441
|
+
passwordChangeRequired: boolean;
|
|
442
|
+
/** `'passkey'` when the key registered by this sign-in is bound. Absent otherwise. */
|
|
443
|
+
sessionBinding?: SessionBindingType;
|
|
444
|
+
/** Epoch milliseconds that key expires at. Only sent alongside `sessionBinding`. */
|
|
445
|
+
keyExpiresAtMillis?: number;
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* The binding half of a sign-in answer, as a type.
|
|
449
|
+
*
|
|
450
|
+
* Named because more than one result carries it: a password reset registers a
|
|
451
|
+
* device key exactly as a sign-in does, so its answer has to say so too or the
|
|
452
|
+
* proxy seals a cookie that does not know the key it holds is short-lived.
|
|
453
|
+
*/
|
|
454
|
+
type LoginBindingFields = Pick<LoginResult, 'sessionBinding' | 'keyExpiresAtMillis'>;
|
|
455
|
+
interface LogoutParams {
|
|
456
|
+
userId: number;
|
|
457
|
+
keyId: string;
|
|
458
|
+
}
|
|
459
|
+
interface ChangePasswordParams {
|
|
460
|
+
userId: number;
|
|
461
|
+
/**
|
|
462
|
+
* The device key this request is signed with.
|
|
463
|
+
*
|
|
464
|
+
* Only read to measure the second-factor window of an enrolled account —
|
|
465
|
+
* an account with nothing enrolled is answered exactly as before, so this
|
|
466
|
+
* adds no refusal for anybody who has not opted in.
|
|
467
|
+
*/
|
|
468
|
+
keyId: string;
|
|
469
|
+
currentPassword?: string;
|
|
470
|
+
newPassword: string;
|
|
471
|
+
passwordHash?: string;
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Register a new user account
|
|
475
|
+
*/
|
|
476
|
+
declare function registerService(params: RegisterParams): Promise<RegisterResult>;
|
|
477
|
+
/**
|
|
478
|
+
* Authenticate user and create session
|
|
479
|
+
*/
|
|
480
|
+
declare function loginService(params: LoginParams): Promise<LoginResult>;
|
|
481
|
+
/**
|
|
482
|
+
* Logout user (revoke current key)
|
|
483
|
+
*/
|
|
484
|
+
declare function logoutService(params: LogoutParams): Promise<void>;
|
|
485
|
+
/**
|
|
486
|
+
* Change user password
|
|
487
|
+
*
|
|
488
|
+
* An enrolled account steps up first (#95): a stolen session must not be able
|
|
489
|
+
* to take the account over by setting a new password. An unenrolled account is
|
|
490
|
+
* unaffected — including the OAuth-only account with no password and a key
|
|
491
|
+
* older than ten minutes, which still sets a first password and gets a 200.
|
|
492
|
+
*/
|
|
493
|
+
declare function changePasswordService(params: ChangePasswordParams): Promise<void>;
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* @spfn/auth - Session Renewal Service
|
|
497
|
+
*
|
|
498
|
+
* What a bound session does when its key runs out: prove, with a fresh WebAuthn
|
|
499
|
+
* assertion, that the person who enrolled the passkey is still at the machine,
|
|
500
|
+
* and get a new short-lived key sealed into the cookie.
|
|
501
|
+
*
|
|
502
|
+
* Neither step is public. The expiring key is named by `expiredKeyId`, and that
|
|
503
|
+
* value reaches the service from `authenticateForRenewal` — the `keyId` of a
|
|
504
|
+
* bearer JWT this very key signed — rather than from the request body, so a
|
|
505
|
+
* caller who does not hold the private half cannot name a key at all. The
|
|
506
|
+
* assertion still has to be signed by a passkey that key's owner enrolled: the
|
|
507
|
+
* signature proves the cookie, and the cookie is the thing that may have been
|
|
508
|
+
* copied.
|
|
509
|
+
*
|
|
510
|
+
* The admission below is run again here all the same. The middleware and the
|
|
511
|
+
* service ask the same four questions of the row, and a service that trusted its
|
|
512
|
+
* caller to have asked them would be one refactor away from not being asked at
|
|
513
|
+
* all.
|
|
514
|
+
*
|
|
515
|
+
* Every refusal is the same refusal. A key that never existed, a stranger's key,
|
|
516
|
+
* an unbound key, a revoked one, one past its grace, an inactive account, a spent
|
|
517
|
+
* challenge, an assertion that did not verify — all `SessionRenewalRefusedError`,
|
|
518
|
+
* with the same body, because anything finer would answer "is this key id live"
|
|
519
|
+
* to whoever asked.
|
|
520
|
+
*
|
|
521
|
+
* Renewal announces nothing. No `auth.login`, no `auth.device.registered`, and
|
|
522
|
+
* `lastLoginAt` does not move: this is the same person on the same device
|
|
523
|
+
* continuing the session they already had, and a subscriber mailing "new sign-in"
|
|
524
|
+
* once a day per device would train its reader to ignore the notice that matters.
|
|
525
|
+
* A `lastLoginAt` that moved every day would make dormant-account detection
|
|
526
|
+
* meaningless for exactly the accounts that turned this protection on.
|
|
527
|
+
*/
|
|
528
|
+
|
|
529
|
+
interface StartSessionRenewParams {
|
|
530
|
+
/** The key that ran out, read off the JWT the request was signed with. */
|
|
531
|
+
expiredKeyId: string;
|
|
532
|
+
}
|
|
533
|
+
interface FinishSessionRenewParams extends StartSessionRenewParams {
|
|
534
|
+
/** The assertion, from `navigator.credentials.get()`. */
|
|
535
|
+
response: AuthenticationResponseJSON;
|
|
536
|
+
/**
|
|
537
|
+
* The new key pair, in the vocabulary the Next.js login interceptor already
|
|
538
|
+
* writes: `renew/verify` is on that interceptor's path list, so these arrive
|
|
539
|
+
* exactly as they do on a login.
|
|
540
|
+
*/
|
|
541
|
+
keyId: string;
|
|
542
|
+
publicKey: string;
|
|
543
|
+
fingerprint: string;
|
|
544
|
+
algorithm?: KeyAlgorithmType;
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* What a completed renewal answers: a sign-in result, plus the new key's id.
|
|
548
|
+
*
|
|
549
|
+
* The id is the one thing a renewal has that a sign-in does not need to say —
|
|
550
|
+
* `renewSession()` promises it to the app, which has no other way to learn it
|
|
551
|
+
* (the key pair is minted in the proxy and the private half never leaves the
|
|
552
|
+
* cookie). It is not a contract operation, so nothing generated reads it.
|
|
553
|
+
*/
|
|
554
|
+
interface SessionRenewResult extends LoginResult {
|
|
555
|
+
/** The key this renewal registered, the one the session now signs with. */
|
|
556
|
+
keyId: string;
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* Step 1 — the challenge the authenticator signs.
|
|
560
|
+
*
|
|
561
|
+
* `allowCredentials` is empty and the account lives only on the challenge row.
|
|
562
|
+
* See `startRenewalCeremonyService`.
|
|
563
|
+
*
|
|
564
|
+
* @throws SessionRenewalRefusedError 갱신할 수 없는 키·계정일 때 (모든 사유 동일)
|
|
565
|
+
*/
|
|
566
|
+
declare function startSessionRenewService(params: StartSessionRenewParams): Promise<PublicKeyCredentialRequestOptionsJSON>;
|
|
567
|
+
/**
|
|
568
|
+
* Step 2 — verify the assertion, put a new bound key in place of the old one.
|
|
569
|
+
*
|
|
570
|
+
* The revocation runs first and its answer is the race winner: two verifies that
|
|
571
|
+
* both got past their own challenges meet at the same conditional UPDATE, and
|
|
572
|
+
* only the one that actually revoked the key goes on to register a replacement.
|
|
573
|
+
*
|
|
574
|
+
* The new key inherits the old row's provenance, so the device list keeps saying
|
|
575
|
+
* where this device first appeared rather than re-stamping itself every day. Its
|
|
576
|
+
* expiry is a fresh window from now — renewal is a renewal, not an extension of
|
|
577
|
+
* what the old key had.
|
|
578
|
+
*
|
|
579
|
+
* @throws SessionRenewalRefusedError 갱신할 수 없을 때 (증명 실패 포함, 모든 사유 동일)
|
|
580
|
+
*/
|
|
581
|
+
declare function finishSessionRenewService(params: FinishSessionRenewParams): Promise<SessionRenewResult>;
|
|
582
|
+
|
|
583
|
+
declare const EmailSchema: _sinclair_typebox.TString;
|
|
584
|
+
declare const PhoneSchema: _sinclair_typebox.TString;
|
|
585
|
+
/**
|
|
586
|
+
* Optional device labels a client may send when registering a key.
|
|
587
|
+
*
|
|
588
|
+
* Display only: the key list uses them to tell one device from another, and
|
|
589
|
+
* nothing is authorized or refused by either value, so a client that lies about
|
|
590
|
+
* them gains nothing. Both are omitted by every key registered before they
|
|
591
|
+
* existed, hence optional rather than defaulted.
|
|
592
|
+
*/
|
|
593
|
+
declare const DeviceNameSchema: _sinclair_typebox.TString;
|
|
594
|
+
declare const PlatformSchema: _sinclair_typebox.TUnion<_sinclair_typebox.TLiteral<"ios" | "android" | "web" | "desktop">[]>;
|
|
595
|
+
/**
|
|
596
|
+
* Key material as a device sends it, bounded.
|
|
597
|
+
*
|
|
598
|
+
* The bounds exist for the one route that takes this material from a caller who
|
|
599
|
+
* has not authenticated and cannot: `POST /_auth/device/start` persists what it
|
|
600
|
+
* is given, and a correctly fingerprinted megabyte of base64 would sit in
|
|
601
|
+
* `device_authorizations` until something swept it — and nothing sweeps it.
|
|
602
|
+
*
|
|
603
|
+
* The numbers are what real key material measures, with room to spare. The
|
|
604
|
+
* package's own generators produce SPKI DER in base64: 124 characters for
|
|
605
|
+
* ES256 (P-256), 392 for RS256 (RSA-2048). An RSA-4096 key would be 736, an
|
|
606
|
+
* RSA-8192 key about 1400, and the same 4096-bit key PEM-armoured about 800 —
|
|
607
|
+
* so 2048 admits every shape of key anyone could reasonably present, while a
|
|
608
|
+
* megabyte is refused three orders of magnitude before it reaches a row.
|
|
609
|
+
*
|
|
610
|
+
* `keyId` is a UUID (36) everywhere this package generates one; 64 leaves room
|
|
611
|
+
* for a client that prefixes or namespaces its own. `fingerprint` is SHA-256
|
|
612
|
+
* hex, exactly 64, and nothing else can ever verify against the public key —
|
|
613
|
+
* 128 is the length a longer digest would need, and no more.
|
|
614
|
+
*/
|
|
615
|
+
declare const PublicKeySchema: _sinclair_typebox.TString;
|
|
616
|
+
declare const KeyIdSchema: _sinclair_typebox.TString;
|
|
617
|
+
declare const FingerprintSchema: _sinclair_typebox.TString;
|
|
618
|
+
/**
|
|
619
|
+
* The code a person reads off the waiting device and types on their own.
|
|
620
|
+
*
|
|
621
|
+
* Loose on purpose: 8 characters plus an optional dash is what is shown, but the
|
|
622
|
+
* server folds whitespace, dashes and lower case away before looking anything up,
|
|
623
|
+
* so refusing those spellings here would refuse a code that is on screen. The
|
|
624
|
+
* bounds exist to stop an unbounded string reaching the database, not to spell
|
|
625
|
+
* out the format — `USER_CODE_ALPHABET` is the only thing that can match a row.
|
|
626
|
+
*/
|
|
627
|
+
declare const UserCodeSchema: _sinclair_typebox.TString;
|
|
628
|
+
/**
|
|
629
|
+
* What `POST /_auth/device/poll` answers with.
|
|
630
|
+
*
|
|
631
|
+
* A union, because the two answers are different kinds of thing rather than one
|
|
632
|
+
* shape with optional fields: pending says "ask again in this long", approved is
|
|
633
|
+
* a completed login carrying exactly what `/_auth/login` returns. `status` is the
|
|
634
|
+
* discriminant, so a generated client narrows on it instead of testing which
|
|
635
|
+
* fields happen to be present.
|
|
636
|
+
*
|
|
637
|
+
* The mobile contract has no union type, so it exports this as one object with
|
|
638
|
+
* `status` required and every branch field optional — see
|
|
639
|
+
* `deviceAuthorization.pollStatusRule` in the bundle. `intervalMillis` is an
|
|
640
|
+
* integer for the same reason: that grammar carries no floating-point scalar,
|
|
641
|
+
* and a count of milliseconds never needed one.
|
|
642
|
+
*
|
|
643
|
+
* That integer is a promise two things keep, because nothing validates a response
|
|
644
|
+
* against this schema on the way out. `configureDeviceAuth` refuses an interval
|
|
645
|
+
* that is not a whole number of milliseconds, so the only value this branch can
|
|
646
|
+
* carry is one; and `contract-export.test.ts` reads this schema to check the
|
|
647
|
+
* exported declaration, so writing `Type.Number` here fails the suite instead of
|
|
648
|
+
* publishing an integer the server does not send.
|
|
649
|
+
*/
|
|
650
|
+
declare const DeviceAuthPollResponseSchema: _sinclair_typebox.TUnion<[_sinclair_typebox.TObject<{
|
|
651
|
+
status: _sinclair_typebox.TLiteral<"pending">;
|
|
652
|
+
intervalMillis: _sinclair_typebox.TInteger;
|
|
653
|
+
}>, _sinclair_typebox.TObject<{
|
|
654
|
+
status: _sinclair_typebox.TLiteral<"approved">;
|
|
655
|
+
userId: _sinclair_typebox.TString;
|
|
656
|
+
publicId: _sinclair_typebox.TString;
|
|
657
|
+
email: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
658
|
+
phone: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
659
|
+
passwordChangeRequired: _sinclair_typebox.TBoolean;
|
|
660
|
+
sessionBinding: _sinclair_typebox.TOptional<_sinclair_typebox.TUnion<_sinclair_typebox.TLiteral<"none" | "passkey">[]>>;
|
|
661
|
+
keyExpiresAtMillis: _sinclair_typebox.TOptional<_sinclair_typebox.TInteger>;
|
|
662
|
+
}>]>;
|
|
663
|
+
declare const PasswordSchema: _sinclair_typebox.TString;
|
|
664
|
+
declare const TargetTypeSchema: _sinclair_typebox.TUnion<[_sinclair_typebox.TLiteral<"email">, _sinclair_typebox.TLiteral<"phone">]>;
|
|
665
|
+
type VerificationTargetType = Static<typeof TargetTypeSchema>;
|
|
666
|
+
declare const VERIFICATION_TARGET_TYPES: readonly ["email", "phone"];
|
|
667
|
+
declare const VerificationPurposeSchema: _sinclair_typebox.TUnion<[_sinclair_typebox.TLiteral<"registration">, _sinclair_typebox.TLiteral<"login">, _sinclair_typebox.TLiteral<"password_reset">, _sinclair_typebox.TLiteral<"email_change">, _sinclair_typebox.TLiteral<"phone_change">, _sinclair_typebox.TLiteral<"account_deletion">]>;
|
|
668
|
+
type VerificationPurpose = Static<typeof VerificationPurposeSchema>;
|
|
669
|
+
declare const VERIFICATION_PURPOSES: readonly ["registration", "login", "password_reset", "email_change", "phone_change", "account_deletion"];
|
|
670
|
+
|
|
671
|
+
/**
|
|
672
|
+
* @spfn/auth - Verification Service
|
|
673
|
+
*
|
|
674
|
+
* Handles OTP code generation, validation, and delivery
|
|
675
|
+
*/
|
|
676
|
+
|
|
677
|
+
interface SendVerificationCodeParams {
|
|
678
|
+
target: string;
|
|
679
|
+
targetType: VerificationTargetType;
|
|
680
|
+
purpose: VerificationPurpose;
|
|
681
|
+
}
|
|
682
|
+
interface SendVerificationCodeResult {
|
|
683
|
+
success: boolean;
|
|
684
|
+
expiresAt: string;
|
|
685
|
+
}
|
|
686
|
+
interface VerifyCodeParams {
|
|
687
|
+
target: string;
|
|
688
|
+
targetType: VerificationTargetType;
|
|
689
|
+
code: string;
|
|
690
|
+
purpose: VerificationPurpose;
|
|
691
|
+
}
|
|
692
|
+
interface VerifyCodeResult {
|
|
693
|
+
valid: boolean;
|
|
694
|
+
verificationToken: string;
|
|
695
|
+
}
|
|
696
|
+
/**
|
|
697
|
+
* Send verification code via email or SMS
|
|
698
|
+
*/
|
|
699
|
+
declare function sendVerificationCodeService(params: SendVerificationCodeParams): Promise<SendVerificationCodeResult>;
|
|
700
|
+
/**
|
|
701
|
+
* Verify OTP code and return verification token
|
|
702
|
+
*/
|
|
703
|
+
declare function verifyCodeService(params: VerifyCodeParams): Promise<{
|
|
704
|
+
valid: boolean;
|
|
705
|
+
verificationToken: string;
|
|
706
|
+
}>;
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* @spfn/auth - Verified-Email Signup Service
|
|
710
|
+
*
|
|
711
|
+
* A signup where the address is proven before a password exists:
|
|
712
|
+
*
|
|
713
|
+
* request -> a one-time link is emailed
|
|
714
|
+
* confirm -> the link is exchanged for a short-lived password-setup session
|
|
715
|
+
* password -> the account is created, the device registered, the user signed in
|
|
716
|
+
*
|
|
717
|
+
* The link token and the setup secret are bearer credentials, so neither is ever
|
|
718
|
+
* stored. Only their SHA-256 hashes are, and lookup is by hash. A database dump
|
|
719
|
+
* therefore yields nothing that can be presented to either step.
|
|
720
|
+
*
|
|
721
|
+
* The six-digit-code registration path is untouched and remains the default; this
|
|
722
|
+
* is a second entry point to the same account creation, not a replacement.
|
|
723
|
+
*/
|
|
724
|
+
|
|
725
|
+
interface RequestSignupLinkParams {
|
|
726
|
+
email: string;
|
|
727
|
+
returnPath?: string;
|
|
728
|
+
}
|
|
729
|
+
interface RequestSignupLinkResult {
|
|
730
|
+
success: boolean;
|
|
731
|
+
expiresAt: string;
|
|
732
|
+
}
|
|
733
|
+
/**
|
|
734
|
+
* Step 1 — issue a confirmation link for an address.
|
|
735
|
+
*
|
|
736
|
+
* Answers identically whether or not the address already has an account. When it
|
|
737
|
+
* does, the owner gets a notice instead of a usable link, through the same
|
|
738
|
+
* dedupe window the six-digit-code path uses.
|
|
739
|
+
*
|
|
740
|
+
* Requesting again is how a resend works: every live link for the address is
|
|
741
|
+
* superseded first, so the newest link is the only one that opens, and any setup
|
|
742
|
+
* session already opened from an older link dies with it.
|
|
743
|
+
*
|
|
744
|
+
* Neither branch sends mail: both hand it to `auth.link-mail`, so the answer
|
|
745
|
+
* costs the same database work whichever one ran. With no pg-boss initialised
|
|
746
|
+
* the mail still goes out on this request — see `lib/link-mail-delivery.ts`.
|
|
747
|
+
*/
|
|
748
|
+
declare function requestSignupLinkService(params: RequestSignupLinkParams): Promise<RequestSignupLinkResult>;
|
|
749
|
+
interface ConfirmSignupLinkParams {
|
|
750
|
+
token: string;
|
|
751
|
+
}
|
|
752
|
+
interface ConfirmSignupLinkResult {
|
|
753
|
+
email: string;
|
|
754
|
+
returnPath: string | null;
|
|
755
|
+
/** Handed to the proxy interceptor, which moves it into an HttpOnly cookie. */
|
|
756
|
+
setupSecret: string;
|
|
757
|
+
setupExpiresAt: string;
|
|
758
|
+
}
|
|
759
|
+
/**
|
|
760
|
+
* Step 2 — exchange a link for a password-setup session.
|
|
761
|
+
*
|
|
762
|
+
* Nothing binds the row to a device or a browser, which is what lets someone
|
|
763
|
+
* request the link on a laptop and open it on a phone.
|
|
764
|
+
*/
|
|
765
|
+
declare function confirmSignupLinkService(params: ConfirmSignupLinkParams): Promise<ConfirmSignupLinkResult>;
|
|
766
|
+
interface CompleteSignupParams {
|
|
767
|
+
setupSecret?: string;
|
|
768
|
+
password: string;
|
|
769
|
+
publicKey: string;
|
|
770
|
+
keyId: string;
|
|
771
|
+
fingerprint: string;
|
|
772
|
+
algorithm?: KeyAlgorithmType;
|
|
773
|
+
deviceName?: string;
|
|
774
|
+
platform?: KeyPlatformType;
|
|
775
|
+
metadata?: Record<string, unknown>;
|
|
776
|
+
/** Client address of the request, from `deviceProvenance` at the route. */
|
|
777
|
+
ip?: string;
|
|
778
|
+
/** `user-agent` of the request, already truncated at the route. */
|
|
779
|
+
userAgent?: string;
|
|
780
|
+
}
|
|
781
|
+
/**
|
|
782
|
+
* Step 3 — set the password, which is what creates the account.
|
|
783
|
+
*
|
|
784
|
+
* Run under `Transactional()`: the user row, the device key and the completion
|
|
785
|
+
* mark commit together. A device-key failure must not leave an account nobody
|
|
786
|
+
* can sign into, and a completion mark must not survive a rolled-back account.
|
|
787
|
+
*
|
|
788
|
+
* A refusal that is the user's to fix — a weak password, an app policy that
|
|
789
|
+
* rejects the registration — leaves the setup session usable, so the fix is
|
|
790
|
+
* retyping the password rather than requesting a fresh email.
|
|
791
|
+
*/
|
|
792
|
+
declare function completeSignupService(params: CompleteSignupParams): Promise<RegisterResult>;
|
|
793
|
+
|
|
794
|
+
/**
|
|
795
|
+
* @spfn/auth - Password Reset Service
|
|
796
|
+
*
|
|
797
|
+
* Getting back into an account whose password is gone, using the address the
|
|
798
|
+
* account already proved:
|
|
799
|
+
*
|
|
800
|
+
* request -> a one-time link is emailed
|
|
801
|
+
* confirm -> the link is exchanged for a short-lived password-setup session
|
|
802
|
+
* complete -> the new password is written, everything else is signed out,
|
|
803
|
+
* and the browser that reset is signed in on a fresh device key
|
|
804
|
+
*
|
|
805
|
+
* Mirrors the verified-email signup slice deliberately — same credentials, same
|
|
806
|
+
* hashing, same supersede-on-resend, same interceptor moves — with two
|
|
807
|
+
* differences that matter.
|
|
808
|
+
*
|
|
809
|
+
* First, the request answers identically for *every* input and sends mail only
|
|
810
|
+
* to an account that can be reset. Signup can afford to tell an existing owner
|
|
811
|
+
* "you already have an account"; a reset cannot send anything to a stranger's
|
|
812
|
+
* mailbox, because the mail itself would be the answer to "does this address
|
|
813
|
+
* have an account here".
|
|
814
|
+
*
|
|
815
|
+
* Second, completing it is a credential change on a live account, so it carries
|
|
816
|
+
* the same blast radius as `changePasswordService`: pending device
|
|
817
|
+
* authorizations are denied and every active key is revoked. Whoever was signed
|
|
818
|
+
* in on the old password is signed out, including the attacker the reset was
|
|
819
|
+
* needed for.
|
|
820
|
+
*/
|
|
821
|
+
|
|
822
|
+
interface RequestPasswordResetParams {
|
|
823
|
+
email: string;
|
|
824
|
+
returnPath?: string;
|
|
825
|
+
}
|
|
826
|
+
interface RequestPasswordResetResult {
|
|
827
|
+
success: boolean;
|
|
828
|
+
expiresAt: string;
|
|
829
|
+
}
|
|
830
|
+
/**
|
|
831
|
+
* Step 1 — issue a reset link for an address.
|
|
832
|
+
*
|
|
833
|
+
* Answers identically for every input: the same status, the same two fields, and
|
|
834
|
+
* an `expiresAt` computed the same way whether or not a row was written. An
|
|
835
|
+
* address with no account, an account that cannot be reset, and an account that
|
|
836
|
+
* can are indistinguishable to the caller — only the first of the three gets
|
|
837
|
+
* mail, and it goes to the owner.
|
|
838
|
+
*
|
|
839
|
+
* Requesting again is how a resend works: every live link for the account is
|
|
840
|
+
* superseded first, so the newest link is the only one that opens, and any setup
|
|
841
|
+
* session already opened from an older link dies with it.
|
|
842
|
+
*
|
|
843
|
+
* The eligible branch does not send the mail either — it hands it to
|
|
844
|
+
* `auth.link-mail` — so the two branches differ by a few database writes and not
|
|
845
|
+
* by a mail provider's round trip. With no pg-boss initialised the mail still
|
|
846
|
+
* goes out on this request; see `lib/link-mail-delivery.ts`.
|
|
847
|
+
*/
|
|
848
|
+
declare function requestPasswordResetService(params: RequestPasswordResetParams): Promise<RequestPasswordResetResult>;
|
|
849
|
+
interface ConfirmPasswordResetParams {
|
|
850
|
+
token: string;
|
|
851
|
+
}
|
|
852
|
+
interface ConfirmPasswordResetResult {
|
|
853
|
+
email: string;
|
|
854
|
+
returnPath: string | null;
|
|
855
|
+
/** Handed to the proxy interceptor, which moves it into an HttpOnly cookie. */
|
|
856
|
+
setupSecret: string;
|
|
857
|
+
setupExpiresAt: string;
|
|
858
|
+
}
|
|
859
|
+
/**
|
|
860
|
+
* Step 2 — exchange a link for a password-setup session.
|
|
861
|
+
*
|
|
862
|
+
* Nothing binds the row to a device or a browser, which is what lets someone ask
|
|
863
|
+
* for the link on a laptop and open it on a phone.
|
|
864
|
+
*/
|
|
865
|
+
declare function confirmPasswordResetService(params: ConfirmPasswordResetParams): Promise<ConfirmPasswordResetResult>;
|
|
866
|
+
interface CompletePasswordResetParams {
|
|
867
|
+
setupSecret?: string;
|
|
868
|
+
password: string;
|
|
869
|
+
publicKey: string;
|
|
870
|
+
keyId: string;
|
|
871
|
+
fingerprint: string;
|
|
872
|
+
algorithm?: KeyAlgorithmType;
|
|
873
|
+
deviceName?: string;
|
|
874
|
+
platform?: KeyPlatformType;
|
|
875
|
+
/** Client address of the request, from `deviceProvenance` at the route. */
|
|
876
|
+
ip?: string;
|
|
877
|
+
/** `user-agent` of the request, already truncated at the route. */
|
|
878
|
+
userAgent?: string;
|
|
879
|
+
/** Whether proxy-guard recognised the trusted Next.js proxy, from the same helper. */
|
|
880
|
+
webProxy?: boolean;
|
|
881
|
+
}
|
|
882
|
+
/**
|
|
883
|
+
* Step 3 — set the new password, which is what completes the reset.
|
|
884
|
+
*
|
|
885
|
+
* Run under `Transactional()`: the password, the revocations, the new device key
|
|
886
|
+
* and the completion mark commit together. A key registration that failed after
|
|
887
|
+
* the revoke-all would otherwise leave an account with a new password and
|
|
888
|
+
* nothing signed in.
|
|
889
|
+
*
|
|
890
|
+
* A refusal that is the user's to fix — a password the policy rejects, a body
|
|
891
|
+
* with no device key — leaves the setup session usable, so the fix is retyping
|
|
892
|
+
* the password rather than asking for a fresh email.
|
|
893
|
+
*
|
|
894
|
+
* The answer carries the binding fields, like every other path that starts a
|
|
895
|
+
* session. A reset on an account that opted in registers a bound key — 24 hours,
|
|
896
|
+
* renewable only by a passkey — and this route is on the sealing interceptor's
|
|
897
|
+
* list, so a body that did not say so would have the proxy seal a cookie
|
|
898
|
+
* believing the session unbound: no user-agent check for the life of that key,
|
|
899
|
+
* and a sign-out a day later instead of the renewal prompt.
|
|
900
|
+
*/
|
|
901
|
+
declare function completePasswordResetService(params: CompletePasswordResetParams): Promise<RegisterResult & LoginBindingFields>;
|
|
902
|
+
|
|
903
|
+
/**
|
|
904
|
+
* @spfn/auth - Device Auth Service
|
|
905
|
+
*
|
|
906
|
+
* Device-code login: a device with no key on file yet shows a short code, the
|
|
907
|
+
* account owner types that code on a device that is already signed in, and the
|
|
908
|
+
* waiting device's key is registered on approval.
|
|
909
|
+
*
|
|
910
|
+
* There is no token to hand over. Every request in this system is signed by the
|
|
911
|
+
* calling device's own key, so "logging a device in" means one thing — getting
|
|
912
|
+
* its public key into `user_public_keys` under the right account. That is what
|
|
913
|
+
* the poll does, and it is why the poll returns exactly what `loginService`
|
|
914
|
+
* returns: from the client's side the two ways in are indistinguishable.
|
|
915
|
+
*
|
|
916
|
+
* | state ↓ op → | info | approve | deny | poll |
|
|
917
|
+
* | --- | --- | --- | --- | --- |
|
|
918
|
+
* | pending | device details | → approved | → denied | pending |
|
|
919
|
+
* | approved | AlreadyHandled | AlreadyHandled | AlreadyHandled | key registered, → consumed |
|
|
920
|
+
* | denied | AlreadyHandled | AlreadyHandled | AlreadyHandled | Denied |
|
|
921
|
+
* | consumed | NotFound | NotFound | NotFound | NotFound |
|
|
922
|
+
* | expired | Expired | Expired | Expired | Expired |
|
|
923
|
+
* | unknown | NotFound | NotFound | NotFound | NotFound |
|
|
924
|
+
*
|
|
925
|
+
* A global revocation — revoke-all, a password change, a deletion request —
|
|
926
|
+
* refuses the account's live records too, as `denied`, so they land in that row
|
|
927
|
+
* of the table. See `denyAllActiveByUserId`; the three callers are the three
|
|
928
|
+
* places that revoke every key at once.
|
|
929
|
+
*/
|
|
930
|
+
|
|
931
|
+
interface StartDeviceAuthParams {
|
|
932
|
+
publicKey: string;
|
|
933
|
+
keyId: string;
|
|
934
|
+
fingerprint: string;
|
|
935
|
+
algorithm?: KeyAlgorithmType;
|
|
936
|
+
/** Device label shown to the approver. Display only — nothing is authorized by it. */
|
|
937
|
+
deviceName?: string;
|
|
938
|
+
platform?: KeyPlatformType;
|
|
939
|
+
}
|
|
940
|
+
interface StartDeviceAuthResult {
|
|
941
|
+
/** Returned once. The waiting device polls with it; the server stores only its hash. */
|
|
942
|
+
deviceCode: string;
|
|
943
|
+
/** `XXXX-XXXX`, for the waiting device's screen and nowhere else. */
|
|
944
|
+
userCode: string;
|
|
945
|
+
expiresAtMillis: number;
|
|
946
|
+
/** Milliseconds the waiting device should wait between polls. */
|
|
947
|
+
intervalMillis: number;
|
|
948
|
+
}
|
|
949
|
+
interface DeviceAuthInfoParams {
|
|
950
|
+
userCode: string;
|
|
951
|
+
}
|
|
952
|
+
/** What the approver is shown about the device asking to be let in. */
|
|
953
|
+
interface DeviceAuthInfoResult {
|
|
954
|
+
deviceName?: string;
|
|
955
|
+
/** One of `KEY_PLATFORM`, which is what the route accepts and the column stores. */
|
|
956
|
+
platform?: KeyPlatformType;
|
|
957
|
+
/** First bytes of the pending key's fingerprint, as the device list truncates it. */
|
|
958
|
+
fingerprintPrefix: string;
|
|
959
|
+
requestedAtMillis: number;
|
|
960
|
+
expiresAtMillis: number;
|
|
961
|
+
}
|
|
962
|
+
interface ApproveDeviceAuthParams {
|
|
963
|
+
userCode: string;
|
|
964
|
+
/** The approver, read from their session. Never from a request body. */
|
|
965
|
+
userId: number;
|
|
966
|
+
}
|
|
967
|
+
interface DenyDeviceAuthParams {
|
|
968
|
+
userCode: string;
|
|
969
|
+
}
|
|
970
|
+
interface PollDeviceAuthParams {
|
|
971
|
+
deviceCode: string;
|
|
972
|
+
/** Client address of the request, from `deviceProvenance` at the route. */
|
|
973
|
+
ip?: string;
|
|
974
|
+
/** `user-agent` of the request, already truncated at the route. */
|
|
975
|
+
userAgent?: string;
|
|
976
|
+
/**
|
|
977
|
+
* Whether proxy-guard recognised the trusted Next.js proxy, from the same
|
|
978
|
+
* helper. A waiting device polls the backend itself, so this is false there
|
|
979
|
+
* and the key it collects is unbound — which is what a device with no browser
|
|
980
|
+
* to run a WebAuthn ceremony in needs.
|
|
981
|
+
*/
|
|
982
|
+
webProxy?: boolean;
|
|
983
|
+
}
|
|
984
|
+
/** Nobody has answered yet. Not an error — the waiting device waits. */
|
|
985
|
+
interface DeviceAuthPendingResult {
|
|
986
|
+
status: 'pending';
|
|
987
|
+
intervalMillis: number;
|
|
988
|
+
}
|
|
989
|
+
/** Approved and spent: the key is registered and this is the login it produced. */
|
|
990
|
+
type DeviceAuthApprovedResult = {
|
|
991
|
+
status: 'approved';
|
|
992
|
+
} & LoginResult;
|
|
993
|
+
type PollDeviceAuthResult = DeviceAuthPendingResult | DeviceAuthApprovedResult;
|
|
994
|
+
/**
|
|
995
|
+
* Park a new device's key and hand back the codes it needs.
|
|
996
|
+
*
|
|
997
|
+
* The caller is unauthenticated by definition — this is what a device does before
|
|
998
|
+
* it has any way to prove anything — so nothing here is attributed to an account.
|
|
999
|
+
* The record gains an owner only when someone approves it.
|
|
1000
|
+
*/
|
|
1001
|
+
declare function startDeviceAuthService(params: StartDeviceAuthParams): Promise<StartDeviceAuthResult>;
|
|
1002
|
+
/**
|
|
1003
|
+
* What the approver sees before deciding.
|
|
1004
|
+
*
|
|
1005
|
+
* This is the whole defence against being talked into approving someone else's
|
|
1006
|
+
* device: the answer names the device that is waiting, so the person holding the
|
|
1007
|
+
* phone can see that it is not theirs. An approval screen that showed only the
|
|
1008
|
+
* code would be asking them to confirm a number they were just told.
|
|
1009
|
+
*/
|
|
1010
|
+
declare function getDeviceAuthInfoService(params: DeviceAuthInfoParams): Promise<DeviceAuthInfoResult>;
|
|
1011
|
+
/**
|
|
1012
|
+
* Bind the record to the approving account.
|
|
1013
|
+
*
|
|
1014
|
+
* The key is not registered here. The waiting device may never come back, and a
|
|
1015
|
+
* key registered for a device that stopped listening is a signing credential
|
|
1016
|
+
* nobody asked for — so approval records the decision and the poll acts on it.
|
|
1017
|
+
*
|
|
1018
|
+
* Answers with the same device description `info` returns, so a client that let
|
|
1019
|
+
* a user approve without looking first can still show them what they just let
|
|
1020
|
+
* in — which is the moment someone talked into approving an attacker's device
|
|
1021
|
+
* has to notice and revoke it.
|
|
1022
|
+
*/
|
|
1023
|
+
declare function approveDeviceAuthService(params: ApproveDeviceAuthParams): Promise<DeviceAuthInfoResult>;
|
|
1024
|
+
/**
|
|
1025
|
+
* Refuse the record, so the waiting device is told no instead of timing out.
|
|
1026
|
+
*
|
|
1027
|
+
* Denying binds no user: the point of refusing is that the account owner wants
|
|
1028
|
+
* nothing to do with the request.
|
|
1029
|
+
*/
|
|
1030
|
+
declare function denyDeviceAuthService(params: DenyDeviceAuthParams): Promise<void>;
|
|
1031
|
+
/**
|
|
1032
|
+
* The waiting device asking whether anyone has answered.
|
|
1033
|
+
*
|
|
1034
|
+
* Approved is the one branch with a side effect, and it is a one-shot: the record
|
|
1035
|
+
* is spent by a conditional update that names `approved`, so of two polls that
|
|
1036
|
+
* arrive together exactly one registers the key. The loser matches nothing and is
|
|
1037
|
+
* answered as if the code were unknown — which by then it is.
|
|
1038
|
+
*/
|
|
1039
|
+
declare function pollDeviceAuthService(params: PollDeviceAuthParams): Promise<PollDeviceAuthResult>;
|
|
903
1040
|
|
|
904
1041
|
/**
|
|
905
1042
|
* @spfn/auth - Passkey Service
|
|
@@ -1033,6 +1170,8 @@ interface FinishPasskeyLoginParams {
|
|
|
1033
1170
|
ip?: string;
|
|
1034
1171
|
/** `user-agent` of the request, already truncated at the route. */
|
|
1035
1172
|
userAgent?: string;
|
|
1173
|
+
/** Whether proxy-guard recognised the trusted Next.js proxy, from the same helper. */
|
|
1174
|
+
webProxy?: boolean;
|
|
1036
1175
|
}
|
|
1037
1176
|
/**
|
|
1038
1177
|
* Step 2 of sign-in — verify the assertion, then sign in exactly as a password
|
|
@@ -1153,7 +1292,7 @@ declare const mfaVerifications: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
1153
1292
|
name: string;
|
|
1154
1293
|
tableName: "mfa_verifications";
|
|
1155
1294
|
dataType: "string enum";
|
|
1156
|
-
data: "
|
|
1295
|
+
data: "passkey" | "totp" | "recovery";
|
|
1157
1296
|
driverParam: string;
|
|
1158
1297
|
notNull: true;
|
|
1159
1298
|
hasDefault: false;
|
|
@@ -1434,7 +1573,7 @@ declare const authLoginEvent: _spfn_core_event.EventDef<{
|
|
|
1434
1573
|
email?: string | undefined;
|
|
1435
1574
|
phone?: string | undefined;
|
|
1436
1575
|
userId: string;
|
|
1437
|
-
provider: "email" | "phone" | "google" | "apple" | "github" | "kakao" | "naver" | "superself" | "
|
|
1576
|
+
provider: "email" | "phone" | "passkey" | "google" | "apple" | "github" | "kakao" | "naver" | "superself" | "device";
|
|
1438
1577
|
mfaEnrolled: boolean;
|
|
1439
1578
|
}>;
|
|
1440
1579
|
/**
|
|
@@ -1446,15 +1585,15 @@ declare const authLoginEvent: _spfn_core_event.EventDef<{
|
|
|
1446
1585
|
* both arrive at `createVerifiedAccount`, so the two name themselves there;
|
|
1447
1586
|
* `'invitation'` is the one path that stores a key without the key service.
|
|
1448
1587
|
*/
|
|
1449
|
-
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">]>;
|
|
1450
|
-
/** The
|
|
1588
|
+
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">]>;
|
|
1589
|
+
/** The ten doors a device key is registered through. */
|
|
1451
1590
|
type DeviceRegistrationChannel = Static<typeof DeviceRegistrationChannelSchema>;
|
|
1452
1591
|
/**
|
|
1453
1592
|
* auth.device.registered — a new device key was added to an account
|
|
1454
1593
|
*
|
|
1455
1594
|
* 발행 시점:
|
|
1456
1595
|
* - a key row was created for an account and the transaction that created it
|
|
1457
|
-
* committed, on every one of the
|
|
1596
|
+
* committed, on every one of the ten channels above
|
|
1458
1597
|
*
|
|
1459
1598
|
* This is the notice an account owner needs and could not get before: a stolen
|
|
1460
1599
|
* password used to sign in on a new device was silent, because a login event
|
|
@@ -1492,7 +1631,7 @@ declare const authDeviceRegisteredEvent: _spfn_core_event.EventDef<{
|
|
|
1492
1631
|
fingerprintPrefix: string;
|
|
1493
1632
|
createdAtMillis: number;
|
|
1494
1633
|
mfaEnrolled: boolean;
|
|
1495
|
-
channel: "password" | "register" | "oauth-native" | "
|
|
1634
|
+
channel: "password" | "register" | "passkey" | "oauth-native" | "renewal" | "oauth" | "invitation" | "signup-link" | "password-reset" | "device-code";
|
|
1496
1635
|
}>;
|
|
1497
1636
|
/**
|
|
1498
1637
|
* auth.register - 회원가입 성공 이벤트
|
|
@@ -1697,6 +1836,18 @@ interface RegisterPublicKeyParams {
|
|
|
1697
1836
|
ip?: string;
|
|
1698
1837
|
/** `user-agent` of the registering request, already truncated. */
|
|
1699
1838
|
userAgent?: string;
|
|
1839
|
+
/**
|
|
1840
|
+
* Whether this key is bound to a passkey, as the registering path decided.
|
|
1841
|
+
*
|
|
1842
|
+
* Decided by the caller from two facts it is the only one holding: the
|
|
1843
|
+
* owner's `session_binding` setting, and whether `proxy-guard` recognised the
|
|
1844
|
+
* request as coming through the trusted Next.js proxy (`decideKeyBinding`).
|
|
1845
|
+
* Never read off a request body, and `platform` is not consulted — that field
|
|
1846
|
+
* is display-only, usually absent on a proxy-made key, and a native client
|
|
1847
|
+
* may declare `'web'` freely. Omitted means `'none'`, which is what every
|
|
1848
|
+
* path that has no opinion gets.
|
|
1849
|
+
*/
|
|
1850
|
+
binding?: SessionBindingType;
|
|
1700
1851
|
/**
|
|
1701
1852
|
* The key this one replaces on the same device, when a revocation actually
|
|
1702
1853
|
* happened. Its presence is what makes this a rotation rather than a new
|
|
@@ -1785,6 +1936,28 @@ interface KeySummary {
|
|
|
1785
1936
|
registeredIp?: string;
|
|
1786
1937
|
/** `user-agent` of the registering request, on the same terms as above. */
|
|
1787
1938
|
registeredUserAgent?: string;
|
|
1939
|
+
/**
|
|
1940
|
+
* `'passkey'` when this key is bound to one, absent when it is not.
|
|
1941
|
+
*
|
|
1942
|
+
* Absent rather than `'none'`, so a deployment where nobody opted in answers
|
|
1943
|
+
* exactly the list it always did, and so every consumer reads "unbound" the
|
|
1944
|
+
* same way it reads it off a sign-in response.
|
|
1945
|
+
*/
|
|
1946
|
+
binding?: SessionBindingType;
|
|
1947
|
+
/**
|
|
1948
|
+
* When this key was last seen from two client addresses inside the
|
|
1949
|
+
* concurrent-use window, absent when that has never been observed.
|
|
1950
|
+
*
|
|
1951
|
+
* A signal for the owner, not a refusal: addresses change legitimately, so
|
|
1952
|
+
* nothing is blocked by it and a device list that shows it is telling someone
|
|
1953
|
+
* to look rather than telling them something happened. The addresses
|
|
1954
|
+
* themselves are never returned.
|
|
1955
|
+
*
|
|
1956
|
+
* Only meaningful where proxy-guard is configured. Without it every web
|
|
1957
|
+
* request carries the Next.js server's own address, so two browsers on two
|
|
1958
|
+
* continents share one and this never fires.
|
|
1959
|
+
*/
|
|
1960
|
+
concurrentUseAtMillis?: number;
|
|
1788
1961
|
}
|
|
1789
1962
|
interface ListKeysParams {
|
|
1790
1963
|
userId: number;
|
|
@@ -1793,6 +1966,20 @@ interface ListKeysParams {
|
|
|
1793
1966
|
}
|
|
1794
1967
|
/** How much of the fingerprint the list returns. */
|
|
1795
1968
|
declare const KEY_FINGERPRINT_PREFIX_LENGTH = 8;
|
|
1969
|
+
/**
|
|
1970
|
+
* What a registration settled on, for the caller that has to tell the browser.
|
|
1971
|
+
*
|
|
1972
|
+
* The sign-in paths put these two into their `LoginResult`, the Next.js proxy
|
|
1973
|
+
* copies them into the sealed cookie, and that is the whole carrier by which the
|
|
1974
|
+
* proxy learns a session is bound and when its key runs out. Returned rather than
|
|
1975
|
+
* looked up again: the row was just written (or just read), so a second query
|
|
1976
|
+
* would be a second answer to a question already settled.
|
|
1977
|
+
*/
|
|
1978
|
+
interface RegisteredKeyBinding {
|
|
1979
|
+
binding: SessionBindingType;
|
|
1980
|
+
/** null only for a key registered before expiry was stamped at all. */
|
|
1981
|
+
expiresAt: Date | null;
|
|
1982
|
+
}
|
|
1796
1983
|
/**
|
|
1797
1984
|
* Register a new public key for a user
|
|
1798
1985
|
*
|
|
@@ -1805,7 +1992,7 @@ declare const KEY_FINGERPRINT_PREFIX_LENGTH = 8;
|
|
|
1805
1992
|
* @throws InvalidKeyFingerprintError fingerprint가 publicKey와 맞지 않을 때
|
|
1806
1993
|
* @throws KeyAlgorithmMismatchError 키의 SPKI 타입이 선언된 algorithm과 다를 때
|
|
1807
1994
|
*/
|
|
1808
|
-
declare function registerPublicKeyService(params: RegisterPublicKeyParams): Promise<
|
|
1995
|
+
declare function registerPublicKeyService(params: RegisterPublicKeyParams): Promise<RegisteredKeyBinding>;
|
|
1809
1996
|
/**
|
|
1810
1997
|
* Rotate user's public key (revoke old, register new)
|
|
1811
1998
|
*
|
|
@@ -2163,6 +2350,8 @@ interface OAuthCallbackParams {
|
|
|
2163
2350
|
ip?: string;
|
|
2164
2351
|
/** `user-agent` of the callback request, already truncated at the route. */
|
|
2165
2352
|
userAgent?: string;
|
|
2353
|
+
/** Whether proxy-guard recognised the trusted Next.js proxy, from the same helper. */
|
|
2354
|
+
webProxy?: boolean;
|
|
2166
2355
|
}
|
|
2167
2356
|
interface OAuthCallbackResult {
|
|
2168
2357
|
redirectUrl: string;
|
|
@@ -2264,6 +2453,13 @@ interface OAuthNativeParams {
|
|
|
2264
2453
|
ip?: string;
|
|
2265
2454
|
/** `user-agent` of the request, already truncated at the route. */
|
|
2266
2455
|
userAgent?: string;
|
|
2456
|
+
/**
|
|
2457
|
+
* Whether proxy-guard recognised the trusted Next.js proxy, from the same
|
|
2458
|
+
* helper. Accepted because every registering route spreads the whole
|
|
2459
|
+
* provenance, and read by nothing: a native sign-in never produces a bound
|
|
2460
|
+
* key — see the note at the registration below.
|
|
2461
|
+
*/
|
|
2462
|
+
webProxy?: boolean;
|
|
2267
2463
|
/** Apple은 첫 로그인에만 이름을 별도로 주므로 클라이언트가 전달할 수 있다. */
|
|
2268
2464
|
profile?: {
|
|
2269
2465
|
name?: string;
|
|
@@ -2415,6 +2611,110 @@ declare function revokeOAuth2GrantService(id: number, userId: number): Promise<v
|
|
|
2415
2611
|
*/
|
|
2416
2612
|
declare function revokeAllOAuth2GrantsForUser(userId: number): Promise<void>;
|
|
2417
2613
|
|
|
2614
|
+
/**
|
|
2615
|
+
* @spfn/auth - Session Binding Service
|
|
2616
|
+
*
|
|
2617
|
+
* The opt-in from #97: an account says its web sessions should run on a key that
|
|
2618
|
+
* expires in hours and can only be renewed by a fresh WebAuthn assertion. A
|
|
2619
|
+
* session cookie copied off that machine — out of a browser profile, out of
|
|
2620
|
+
* DevTools, by malware — still signs like the original until that key runs out,
|
|
2621
|
+
* and then stops, because the copy cannot produce the assertion.
|
|
2622
|
+
*
|
|
2623
|
+
* Three rules shape this file.
|
|
2624
|
+
*
|
|
2625
|
+
* It can only be turned on where the backend can recognise the trusted Next.js
|
|
2626
|
+
* proxy. `clientType` is that recognition and `proxy-guard` is what sets it;
|
|
2627
|
+
* without it every key would be registered unbound and the setting would be a
|
|
2628
|
+
* switch that reports success and protects nothing, so the request is refused as
|
|
2629
|
+
* the configuration error it is.
|
|
2630
|
+
*
|
|
2631
|
+
* Turning it *off* is the privileged direction, which is the reverse of the
|
|
2632
|
+
* usual posture. `assertRecentAuthentication` is satisfied by the age of the
|
|
2633
|
+
* device key this request is signed with — and a cookie copied in the ten
|
|
2634
|
+
* minutes after a sign-in carries exactly that. So leaving `'passkey'` asks for
|
|
2635
|
+
* a credential the copy does not hold: a renewal-grade assertion, or the account
|
|
2636
|
+
* password.
|
|
2637
|
+
*
|
|
2638
|
+
* Enabling twice is idempotent. Recomputing the expiry on a repeat call would
|
|
2639
|
+
* make this route a way to extend a bound key without presenting anything, which
|
|
2640
|
+
* is the thing the short life exists to prevent.
|
|
2641
|
+
*/
|
|
2642
|
+
|
|
2643
|
+
/** What every one of the three reads and writes here answers with. */
|
|
2644
|
+
interface SessionBindingResult {
|
|
2645
|
+
mode: SessionBindingType;
|
|
2646
|
+
/**
|
|
2647
|
+
* When the key this request is signed with expires, for a bound session.
|
|
2648
|
+
*
|
|
2649
|
+
* Absent when the account is unbound, and absent for a bound account asking
|
|
2650
|
+
* from a key that is not itself bound — a native client, say, whose key was
|
|
2651
|
+
* registered on a channel the proxy never touched. The response is the input
|
|
2652
|
+
* the Next.js interceptor re-seals the cookie from, so it describes this
|
|
2653
|
+
* session rather than the account.
|
|
2654
|
+
*/
|
|
2655
|
+
keyExpiresAtMillis?: number;
|
|
2656
|
+
}
|
|
2657
|
+
interface SessionBindingParams {
|
|
2658
|
+
userId: number;
|
|
2659
|
+
/** The device key this request is signed with — the one binding is applied to. */
|
|
2660
|
+
keyId: string;
|
|
2661
|
+
/** Whether proxy-guard recognised the trusted Next.js proxy. */
|
|
2662
|
+
webProxy?: boolean;
|
|
2663
|
+
}
|
|
2664
|
+
interface DisableSessionBindingParams extends SessionBindingParams {
|
|
2665
|
+
/** A renewal-grade assertion, from `binding/disable/options`. */
|
|
2666
|
+
response?: AuthenticationResponseJSON;
|
|
2667
|
+
/** The account password, for a browser with no passkey to hand. */
|
|
2668
|
+
currentPassword?: string;
|
|
2669
|
+
}
|
|
2670
|
+
/** What binding is on this account, and when this session's key runs out. */
|
|
2671
|
+
declare function getSessionBindingService(params: SessionBindingParams): Promise<SessionBindingResult>;
|
|
2672
|
+
/**
|
|
2673
|
+
* The binding facts of one key, by key id alone.
|
|
2674
|
+
*
|
|
2675
|
+
* For `/_auth/oauth/finalize`, which is the one sealing path with no session and
|
|
2676
|
+
* no `LoginResult` to read them off: the browser arrives at the callback page
|
|
2677
|
+
* holding a key id the OAuth state minted, and the interceptor seals a session
|
|
2678
|
+
* from the answer. Reflecting the caller's own claim about binding would let a
|
|
2679
|
+
* caller ask for an unbound cookie over a bound key, so the row is read instead.
|
|
2680
|
+
*
|
|
2681
|
+
* Scoped by key id and nothing else. The id is a server-minted UUID that only
|
|
2682
|
+
* ever travelled inside the sealed state, so a caller who holds one holds it
|
|
2683
|
+
* because the flow gave it to them; adding the caller's own `userId` to the
|
|
2684
|
+
* lookup would make the answer vary by a second guessable value without
|
|
2685
|
+
* withholding anything from someone who already has the first.
|
|
2686
|
+
*/
|
|
2687
|
+
declare function keySessionBindingService(keyId: string): Promise<{
|
|
2688
|
+
sessionBinding?: SessionBindingType;
|
|
2689
|
+
keyExpiresAtMillis?: number;
|
|
2690
|
+
}>;
|
|
2691
|
+
/**
|
|
2692
|
+
* Turn session binding on, and bind the key this request is signed with.
|
|
2693
|
+
*
|
|
2694
|
+
* The current key is bound here rather than only at the next sign-in, because
|
|
2695
|
+
* otherwise the setting would not apply to the session that asked for it until
|
|
2696
|
+
* the person signed in again — and the response is what tells the Next.js proxy
|
|
2697
|
+
* to re-seal the cookie with the new expiry, so the browser and the key row
|
|
2698
|
+
* agree from this moment on.
|
|
2699
|
+
*
|
|
2700
|
+
* @throws SessionBindingUnavailableError proxy-guard가 설정되지 않은 배포일 때
|
|
2701
|
+
* @throws ValidationError 살아 있는 패스키가 하나도 없을 때
|
|
2702
|
+
* @throws RecentAuthenticationRequiredError 최근 인증이 없을 때
|
|
2703
|
+
*/
|
|
2704
|
+
declare function enableSessionBindingService(params: SessionBindingParams): Promise<SessionBindingResult>;
|
|
2705
|
+
/**
|
|
2706
|
+
* Turn session binding off, once the caller has proved they are still the owner.
|
|
2707
|
+
*
|
|
2708
|
+
* Every bound key returns to an ordinary ninety-day life in the same call. A row
|
|
2709
|
+
* left marked `'passkey'` would keep expiring in hours with nothing left to
|
|
2710
|
+
* renew it, which is the state the person just asked not to be in.
|
|
2711
|
+
*
|
|
2712
|
+
* @throws RecentAuthenticationRequiredError 새 자격증명 없이 껐을 때
|
|
2713
|
+
*/
|
|
2714
|
+
declare function disableSessionBindingService(params: DisableSessionBindingParams): Promise<SessionBindingResult>;
|
|
2715
|
+
/** The challenge the disabling ceremony signs. Same ceremony renewal uses. */
|
|
2716
|
+
declare function startSessionBindingDisableService(userId: number): Promise<PublicKeyCredentialRequestOptionsJSON>;
|
|
2717
|
+
|
|
2418
2718
|
/**
|
|
2419
2719
|
* @spfn/auth - Main Router
|
|
2420
2720
|
*
|
|
@@ -2522,7 +2822,7 @@ declare const mainAuthRouter: _spfn_core_route.Router<{
|
|
|
2522
2822
|
deviceName: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
2523
2823
|
platform: _sinclair_typebox.TOptional<_sinclair_typebox.TUnion<_sinclair_typebox.TLiteral<"ios" | "android" | "web" | "desktop">[]>>;
|
|
2524
2824
|
}>;
|
|
2525
|
-
}, RegisterResult>;
|
|
2825
|
+
}, RegisterResult & LoginBindingFields>;
|
|
2526
2826
|
login: _spfn_core_route.RouteDef<{
|
|
2527
2827
|
body: _sinclair_typebox.TObject<{
|
|
2528
2828
|
email: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
@@ -2560,6 +2860,8 @@ declare const mainAuthRouter: _spfn_core_route.Router<{
|
|
|
2560
2860
|
} | {
|
|
2561
2861
|
email?: string | undefined;
|
|
2562
2862
|
phone?: string | undefined;
|
|
2863
|
+
sessionBinding?: "none" | "passkey" | undefined;
|
|
2864
|
+
keyExpiresAtMillis?: number | undefined;
|
|
2563
2865
|
status: "approved";
|
|
2564
2866
|
userId: string;
|
|
2565
2867
|
publicId: string;
|
|
@@ -2736,6 +3038,32 @@ declare const mainAuthRouter: _spfn_core_route.Router<{
|
|
|
2736
3038
|
phoneVerified: boolean;
|
|
2737
3039
|
hasPassword: boolean;
|
|
2738
3040
|
}>;
|
|
3041
|
+
setSessionBinding: _spfn_core_route.RouteDef<{
|
|
3042
|
+
body: _sinclair_typebox.TObject<{
|
|
3043
|
+
mode: _sinclair_typebox.TUnion<[_sinclair_typebox.TLiteral<"passkey">, _sinclair_typebox.TLiteral<"none">]>;
|
|
3044
|
+
response: _sinclair_typebox.TOptional<_sinclair_typebox.TUnknown>;
|
|
3045
|
+
currentPassword: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3046
|
+
}>;
|
|
3047
|
+
}, {}, SessionBindingResult>;
|
|
3048
|
+
getSessionBinding: _spfn_core_route.RouteDef<{}, {}, SessionBindingResult>;
|
|
3049
|
+
sessionBindingDisableOptions: _spfn_core_route.RouteDef<{
|
|
3050
|
+
body: _sinclair_typebox.TObject<{}>;
|
|
3051
|
+
}, {}, _simplewebauthn_server.PublicKeyCredentialRequestOptionsJSON>;
|
|
3052
|
+
sessionRenewOptions: _spfn_core_route.RouteDef<{
|
|
3053
|
+
body: _sinclair_typebox.TObject<{}>;
|
|
3054
|
+
}, {}, _simplewebauthn_server.PublicKeyCredentialRequestOptionsJSON>;
|
|
3055
|
+
sessionRenewVerify: _spfn_core_route.RouteDef<{
|
|
3056
|
+
body: _sinclair_typebox.TObject<{
|
|
3057
|
+
response: _sinclair_typebox.TUnknown;
|
|
3058
|
+
}>;
|
|
3059
|
+
}, {
|
|
3060
|
+
body: _sinclair_typebox.TObject<{
|
|
3061
|
+
publicKey: _sinclair_typebox.TString;
|
|
3062
|
+
keyId: _sinclair_typebox.TString;
|
|
3063
|
+
fingerprint: _sinclair_typebox.TString;
|
|
3064
|
+
algorithm: _sinclair_typebox.TOptional<_sinclair_typebox.TUnion<_sinclair_typebox.TLiteral<"ES256" | "RS256">[]>>;
|
|
3065
|
+
}>;
|
|
3066
|
+
}, SessionRenewResult>;
|
|
2739
3067
|
issueOneTimeToken: _spfn_core_route.RouteDef<{}, {}, IssueOneTimeTokenResult>;
|
|
2740
3068
|
requestAccountDeletion: _spfn_core_route.RouteDef<{
|
|
2741
3069
|
body: _sinclair_typebox.TObject<{
|
|
@@ -2791,398 +3119,797 @@ declare const mainAuthRouter: _spfn_core_route.Router<{
|
|
|
2791
3119
|
}, {}, {
|
|
2792
3120
|
authUrl: string;
|
|
2793
3121
|
}>;
|
|
2794
|
-
oauthFinalize: _spfn_core_route.RouteDef<{
|
|
3122
|
+
oauthFinalize: _spfn_core_route.RouteDef<{
|
|
3123
|
+
body: _sinclair_typebox.TObject<{
|
|
3124
|
+
userId: _sinclair_typebox.TString;
|
|
3125
|
+
keyId: _sinclair_typebox.TString;
|
|
3126
|
+
returnUrl: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3127
|
+
}>;
|
|
3128
|
+
}, {}, {
|
|
3129
|
+
sessionBinding?: SessionBindingType;
|
|
3130
|
+
keyExpiresAtMillis?: number;
|
|
3131
|
+
success: boolean;
|
|
3132
|
+
userId: string;
|
|
3133
|
+
keyId: string;
|
|
3134
|
+
returnUrl: string;
|
|
3135
|
+
}>;
|
|
3136
|
+
oauthProviderStart: _spfn_core_route.RouteDef<{
|
|
3137
|
+
params: _sinclair_typebox.TObject<{
|
|
3138
|
+
provider: _sinclair_typebox.TUnion<_sinclair_typebox.TLiteral<"google" | "apple" | "github" | "kakao" | "naver" | "superself">[]>;
|
|
3139
|
+
}>;
|
|
3140
|
+
query: _sinclair_typebox.TObject<{
|
|
3141
|
+
state: _sinclair_typebox.TString;
|
|
3142
|
+
}>;
|
|
3143
|
+
}, {}, Response>;
|
|
3144
|
+
oauthProviderCallback: _spfn_core_route.RouteDef<{
|
|
3145
|
+
params: _sinclair_typebox.TObject<{
|
|
3146
|
+
provider: _sinclair_typebox.TUnion<_sinclair_typebox.TLiteral<"google" | "apple" | "github" | "kakao" | "naver" | "superself">[]>;
|
|
3147
|
+
}>;
|
|
3148
|
+
query: _sinclair_typebox.TObject<{
|
|
3149
|
+
code: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3150
|
+
state: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3151
|
+
error: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3152
|
+
error_description: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3153
|
+
}>;
|
|
3154
|
+
}, {}, Response>;
|
|
3155
|
+
getProviderOAuthUrl: _spfn_core_route.RouteDef<{
|
|
3156
|
+
params: _sinclair_typebox.TObject<{
|
|
3157
|
+
provider: _sinclair_typebox.TUnion<_sinclair_typebox.TLiteral<"google" | "apple" | "github" | "kakao" | "naver" | "superself">[]>;
|
|
3158
|
+
}>;
|
|
3159
|
+
body: _sinclair_typebox.TObject<{
|
|
3160
|
+
returnUrl: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3161
|
+
metadata: _sinclair_typebox.TOptional<_sinclair_typebox.TRecord<_sinclair_typebox.TString, _sinclair_typebox.TUnknown>>;
|
|
3162
|
+
state: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3163
|
+
}>;
|
|
3164
|
+
}, {}, {
|
|
3165
|
+
authUrl: string;
|
|
3166
|
+
}>;
|
|
3167
|
+
oauthNative: _spfn_core_route.RouteDef<{
|
|
3168
|
+
params: _sinclair_typebox.TObject<{
|
|
3169
|
+
provider: _sinclair_typebox.TUnion<_sinclair_typebox.TLiteral<"google" | "apple" | "github" | "kakao" | "naver" | "superself">[]>;
|
|
3170
|
+
}>;
|
|
3171
|
+
body: _sinclair_typebox.TObject<{
|
|
3172
|
+
idToken: _sinclair_typebox.TString;
|
|
3173
|
+
nonce: _sinclair_typebox.TString;
|
|
3174
|
+
accessToken: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3175
|
+
publicKey: _sinclair_typebox.TString;
|
|
3176
|
+
keyId: _sinclair_typebox.TString;
|
|
3177
|
+
fingerprint: _sinclair_typebox.TString;
|
|
3178
|
+
algorithm: _sinclair_typebox.TUnion<_sinclair_typebox.TLiteral<"ES256" | "RS256">[]>;
|
|
3179
|
+
deviceName: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3180
|
+
platform: _sinclair_typebox.TOptional<_sinclair_typebox.TUnion<_sinclair_typebox.TLiteral<"ios" | "android" | "web" | "desktop">[]>>;
|
|
3181
|
+
profile: _sinclair_typebox.TOptional<_sinclair_typebox.TObject<{
|
|
3182
|
+
name: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3183
|
+
}>>;
|
|
3184
|
+
metadata: _sinclair_typebox.TOptional<_sinclair_typebox.TRecord<_sinclair_typebox.TString, _sinclair_typebox.TUnknown>>;
|
|
3185
|
+
}>;
|
|
3186
|
+
}, {}, OAuthNativeResult>;
|
|
3187
|
+
oauthUnlinkNotify: _spfn_core_route.RouteDef<{
|
|
3188
|
+
params: _sinclair_typebox.TObject<{
|
|
3189
|
+
provider: _sinclair_typebox.TUnion<_sinclair_typebox.TLiteral<"google" | "apple" | "github" | "kakao" | "naver" | "superself">[]>;
|
|
3190
|
+
}>;
|
|
3191
|
+
}, {}, void | Response>;
|
|
3192
|
+
oauthUnlinkNotifyGet: _spfn_core_route.RouteDef<{
|
|
3193
|
+
params: _sinclair_typebox.TObject<{
|
|
3194
|
+
provider: _sinclair_typebox.TUnion<_sinclair_typebox.TLiteral<"google" | "apple" | "github" | "kakao" | "naver" | "superself">[]>;
|
|
3195
|
+
}>;
|
|
3196
|
+
}, {}, void | Response>;
|
|
3197
|
+
getInvitation: _spfn_core_route.RouteDef<{
|
|
3198
|
+
params: _sinclair_typebox.TObject<{
|
|
3199
|
+
token: _sinclair_typebox.TString;
|
|
3200
|
+
}>;
|
|
3201
|
+
}, {}, {
|
|
3202
|
+
email: string;
|
|
3203
|
+
role: string;
|
|
3204
|
+
roleDisplayName: string;
|
|
3205
|
+
invitedBy: string;
|
|
3206
|
+
expiresAt: string;
|
|
3207
|
+
metadata: Record<string, any> | undefined;
|
|
3208
|
+
}>;
|
|
3209
|
+
acceptInvitation: _spfn_core_route.RouteDef<{
|
|
3210
|
+
body: _sinclair_typebox.TObject<{
|
|
3211
|
+
token: _sinclair_typebox.TString;
|
|
3212
|
+
password: _sinclair_typebox.TString;
|
|
3213
|
+
}>;
|
|
3214
|
+
}, {
|
|
3215
|
+
body: _sinclair_typebox.TObject<{
|
|
3216
|
+
publicKey: _sinclair_typebox.TString;
|
|
3217
|
+
keyId: _sinclair_typebox.TString;
|
|
3218
|
+
fingerprint: _sinclair_typebox.TString;
|
|
3219
|
+
algorithm: _sinclair_typebox.TUnion<_sinclair_typebox.TLiteral<"ES256" | "RS256">[]>;
|
|
3220
|
+
}>;
|
|
3221
|
+
}, {
|
|
3222
|
+
userId: number;
|
|
3223
|
+
email: string;
|
|
3224
|
+
role: string;
|
|
3225
|
+
}>;
|
|
3226
|
+
createInvitation: _spfn_core_route.RouteDef<{
|
|
3227
|
+
body: _sinclair_typebox.TObject<{
|
|
3228
|
+
email: _sinclair_typebox.TString;
|
|
3229
|
+
roleId: _sinclair_typebox.TNumber;
|
|
3230
|
+
expiresInDays: _sinclair_typebox.TOptional<_sinclair_typebox.TNumber>;
|
|
3231
|
+
expiresAt: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3232
|
+
metadata: _sinclair_typebox.TOptional<_sinclair_typebox.TAny>;
|
|
3233
|
+
}>;
|
|
3234
|
+
}, {}, {
|
|
3235
|
+
id: number;
|
|
3236
|
+
email: string;
|
|
3237
|
+
token: string;
|
|
3238
|
+
roleId: number;
|
|
3239
|
+
expiresAt: string;
|
|
3240
|
+
invitationUrl: string;
|
|
3241
|
+
}>;
|
|
3242
|
+
listInvitations: _spfn_core_route.RouteDef<{
|
|
3243
|
+
query: _sinclair_typebox.TObject<{
|
|
3244
|
+
status: _sinclair_typebox.TOptional<_sinclair_typebox.TUnion<_sinclair_typebox.TLiteral<"pending" | "accepted" | "expired" | "cancelled">[]>>;
|
|
3245
|
+
page: _sinclair_typebox.TOptional<_sinclair_typebox.TNumber>;
|
|
3246
|
+
limit: _sinclair_typebox.TOptional<_sinclair_typebox.TNumber>;
|
|
3247
|
+
}>;
|
|
3248
|
+
}, {}, {
|
|
3249
|
+
invitations: {
|
|
3250
|
+
id: number;
|
|
3251
|
+
email: string;
|
|
3252
|
+
token: string;
|
|
3253
|
+
roleId: number;
|
|
3254
|
+
invitedBy: number;
|
|
3255
|
+
status: "pending" | "accepted" | "expired" | "cancelled";
|
|
3256
|
+
expiresAt: Date;
|
|
3257
|
+
acceptedAt: Date | null;
|
|
3258
|
+
cancelledAt: Date | null;
|
|
3259
|
+
metadata: Record<string, any> | null;
|
|
3260
|
+
createdAt: Date;
|
|
3261
|
+
updatedAt: Date;
|
|
3262
|
+
role: {
|
|
3263
|
+
id: number;
|
|
3264
|
+
name: string;
|
|
3265
|
+
displayName: string;
|
|
3266
|
+
};
|
|
3267
|
+
inviter: {
|
|
3268
|
+
id: number;
|
|
3269
|
+
email: string | null;
|
|
3270
|
+
};
|
|
3271
|
+
}[];
|
|
3272
|
+
total: number;
|
|
3273
|
+
page: number;
|
|
3274
|
+
limit: number;
|
|
3275
|
+
totalPages: number;
|
|
3276
|
+
}>;
|
|
3277
|
+
cancelInvitation: _spfn_core_route.RouteDef<{
|
|
3278
|
+
body: _sinclair_typebox.TObject<{
|
|
3279
|
+
id: _sinclair_typebox.TNumber;
|
|
3280
|
+
reason: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3281
|
+
}>;
|
|
3282
|
+
}, {}, {
|
|
3283
|
+
cancelledAt: string;
|
|
3284
|
+
}>;
|
|
3285
|
+
resendInvitation: _spfn_core_route.RouteDef<{
|
|
3286
|
+
body: _sinclair_typebox.TObject<{
|
|
3287
|
+
id: _sinclair_typebox.TNumber;
|
|
3288
|
+
expiresInDays: _sinclair_typebox.TOptional<_sinclair_typebox.TNumber>;
|
|
3289
|
+
}>;
|
|
3290
|
+
}, {}, {
|
|
3291
|
+
expiresAt: string;
|
|
3292
|
+
}>;
|
|
3293
|
+
deleteInvitation: _spfn_core_route.RouteDef<{
|
|
3294
|
+
body: _sinclair_typebox.TObject<{
|
|
3295
|
+
id: _sinclair_typebox.TNumber;
|
|
3296
|
+
}>;
|
|
3297
|
+
}, {}, void>;
|
|
3298
|
+
getUserProfile: _spfn_core_route.RouteDef<{}, {}, UserProfile>;
|
|
3299
|
+
updateUserProfile: _spfn_core_route.RouteDef<{
|
|
3300
|
+
body: _sinclair_typebox.TObject<{
|
|
3301
|
+
displayName: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3302
|
+
firstName: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3303
|
+
lastName: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3304
|
+
avatarUrl: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3305
|
+
bio: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3306
|
+
locale: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3307
|
+
timezone: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3308
|
+
dateOfBirth: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3309
|
+
gender: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3310
|
+
website: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3311
|
+
location: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3312
|
+
company: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3313
|
+
jobTitle: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3314
|
+
metadata: _sinclair_typebox.TOptional<_sinclair_typebox.TRecord<_sinclair_typebox.TString, _sinclair_typebox.TAny>>;
|
|
3315
|
+
}>;
|
|
3316
|
+
}, {}, ProfileInfo>;
|
|
3317
|
+
checkUsername: _spfn_core_route.RouteDef<{
|
|
3318
|
+
query: _sinclair_typebox.TObject<{
|
|
3319
|
+
username: _sinclair_typebox.TString;
|
|
3320
|
+
}>;
|
|
3321
|
+
}, {}, {
|
|
3322
|
+
available: boolean;
|
|
3323
|
+
}>;
|
|
3324
|
+
updateUsername: _spfn_core_route.RouteDef<{
|
|
3325
|
+
body: _sinclair_typebox.TObject<{
|
|
3326
|
+
username: _sinclair_typebox.TUnion<[_sinclair_typebox.TString, _sinclair_typebox.TNull]>;
|
|
3327
|
+
}>;
|
|
3328
|
+
}, {}, {
|
|
3329
|
+
deletedAt: Date | null;
|
|
3330
|
+
deletedBy: string | null;
|
|
3331
|
+
createdAt: Date;
|
|
3332
|
+
updatedAt: Date;
|
|
3333
|
+
id: number;
|
|
3334
|
+
publicId: string;
|
|
3335
|
+
email: string | null;
|
|
3336
|
+
phone: string | null;
|
|
3337
|
+
username: string | null;
|
|
3338
|
+
passwordHash: string | null;
|
|
3339
|
+
passwordChangeRequired: boolean;
|
|
3340
|
+
roleId: number;
|
|
3341
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
3342
|
+
emailVerifiedAt: Date | null;
|
|
3343
|
+
phoneVerifiedAt: Date | null;
|
|
3344
|
+
keyEpoch: number;
|
|
3345
|
+
sessionBinding: "none" | "passkey";
|
|
3346
|
+
sessionBindingChangedAt: Date | null;
|
|
3347
|
+
lastLoginAt: Date | null;
|
|
3348
|
+
}>;
|
|
3349
|
+
updateLocale: _spfn_core_route.RouteDef<{
|
|
3350
|
+
body: _sinclair_typebox.TObject<{
|
|
3351
|
+
locale: _sinclair_typebox.TString;
|
|
3352
|
+
}>;
|
|
3353
|
+
}, {}, {
|
|
3354
|
+
locale: string;
|
|
3355
|
+
}>;
|
|
3356
|
+
listRoles: _spfn_core_route.RouteDef<{
|
|
3357
|
+
query: _sinclair_typebox.TObject<{
|
|
3358
|
+
includeInactive: _sinclair_typebox.TOptional<_sinclair_typebox.TBoolean>;
|
|
3359
|
+
}>;
|
|
3360
|
+
}, {}, {
|
|
3361
|
+
roles: {
|
|
3362
|
+
description: string | null;
|
|
3363
|
+
name: string;
|
|
3364
|
+
id: number;
|
|
3365
|
+
displayName: string;
|
|
3366
|
+
isBuiltin: boolean;
|
|
3367
|
+
isSystem: boolean;
|
|
3368
|
+
isActive: boolean;
|
|
3369
|
+
priority: number;
|
|
3370
|
+
createdAt: Date;
|
|
3371
|
+
updatedAt: Date;
|
|
3372
|
+
}[];
|
|
3373
|
+
}>;
|
|
3374
|
+
createAdminRole: _spfn_core_route.RouteDef<{
|
|
2795
3375
|
body: _sinclair_typebox.TObject<{
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
3376
|
+
name: _sinclair_typebox.TString;
|
|
3377
|
+
displayName: _sinclair_typebox.TString;
|
|
3378
|
+
description: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3379
|
+
priority: _sinclair_typebox.TOptional<_sinclair_typebox.TNumber>;
|
|
3380
|
+
permissionIds: _sinclair_typebox.TOptional<_sinclair_typebox.TArray<_sinclair_typebox.TNumber>>;
|
|
2799
3381
|
}>;
|
|
2800
3382
|
}, {}, {
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
3383
|
+
role: {
|
|
3384
|
+
description: string | null;
|
|
3385
|
+
name: string;
|
|
3386
|
+
id: number;
|
|
3387
|
+
displayName: string;
|
|
3388
|
+
isBuiltin: boolean;
|
|
3389
|
+
isSystem: boolean;
|
|
3390
|
+
isActive: boolean;
|
|
3391
|
+
priority: number;
|
|
3392
|
+
createdAt: Date;
|
|
3393
|
+
updatedAt: Date;
|
|
3394
|
+
};
|
|
2805
3395
|
}>;
|
|
2806
|
-
|
|
3396
|
+
updateAdminRole: _spfn_core_route.RouteDef<{
|
|
2807
3397
|
params: _sinclair_typebox.TObject<{
|
|
2808
|
-
|
|
3398
|
+
id: _sinclair_typebox.TNumber;
|
|
2809
3399
|
}>;
|
|
2810
|
-
|
|
2811
|
-
|
|
3400
|
+
body: _sinclair_typebox.TObject<{
|
|
3401
|
+
displayName: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3402
|
+
description: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3403
|
+
priority: _sinclair_typebox.TOptional<_sinclair_typebox.TNumber>;
|
|
3404
|
+
isActive: _sinclair_typebox.TOptional<_sinclair_typebox.TBoolean>;
|
|
2812
3405
|
}>;
|
|
2813
|
-
}, {},
|
|
2814
|
-
|
|
3406
|
+
}, {}, {
|
|
3407
|
+
role: {
|
|
3408
|
+
description: string | null;
|
|
3409
|
+
name: string;
|
|
3410
|
+
id: number;
|
|
3411
|
+
displayName: string;
|
|
3412
|
+
isBuiltin: boolean;
|
|
3413
|
+
isSystem: boolean;
|
|
3414
|
+
isActive: boolean;
|
|
3415
|
+
priority: number;
|
|
3416
|
+
createdAt: Date;
|
|
3417
|
+
updatedAt: Date;
|
|
3418
|
+
};
|
|
3419
|
+
}>;
|
|
3420
|
+
deleteAdminRole: _spfn_core_route.RouteDef<{
|
|
2815
3421
|
params: _sinclair_typebox.TObject<{
|
|
2816
|
-
|
|
2817
|
-
}>;
|
|
2818
|
-
query: _sinclair_typebox.TObject<{
|
|
2819
|
-
code: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
2820
|
-
state: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
2821
|
-
error: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
2822
|
-
error_description: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3422
|
+
id: _sinclair_typebox.TNumber;
|
|
2823
3423
|
}>;
|
|
2824
|
-
}, {},
|
|
2825
|
-
|
|
3424
|
+
}, {}, void>;
|
|
3425
|
+
updateUserRole: _spfn_core_route.RouteDef<{
|
|
2826
3426
|
params: _sinclair_typebox.TObject<{
|
|
2827
|
-
|
|
3427
|
+
userId: _sinclair_typebox.TNumber;
|
|
2828
3428
|
}>;
|
|
2829
3429
|
body: _sinclair_typebox.TObject<{
|
|
2830
|
-
|
|
2831
|
-
metadata: _sinclair_typebox.TOptional<_sinclair_typebox.TRecord<_sinclair_typebox.TString, _sinclair_typebox.TUnknown>>;
|
|
2832
|
-
state: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3430
|
+
roleId: _sinclair_typebox.TNumber;
|
|
2833
3431
|
}>;
|
|
2834
3432
|
}, {}, {
|
|
2835
|
-
|
|
3433
|
+
userId: number;
|
|
3434
|
+
roleId: number;
|
|
2836
3435
|
}>;
|
|
2837
|
-
|
|
2838
|
-
params: _sinclair_typebox.TObject<{
|
|
2839
|
-
provider: _sinclair_typebox.TUnion<_sinclair_typebox.TLiteral<"google" | "apple" | "github" | "kakao" | "naver" | "superself">[]>;
|
|
2840
|
-
}>;
|
|
3436
|
+
issueOpsToken: _spfn_core_route.RouteDef<{
|
|
2841
3437
|
body: _sinclair_typebox.TObject<{
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
publicKey: _sinclair_typebox.TString;
|
|
2846
|
-
keyId: _sinclair_typebox.TString;
|
|
2847
|
-
fingerprint: _sinclair_typebox.TString;
|
|
2848
|
-
algorithm: _sinclair_typebox.TUnion<_sinclair_typebox.TLiteral<"ES256" | "RS256">[]>;
|
|
2849
|
-
deviceName: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
2850
|
-
platform: _sinclair_typebox.TOptional<_sinclair_typebox.TUnion<_sinclair_typebox.TLiteral<"ios" | "android" | "web" | "desktop">[]>>;
|
|
2851
|
-
profile: _sinclair_typebox.TOptional<_sinclair_typebox.TObject<{
|
|
2852
|
-
name: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
2853
|
-
}>>;
|
|
2854
|
-
metadata: _sinclair_typebox.TOptional<_sinclair_typebox.TRecord<_sinclair_typebox.TString, _sinclair_typebox.TUnknown>>;
|
|
3438
|
+
name: _sinclair_typebox.TString;
|
|
3439
|
+
scopes: _sinclair_typebox.TArray<_sinclair_typebox.TString>;
|
|
3440
|
+
expiresInDays: _sinclair_typebox.TOptional<_sinclair_typebox.TUnion<[_sinclair_typebox.TNumber, _sinclair_typebox.TNull]>>;
|
|
2855
3441
|
}>;
|
|
2856
|
-
}, {},
|
|
2857
|
-
|
|
3442
|
+
}, {}, {
|
|
3443
|
+
token: string;
|
|
3444
|
+
opsToken: {
|
|
3445
|
+
id: number;
|
|
3446
|
+
name: string;
|
|
3447
|
+
scopes: string[];
|
|
3448
|
+
expiresAt: string | null;
|
|
3449
|
+
revokedAt: string | null;
|
|
3450
|
+
lastUsedAt: string | null;
|
|
3451
|
+
createdAt: string | null;
|
|
3452
|
+
};
|
|
3453
|
+
}>;
|
|
3454
|
+
listOpsTokens: _spfn_core_route.RouteDef<{}, {}, {
|
|
3455
|
+
opsTokens: {
|
|
3456
|
+
id: number;
|
|
3457
|
+
name: string;
|
|
3458
|
+
scopes: string[];
|
|
3459
|
+
expiresAt: string | null;
|
|
3460
|
+
revokedAt: string | null;
|
|
3461
|
+
lastUsedAt: string | null;
|
|
3462
|
+
createdAt: string | null;
|
|
3463
|
+
}[];
|
|
3464
|
+
}>;
|
|
3465
|
+
revokeOpsToken: _spfn_core_route.RouteDef<{
|
|
2858
3466
|
params: _sinclair_typebox.TObject<{
|
|
2859
|
-
|
|
3467
|
+
id: _sinclair_typebox.TNumber;
|
|
2860
3468
|
}>;
|
|
2861
|
-
}, {},
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
3469
|
+
}, {}, {
|
|
3470
|
+
opsToken: {
|
|
3471
|
+
id: number;
|
|
3472
|
+
name: string;
|
|
3473
|
+
scopes: string[];
|
|
3474
|
+
expiresAt: string | null;
|
|
3475
|
+
revokedAt: string | null;
|
|
3476
|
+
lastUsedAt: string | null;
|
|
3477
|
+
createdAt: string | null;
|
|
3478
|
+
};
|
|
3479
|
+
}>;
|
|
3480
|
+
registerOAuth2Client: _spfn_core_route.RouteDef<{}, {}, Response>;
|
|
3481
|
+
getOAuth2Authorize: _spfn_core_route.RouteDef<{
|
|
3482
|
+
query: _sinclair_typebox.TObject<{
|
|
3483
|
+
client_id: _sinclair_typebox.TString;
|
|
3484
|
+
redirect_uri: _sinclair_typebox.TString;
|
|
3485
|
+
code_challenge: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3486
|
+
code_challenge_method: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3487
|
+
resource: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3488
|
+
scope: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3489
|
+
state: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
2865
3490
|
}>;
|
|
2866
|
-
}, {},
|
|
2867
|
-
|
|
3491
|
+
}, {}, OAuth2ConsentView>;
|
|
3492
|
+
createOAuth2AuthorizationCode: _spfn_core_route.RouteDef<{
|
|
3493
|
+
body: _sinclair_typebox.TObject<{
|
|
3494
|
+
approve: _sinclair_typebox.TBoolean;
|
|
3495
|
+
client_id: _sinclair_typebox.TString;
|
|
3496
|
+
redirect_uri: _sinclair_typebox.TString;
|
|
3497
|
+
code_challenge: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3498
|
+
code_challenge_method: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3499
|
+
resource: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3500
|
+
scope: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3501
|
+
state: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3502
|
+
}>;
|
|
3503
|
+
}, {}, OAuth2AuthorizationCodeIssued>;
|
|
3504
|
+
oauth2Token: _spfn_core_route.RouteDef<{}, {}, Response>;
|
|
3505
|
+
oauth2Revoke: _spfn_core_route.RouteDef<{}, {}, Response>;
|
|
3506
|
+
listOAuth2Grants: _spfn_core_route.RouteDef<{}, {}, {
|
|
3507
|
+
grants: OAuth2GrantSummary[];
|
|
3508
|
+
}>;
|
|
3509
|
+
revokeOAuth2Grant: _spfn_core_route.RouteDef<{
|
|
2868
3510
|
params: _sinclair_typebox.TObject<{
|
|
2869
|
-
|
|
3511
|
+
id: _sinclair_typebox.TNumber;
|
|
2870
3512
|
}>;
|
|
2871
3513
|
}, {}, {
|
|
2872
|
-
|
|
2873
|
-
role: string;
|
|
2874
|
-
roleDisplayName: string;
|
|
2875
|
-
invitedBy: string;
|
|
2876
|
-
expiresAt: string;
|
|
2877
|
-
metadata: Record<string, any> | undefined;
|
|
3514
|
+
revoked: boolean;
|
|
2878
3515
|
}>;
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
3516
|
+
oauth2AuthorizationServerMetadata: _spfn_core_route.RouteDef<{}, {}, Response>;
|
|
3517
|
+
}>;
|
|
3518
|
+
|
|
3519
|
+
/**
|
|
3520
|
+
* @spfn/auth - User Public Keys Entity
|
|
3521
|
+
*
|
|
3522
|
+
* Stores client-generated public keys for JWT verification
|
|
3523
|
+
* Supports key rotation and multi-key management per user
|
|
3524
|
+
*/
|
|
3525
|
+
/**
|
|
3526
|
+
* User Public Keys Table
|
|
3527
|
+
* Each user can have multiple public keys (for rotation)
|
|
3528
|
+
*/
|
|
3529
|
+
declare const userPublicKeys: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
3530
|
+
name: "user_public_keys";
|
|
3531
|
+
schema: string;
|
|
3532
|
+
columns: {
|
|
3533
|
+
id: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.SetIsPrimaryKey<drizzle_orm_pg_core.PgBigSerial53Builder>, {
|
|
3534
|
+
name: string;
|
|
3535
|
+
tableName: "user_public_keys";
|
|
3536
|
+
dataType: "number int53";
|
|
3537
|
+
data: number;
|
|
3538
|
+
driverParam: number;
|
|
3539
|
+
notNull: true;
|
|
3540
|
+
hasDefault: true;
|
|
3541
|
+
isPrimaryKey: false;
|
|
3542
|
+
isAutoincrement: false;
|
|
3543
|
+
hasRuntimeDefault: false;
|
|
3544
|
+
enumValues: undefined;
|
|
3545
|
+
identity: undefined;
|
|
3546
|
+
generated: undefined;
|
|
2883
3547
|
}>;
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
3548
|
+
userId: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgBigInt53Builder>, {
|
|
3549
|
+
name: string;
|
|
3550
|
+
tableName: "user_public_keys";
|
|
3551
|
+
dataType: "number int53";
|
|
3552
|
+
data: number;
|
|
3553
|
+
driverParam: string | number;
|
|
3554
|
+
notNull: true;
|
|
3555
|
+
hasDefault: false;
|
|
3556
|
+
isPrimaryKey: false;
|
|
3557
|
+
isAutoincrement: false;
|
|
3558
|
+
hasRuntimeDefault: false;
|
|
3559
|
+
enumValues: undefined;
|
|
3560
|
+
identity: undefined;
|
|
3561
|
+
generated: undefined;
|
|
2890
3562
|
}>;
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
3563
|
+
keyId: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
|
|
3564
|
+
name: string;
|
|
3565
|
+
tableName: "user_public_keys";
|
|
3566
|
+
dataType: "string";
|
|
3567
|
+
data: string;
|
|
3568
|
+
driverParam: string;
|
|
3569
|
+
notNull: true;
|
|
3570
|
+
hasDefault: false;
|
|
3571
|
+
isPrimaryKey: false;
|
|
3572
|
+
isAutoincrement: false;
|
|
3573
|
+
hasRuntimeDefault: false;
|
|
3574
|
+
enumValues: undefined;
|
|
3575
|
+
identity: undefined;
|
|
3576
|
+
generated: undefined;
|
|
3577
|
+
}>;
|
|
3578
|
+
publicKey: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
|
|
3579
|
+
name: string;
|
|
3580
|
+
tableName: "user_public_keys";
|
|
3581
|
+
dataType: "string";
|
|
3582
|
+
data: string;
|
|
3583
|
+
driverParam: string;
|
|
3584
|
+
notNull: true;
|
|
3585
|
+
hasDefault: false;
|
|
3586
|
+
isPrimaryKey: false;
|
|
3587
|
+
isAutoincrement: false;
|
|
3588
|
+
hasRuntimeDefault: false;
|
|
3589
|
+
enumValues: undefined;
|
|
3590
|
+
identity: undefined;
|
|
3591
|
+
generated: undefined;
|
|
2903
3592
|
}>;
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
3593
|
+
algorithm: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<["ES256", "RS256"] & [string, ...string[]]>>>, {
|
|
3594
|
+
name: string;
|
|
3595
|
+
tableName: "user_public_keys";
|
|
3596
|
+
dataType: "string enum";
|
|
3597
|
+
data: "ES256" | "RS256";
|
|
3598
|
+
driverParam: string;
|
|
3599
|
+
notNull: true;
|
|
3600
|
+
hasDefault: true;
|
|
3601
|
+
isPrimaryKey: false;
|
|
3602
|
+
isAutoincrement: false;
|
|
3603
|
+
hasRuntimeDefault: false;
|
|
3604
|
+
enumValues: ["ES256", "RS256"] & [string, ...string[]];
|
|
3605
|
+
identity: undefined;
|
|
3606
|
+
generated: undefined;
|
|
2917
3607
|
}>;
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
role: {
|
|
2933
|
-
id: number;
|
|
2934
|
-
name: string;
|
|
2935
|
-
displayName: string;
|
|
2936
|
-
};
|
|
2937
|
-
inviter: {
|
|
2938
|
-
id: number;
|
|
2939
|
-
email: string | null;
|
|
2940
|
-
};
|
|
2941
|
-
}[];
|
|
2942
|
-
total: number;
|
|
2943
|
-
page: number;
|
|
2944
|
-
limit: number;
|
|
2945
|
-
totalPages: number;
|
|
2946
|
-
}>;
|
|
2947
|
-
cancelInvitation: _spfn_core_route.RouteDef<{
|
|
2948
|
-
body: _sinclair_typebox.TObject<{
|
|
2949
|
-
id: _sinclair_typebox.TNumber;
|
|
2950
|
-
reason: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3608
|
+
fingerprint: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
|
|
3609
|
+
name: string;
|
|
3610
|
+
tableName: "user_public_keys";
|
|
3611
|
+
dataType: "string";
|
|
3612
|
+
data: string;
|
|
3613
|
+
driverParam: string;
|
|
3614
|
+
notNull: true;
|
|
3615
|
+
hasDefault: false;
|
|
3616
|
+
isPrimaryKey: false;
|
|
3617
|
+
isAutoincrement: false;
|
|
3618
|
+
hasRuntimeDefault: false;
|
|
3619
|
+
enumValues: undefined;
|
|
3620
|
+
identity: undefined;
|
|
3621
|
+
generated: undefined;
|
|
2951
3622
|
}>;
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
3623
|
+
deviceName: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
|
|
3624
|
+
name: string;
|
|
3625
|
+
tableName: "user_public_keys";
|
|
3626
|
+
dataType: "string";
|
|
3627
|
+
data: string;
|
|
3628
|
+
driverParam: string;
|
|
3629
|
+
notNull: false;
|
|
3630
|
+
hasDefault: false;
|
|
3631
|
+
isPrimaryKey: false;
|
|
3632
|
+
isAutoincrement: false;
|
|
3633
|
+
hasRuntimeDefault: false;
|
|
3634
|
+
enumValues: undefined;
|
|
3635
|
+
identity: undefined;
|
|
3636
|
+
generated: undefined;
|
|
2959
3637
|
}>;
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
3638
|
+
platform: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.PgTextBuilder<["ios", "android", "web", "desktop"] & [string, ...string[]]>, {
|
|
3639
|
+
name: string;
|
|
3640
|
+
tableName: "user_public_keys";
|
|
3641
|
+
dataType: "string enum";
|
|
3642
|
+
data: "ios" | "android" | "web" | "desktop";
|
|
3643
|
+
driverParam: string;
|
|
3644
|
+
notNull: false;
|
|
3645
|
+
hasDefault: false;
|
|
3646
|
+
isPrimaryKey: false;
|
|
3647
|
+
isAutoincrement: false;
|
|
3648
|
+
hasRuntimeDefault: false;
|
|
3649
|
+
enumValues: ["ios", "android", "web", "desktop"] & [string, ...string[]];
|
|
3650
|
+
identity: undefined;
|
|
3651
|
+
generated: undefined;
|
|
2966
3652
|
}>;
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
location: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
2982
|
-
company: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
2983
|
-
jobTitle: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
2984
|
-
metadata: _sinclair_typebox.TOptional<_sinclair_typebox.TRecord<_sinclair_typebox.TString, _sinclair_typebox.TAny>>;
|
|
3653
|
+
registeredIp: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
|
|
3654
|
+
name: string;
|
|
3655
|
+
tableName: "user_public_keys";
|
|
3656
|
+
dataType: "string";
|
|
3657
|
+
data: string;
|
|
3658
|
+
driverParam: string;
|
|
3659
|
+
notNull: false;
|
|
3660
|
+
hasDefault: false;
|
|
3661
|
+
isPrimaryKey: false;
|
|
3662
|
+
isAutoincrement: false;
|
|
3663
|
+
hasRuntimeDefault: false;
|
|
3664
|
+
enumValues: undefined;
|
|
3665
|
+
identity: undefined;
|
|
3666
|
+
generated: undefined;
|
|
2985
3667
|
}>;
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
3668
|
+
registeredUserAgent: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
|
|
3669
|
+
name: string;
|
|
3670
|
+
tableName: "user_public_keys";
|
|
3671
|
+
dataType: "string";
|
|
3672
|
+
data: string;
|
|
3673
|
+
driverParam: string;
|
|
3674
|
+
notNull: false;
|
|
3675
|
+
hasDefault: false;
|
|
3676
|
+
isPrimaryKey: false;
|
|
3677
|
+
isAutoincrement: false;
|
|
3678
|
+
hasRuntimeDefault: false;
|
|
3679
|
+
enumValues: undefined;
|
|
3680
|
+
identity: undefined;
|
|
3681
|
+
generated: undefined;
|
|
2990
3682
|
}>;
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
3683
|
+
registeredUaFamily: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
|
|
3684
|
+
name: string;
|
|
3685
|
+
tableName: "user_public_keys";
|
|
3686
|
+
dataType: "string";
|
|
3687
|
+
data: string;
|
|
3688
|
+
driverParam: string;
|
|
3689
|
+
notNull: false;
|
|
3690
|
+
hasDefault: false;
|
|
3691
|
+
isPrimaryKey: false;
|
|
3692
|
+
isAutoincrement: false;
|
|
3693
|
+
hasRuntimeDefault: false;
|
|
3694
|
+
enumValues: undefined;
|
|
3695
|
+
identity: undefined;
|
|
3696
|
+
generated: undefined;
|
|
2997
3697
|
}>;
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
emailVerifiedAt: Date | null;
|
|
3013
|
-
phoneVerifiedAt: Date | null;
|
|
3014
|
-
keyEpoch: number;
|
|
3015
|
-
lastLoginAt: Date | null;
|
|
3016
|
-
}>;
|
|
3017
|
-
updateLocale: _spfn_core_route.RouteDef<{
|
|
3018
|
-
body: _sinclair_typebox.TObject<{
|
|
3019
|
-
locale: _sinclair_typebox.TString;
|
|
3698
|
+
lastSeenIp: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
|
|
3699
|
+
name: string;
|
|
3700
|
+
tableName: "user_public_keys";
|
|
3701
|
+
dataType: "string";
|
|
3702
|
+
data: string;
|
|
3703
|
+
driverParam: string;
|
|
3704
|
+
notNull: false;
|
|
3705
|
+
hasDefault: false;
|
|
3706
|
+
isPrimaryKey: false;
|
|
3707
|
+
isAutoincrement: false;
|
|
3708
|
+
hasRuntimeDefault: false;
|
|
3709
|
+
enumValues: undefined;
|
|
3710
|
+
identity: undefined;
|
|
3711
|
+
generated: undefined;
|
|
3020
3712
|
}>;
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3713
|
+
lastSeenAt: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.PgTimestampBuilder, {
|
|
3714
|
+
name: string;
|
|
3715
|
+
tableName: "user_public_keys";
|
|
3716
|
+
dataType: "object date";
|
|
3717
|
+
data: Date;
|
|
3718
|
+
driverParam: string;
|
|
3719
|
+
notNull: false;
|
|
3720
|
+
hasDefault: false;
|
|
3721
|
+
isPrimaryKey: false;
|
|
3722
|
+
isAutoincrement: false;
|
|
3723
|
+
hasRuntimeDefault: false;
|
|
3724
|
+
enumValues: undefined;
|
|
3725
|
+
identity: undefined;
|
|
3726
|
+
generated: undefined;
|
|
3027
3727
|
}>;
|
|
3028
|
-
|
|
3029
|
-
roles: {
|
|
3030
|
-
description: string | null;
|
|
3728
|
+
concurrentUseAt: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.PgTimestampBuilder, {
|
|
3031
3729
|
name: string;
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
name: _sinclair_typebox.TString;
|
|
3045
|
-
displayName: _sinclair_typebox.TString;
|
|
3046
|
-
description: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3047
|
-
priority: _sinclair_typebox.TOptional<_sinclair_typebox.TNumber>;
|
|
3048
|
-
permissionIds: _sinclair_typebox.TOptional<_sinclair_typebox.TArray<_sinclair_typebox.TNumber>>;
|
|
3730
|
+
tableName: "user_public_keys";
|
|
3731
|
+
dataType: "object date";
|
|
3732
|
+
data: Date;
|
|
3733
|
+
driverParam: string;
|
|
3734
|
+
notNull: false;
|
|
3735
|
+
hasDefault: false;
|
|
3736
|
+
isPrimaryKey: false;
|
|
3737
|
+
isAutoincrement: false;
|
|
3738
|
+
hasRuntimeDefault: false;
|
|
3739
|
+
enumValues: undefined;
|
|
3740
|
+
identity: undefined;
|
|
3741
|
+
generated: undefined;
|
|
3049
3742
|
}>;
|
|
3050
|
-
|
|
3051
|
-
role: {
|
|
3052
|
-
description: string | null;
|
|
3743
|
+
binding: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<["none", "passkey"] & [string, ...string[]]>>>, {
|
|
3053
3744
|
name: string;
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
id: _sinclair_typebox.TNumber;
|
|
3745
|
+
tableName: "user_public_keys";
|
|
3746
|
+
dataType: "string enum";
|
|
3747
|
+
data: "none" | "passkey";
|
|
3748
|
+
driverParam: string;
|
|
3749
|
+
notNull: true;
|
|
3750
|
+
hasDefault: true;
|
|
3751
|
+
isPrimaryKey: false;
|
|
3752
|
+
isAutoincrement: false;
|
|
3753
|
+
hasRuntimeDefault: false;
|
|
3754
|
+
enumValues: ["none", "passkey"] & [string, ...string[]];
|
|
3755
|
+
identity: undefined;
|
|
3756
|
+
generated: undefined;
|
|
3067
3757
|
}>;
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3758
|
+
clientKind: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.PgTextBuilder<["web", "ios", "android"] & [string, ...string[]]>, {
|
|
3759
|
+
name: string;
|
|
3760
|
+
tableName: "user_public_keys";
|
|
3761
|
+
dataType: "string enum";
|
|
3762
|
+
data: "ios" | "android" | "web";
|
|
3763
|
+
driverParam: string;
|
|
3764
|
+
notNull: false;
|
|
3765
|
+
hasDefault: false;
|
|
3766
|
+
isPrimaryKey: false;
|
|
3767
|
+
isAutoincrement: false;
|
|
3768
|
+
hasRuntimeDefault: false;
|
|
3769
|
+
enumValues: ["web", "ios", "android"] & [string, ...string[]];
|
|
3770
|
+
identity: undefined;
|
|
3771
|
+
generated: undefined;
|
|
3073
3772
|
}>;
|
|
3074
|
-
|
|
3075
|
-
role: {
|
|
3076
|
-
description: string | null;
|
|
3773
|
+
clientVersion: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
|
|
3077
3774
|
name: string;
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
id: _sinclair_typebox.TNumber;
|
|
3775
|
+
tableName: "user_public_keys";
|
|
3776
|
+
dataType: "string";
|
|
3777
|
+
data: string;
|
|
3778
|
+
driverParam: string;
|
|
3779
|
+
notNull: false;
|
|
3780
|
+
hasDefault: false;
|
|
3781
|
+
isPrimaryKey: false;
|
|
3782
|
+
isAutoincrement: false;
|
|
3783
|
+
hasRuntimeDefault: false;
|
|
3784
|
+
enumValues: undefined;
|
|
3785
|
+
identity: undefined;
|
|
3786
|
+
generated: undefined;
|
|
3091
3787
|
}>;
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3788
|
+
clientContractVersion: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
|
|
3789
|
+
name: string;
|
|
3790
|
+
tableName: "user_public_keys";
|
|
3791
|
+
dataType: "string";
|
|
3792
|
+
data: string;
|
|
3793
|
+
driverParam: string;
|
|
3794
|
+
notNull: false;
|
|
3795
|
+
hasDefault: false;
|
|
3796
|
+
isPrimaryKey: false;
|
|
3797
|
+
isAutoincrement: false;
|
|
3798
|
+
hasRuntimeDefault: false;
|
|
3799
|
+
enumValues: undefined;
|
|
3800
|
+
identity: undefined;
|
|
3801
|
+
generated: undefined;
|
|
3096
3802
|
}>;
|
|
3097
|
-
|
|
3098
|
-
|
|
3803
|
+
clientSeenAt: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.PgTimestampBuilder, {
|
|
3804
|
+
name: string;
|
|
3805
|
+
tableName: "user_public_keys";
|
|
3806
|
+
dataType: "object date";
|
|
3807
|
+
data: Date;
|
|
3808
|
+
driverParam: string;
|
|
3809
|
+
notNull: false;
|
|
3810
|
+
hasDefault: false;
|
|
3811
|
+
isPrimaryKey: false;
|
|
3812
|
+
isAutoincrement: false;
|
|
3813
|
+
hasRuntimeDefault: false;
|
|
3814
|
+
enumValues: undefined;
|
|
3815
|
+
identity: undefined;
|
|
3816
|
+
generated: undefined;
|
|
3099
3817
|
}>;
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3818
|
+
isActive: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgBooleanBuilder>>, {
|
|
3819
|
+
name: string;
|
|
3820
|
+
tableName: "user_public_keys";
|
|
3821
|
+
dataType: "boolean";
|
|
3822
|
+
data: boolean;
|
|
3823
|
+
driverParam: boolean;
|
|
3824
|
+
notNull: true;
|
|
3825
|
+
hasDefault: true;
|
|
3826
|
+
isPrimaryKey: false;
|
|
3827
|
+
isAutoincrement: false;
|
|
3828
|
+
hasRuntimeDefault: false;
|
|
3829
|
+
enumValues: undefined;
|
|
3830
|
+
identity: undefined;
|
|
3831
|
+
generated: undefined;
|
|
3109
3832
|
}>;
|
|
3110
|
-
|
|
3111
|
-
token: string;
|
|
3112
|
-
opsToken: {
|
|
3113
|
-
id: number;
|
|
3833
|
+
createdAt: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTimestampBuilder>>, {
|
|
3114
3834
|
name: string;
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3835
|
+
tableName: "user_public_keys";
|
|
3836
|
+
dataType: "object date";
|
|
3837
|
+
data: Date;
|
|
3838
|
+
driverParam: string;
|
|
3839
|
+
notNull: true;
|
|
3840
|
+
hasDefault: true;
|
|
3841
|
+
isPrimaryKey: false;
|
|
3842
|
+
isAutoincrement: false;
|
|
3843
|
+
hasRuntimeDefault: false;
|
|
3844
|
+
enumValues: undefined;
|
|
3845
|
+
identity: undefined;
|
|
3846
|
+
generated: undefined;
|
|
3847
|
+
}>;
|
|
3848
|
+
lastUsedAt: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.PgTimestampBuilder, {
|
|
3125
3849
|
name: string;
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3850
|
+
tableName: "user_public_keys";
|
|
3851
|
+
dataType: "object date";
|
|
3852
|
+
data: Date;
|
|
3853
|
+
driverParam: string;
|
|
3854
|
+
notNull: false;
|
|
3855
|
+
hasDefault: false;
|
|
3856
|
+
isPrimaryKey: false;
|
|
3857
|
+
isAutoincrement: false;
|
|
3858
|
+
hasRuntimeDefault: false;
|
|
3859
|
+
enumValues: undefined;
|
|
3860
|
+
identity: undefined;
|
|
3861
|
+
generated: undefined;
|
|
3136
3862
|
}>;
|
|
3137
|
-
|
|
3138
|
-
opsToken: {
|
|
3139
|
-
id: number;
|
|
3863
|
+
expiresAt: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.PgTimestampBuilder, {
|
|
3140
3864
|
name: string;
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
code_challenge: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3154
|
-
code_challenge_method: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3155
|
-
resource: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3156
|
-
scope: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3157
|
-
state: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
3865
|
+
tableName: "user_public_keys";
|
|
3866
|
+
dataType: "object date";
|
|
3867
|
+
data: Date;
|
|
3868
|
+
driverParam: string;
|
|
3869
|
+
notNull: false;
|
|
3870
|
+
hasDefault: false;
|
|
3871
|
+
isPrimaryKey: false;
|
|
3872
|
+
isAutoincrement: false;
|
|
3873
|
+
hasRuntimeDefault: false;
|
|
3874
|
+
enumValues: undefined;
|
|
3875
|
+
identity: undefined;
|
|
3876
|
+
generated: undefined;
|
|
3158
3877
|
}>;
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3878
|
+
revokedAt: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.PgTimestampBuilder, {
|
|
3879
|
+
name: string;
|
|
3880
|
+
tableName: "user_public_keys";
|
|
3881
|
+
dataType: "object date";
|
|
3882
|
+
data: Date;
|
|
3883
|
+
driverParam: string;
|
|
3884
|
+
notNull: false;
|
|
3885
|
+
hasDefault: false;
|
|
3886
|
+
isPrimaryKey: false;
|
|
3887
|
+
isAutoincrement: false;
|
|
3888
|
+
hasRuntimeDefault: false;
|
|
3889
|
+
enumValues: undefined;
|
|
3890
|
+
identity: undefined;
|
|
3891
|
+
generated: undefined;
|
|
3170
3892
|
}>;
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3893
|
+
revokedReason: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
|
|
3894
|
+
name: string;
|
|
3895
|
+
tableName: "user_public_keys";
|
|
3896
|
+
dataType: "string";
|
|
3897
|
+
data: string;
|
|
3898
|
+
driverParam: string;
|
|
3899
|
+
notNull: false;
|
|
3900
|
+
hasDefault: false;
|
|
3901
|
+
isPrimaryKey: false;
|
|
3902
|
+
isAutoincrement: false;
|
|
3903
|
+
hasRuntimeDefault: false;
|
|
3904
|
+
enumValues: undefined;
|
|
3905
|
+
identity: undefined;
|
|
3906
|
+
generated: undefined;
|
|
3180
3907
|
}>;
|
|
3181
|
-
}
|
|
3182
|
-
|
|
3183
|
-
}>;
|
|
3184
|
-
oauth2AuthorizationServerMetadata: _spfn_core_route.RouteDef<{}, {}, Response>;
|
|
3908
|
+
};
|
|
3909
|
+
dialect: "pg";
|
|
3185
3910
|
}>;
|
|
3911
|
+
type UserPublicKey = typeof userPublicKeys.$inferSelect;
|
|
3912
|
+
type NewUserPublicKey = typeof userPublicKeys.$inferInsert;
|
|
3186
3913
|
|
|
3187
3914
|
/**
|
|
3188
3915
|
* The auth-profile registry the authenticate middleware dispatches on.
|
|
@@ -3323,6 +4050,46 @@ declare module 'hono' {
|
|
|
3323
4050
|
auth: AuthContext;
|
|
3324
4051
|
}
|
|
3325
4052
|
}
|
|
4053
|
+
/** Why a Bearer credential was not admitted, in the order the steps run. */
|
|
4054
|
+
type BearerRefusal = 'absent' | 'machine' | 'undecodable' | 'unknown' | 'expired' | 'token-expired' | 'bad-signature' | 'unverifiable';
|
|
4055
|
+
/** What a Bearer credential resolved to: the key row it named, or why not. */
|
|
4056
|
+
type BearerOutcome = {
|
|
4057
|
+
key: UserPublicKey;
|
|
4058
|
+
} | {
|
|
4059
|
+
refused: BearerRefusal;
|
|
4060
|
+
};
|
|
4061
|
+
/**
|
|
4062
|
+
* Admit the Bearer credential on this request, or say what stopped it.
|
|
4063
|
+
*
|
|
4064
|
+
* The one lookup-and-verify path every Bearer middleware takes — header, machine
|
|
4065
|
+
* discriminator, decode, key row, expiry, signature — kept in one place because
|
|
4066
|
+
* the order is itself a rule: a machine credential never reaches a decode, and a
|
|
4067
|
+
* key row is found before its signature is checked so that an unknown key and a
|
|
4068
|
+
* forged one cost the same work.
|
|
4069
|
+
*
|
|
4070
|
+
* It answers rather than throws, which is what lets two middlewares share it.
|
|
4071
|
+
* `authenticate` turns each refusal into the error that step has always
|
|
4072
|
+
* answered with; `authenticateForRenewal` turns every one of them into a single
|
|
4073
|
+
* refusal, so a caller holding no private key cannot tell a live key id from a
|
|
4074
|
+
* dead one. A shared step that threw would have to be unwound to get there.
|
|
4075
|
+
*
|
|
4076
|
+
* @param c - the Hono context of the request being authenticated
|
|
4077
|
+
* @param admitsExpired - whether a key past its `expiresAt` may still be
|
|
4078
|
+
* admitted. Renewal is the only caller that says yes, and only for a bound key
|
|
4079
|
+
* inside its grace.
|
|
4080
|
+
*/
|
|
4081
|
+
declare function admitBearerKey(c: Context, admitsExpired: (key: UserPublicKey) => boolean): Promise<BearerOutcome>;
|
|
4082
|
+
/**
|
|
4083
|
+
* The principal a verified Bearer key resolves to.
|
|
4084
|
+
*
|
|
4085
|
+
* The one place the Bearer path builds an `AuthContext`, so that a middleware
|
|
4086
|
+
* added beside `authenticate` cannot invent a second shape of it.
|
|
4087
|
+
*/
|
|
4088
|
+
declare function bearerAuthContext(keyId: string, resolved: {
|
|
4089
|
+
user: User;
|
|
4090
|
+
role: string | null;
|
|
4091
|
+
locale: string;
|
|
4092
|
+
}): AuthContext;
|
|
3326
4093
|
/**
|
|
3327
4094
|
* Authentication middleware
|
|
3328
4095
|
*
|
|
@@ -3476,4 +4243,4 @@ declare const machineAuth: _spfn_core_route.NamedMiddleware<"machineAuth">;
|
|
|
3476
4243
|
*/
|
|
3477
4244
|
declare const requireMachineScope: _spfn_core_route.NamedMiddlewareFactory<"machineScope", string[]>;
|
|
3478
4245
|
|
|
3479
|
-
export { type
|
|
4246
|
+
export { type AuthLoginPayload as $, type AuthInitOptions as A, type Passkey as B, type ConfirmSignupLinkResult as C, type DeviceAuthInfoResult as D, type MfaVerification as E, type FinishPasskeyEnrollmentResult as F, type MfaVerificationMethod as G, type UserPublicKey as H, type IssueOneTimeTokenResult as I, type AuthContext as J, type KeySummary as K, type LoginBindingFields as L, type MfaStatus as M, type NewUserPublicKey as N, type OAuthStartResult as O, type PermissionConfig as P, type ApproveDeviceAuthParams as Q, type RoleConfig as R, type SendVerificationCodeResult as S, type TotpEnrolmentResult as T, type UserProfile as U, VERIFICATION_PURPOSES as V, type AssertStepUpParams as W, type AuthDeletionCancelledPayload as X, type AuthDeletionCompletedPayload as Y, type AuthDeletionRequestedPayload as Z, type AuthDeviceRegisteredPayload as _, type RegisterResult as a, type RevokeAllKeysParams as a$, type AuthPasswordResetPayload as a0, type AuthProfileOutcome as a1, type AuthProfileVerifier as a2, AuthProviderSchema as a3, type AuthRegisterPayload as a4, type BearerOutcome as a5, type BearerRefusal as a6, type ChangePasswordParams as a7, type CompletePasswordResetParams as a8, type CompleteSignupParams as a9, type NativeVerifyOptions as aA, type NewMfaVerification as aB, type NormalizedIdentity as aC, type OAuth2AuthorizeParams as aD, type OAuth2ScopeDescription as aE, type OAuthCallbackParams as aF, type OAuthCallbackResult as aG, type OAuthCodeExchangeOptions as aH, type OAuthNativeParams as aI, type OAuthStartParams as aJ, type OAuthTokens as aK, type OAuthUnlinkedPayload as aL, PASSKEY_DEVICE_TYPES as aM, PASSKEY_LABEL_MAX_LENGTH as aN, type PasskeyDeviceType as aO, PasswordSchema as aP, PhoneSchema as aQ, PlatformSchema as aR, type PollDeviceAuthParams as aS, type PollDeviceAuthResult as aT, PublicKeySchema as aU, type RecentAuthenticationParams as aV, type RegisterParams as aW, type RegisterPublicKeyParams as aX, type RenamePasskeyParams as aY, type RequestPasswordResetParams as aZ, type RequestSignupLinkParams as a_, type ConfirmPasswordResetParams as aa, type ConfirmSignupLinkParams as ab, type ConfirmTotpParams as ac, type DenyDeviceAuthParams as ad, type DeviceAuthApprovedResult as ae, type DeviceAuthInfoParams as af, type DeviceAuthPendingResult as ag, DeviceAuthPollResponseSchema as ah, DeviceNameSchema as ai, type DeviceRegistrationChannel as aj, type DisableSessionBindingParams as ak, EmailSchema as al, FingerprintSchema as am, type FinishPasskeyEnrollmentParams as an, type FinishPasskeyLoginParams as ao, type FinishSessionRenewParams as ap, type InvitationAcceptedPayload as aq, type InvitationCreatedPayload as ar, KEY_FINGERPRINT_PREFIX_LENGTH as as, KeyIdSchema as at, type LoginParams as au, type LogoutParams as av, MFA_VERIFICATION_METHODS as aw, type MachinePrincipal as ax, type MachineVerifierRegistration as ay, type MarkPasskeyParams as az, type RequestSignupLinkResult as b, listOAuth2GrantsService as b$, type RevokeKeyParams as b0, type RevokePasskeyParams as b1, type RotateKeyParams as b2, type SendVerificationCodeParams as b3, type SessionBindingParams as b4, type StartDeviceAuthParams as b5, type StartPasskeyEnrollmentParams as b6, type StartSessionRenewParams as b7, type StepUpParams as b8, TargetTypeSchema as b9, completePasswordResetService as bA, completeSignupService as bB, confirmPasswordResetService as bC, confirmSignupLinkService as bD, confirmTotpEnrolmentService as bE, denyDeviceAuthService as bF, denyOAuth2AuthorizeService as bG, describeOAuth2AuthorizeRequestService as bH, disableMfaService as bI, disableSessionBindingService as bJ, enableSessionBindingService as bK, finishPasskeyEnrollmentService as bL, finishPasskeyLoginService as bM, finishSessionRenewService as bN, getDeviceAuthInfoService as bO, getEnabledOAuthProviders as bP, getGoogleAccessToken as bQ, getMachinePrincipal as bR, getOAuthProvider as bS, getRegisteredProviders as bT, getSessionBindingService as bU, invitationAcceptedEvent as bV, invitationCreatedEvent as bW, isOAuthProviderEnabled as bX, issueOneTimeTokenService as bY, keySessionBindingService as bZ, listKeysService as b_, type UnlinkNotification as ba, UnlinkNotifyRejection as bb, type UnlinkNotifyRequest as bc, type UnlinkNotifyResult as bd, UserCodeSchema as be, VerificationPurposeSchema as bf, type VerifyCodeParams as bg, type VerifyCodeResult as bh, admitBearerKey as bi, approveDeviceAuthService as bj, approveOAuth2AuthorizeService as bk, assertNotLastRecoveryCredential as bl, assertRecentAuthentication as bm, assertStepUp 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, carryStepUpVerification as by, changePasswordService as bz, type RequestPasswordResetResult as c, listPasskeysService as c0, loginService as c1, logoutService as c2, machineAuth as c3, markPasskeySecondFactorService as c4, mfaEnrolledForUser as c5, mfaStatusService as c6, mfaVerifications as c7, oauthCallbackService as c8, oauthNativeService as c9, sendVerificationCodeService as cA, startDeviceAuthService as cB, startPasskeyEnrollmentService as cC, startPasskeyLoginService as cD, startSessionBindingDisableService as cE, startSessionRenewService as cF, startStepUpService as cG, startTotpEnrolmentService as cH, stepUpService as cI, sweepUnconfirmedMfaService as cJ, userPublicKeys as cK, verifyCodeService as cL, verifyOneTimeTokenService as cM, verifySecondFactor as cN, oauthStartService as ca, oauthUnlinkNotifyService as cb, oauthUnlinkedEvent as cc, optionalAuth as cd, passkeys as ce, pollDeviceAuthService as cf, regenerateRecoveryCodesService as cg, registerAuthProfile as ch, registerMachineVerifier as ci, registerOAuthProvider as cj, registerPublicKeyService as ck, registerService as cl, renamePasskeyService as cm, requestPasswordResetService as cn, requestSignupLinkService as co, requireEnabledProvider as cp, requireMachineScope as cq, resolveAuthenticatedUser as cr, revokeAllKeysService as cs, revokeAllOAuth2GrantsForUser as ct, revokeKeyService as cu, revokeOAuth2GrantService as cv, revokePasskeyService as cw, rotateKeyService as cx, runAuthProfile as cy, selectAuthProfile as cz, type ConfirmPasswordResetResult as d, type LoginResult as e, type StartDeviceAuthResult as f, type PasskeySummary as g, type ConfirmTotpResult as h, type RotateKeyResult as i, type RevokeAllKeysResult as j, type SessionBindingResult as k, type SessionRenewResult as l, mainAuthRouter as m, type OAuthNativeResult as n, type ProfileInfo as o, type OAuth2ConsentView as p, type OAuth2AuthorizationCodeIssued as q, type OAuth2GrantSummary as r, type AuthSession as s, PERMISSION_CATEGORIES as t, type PermissionCategory as u, VERIFICATION_TARGET_TYPES as v, type VerificationPurpose as w, type VerificationTargetType as x, type OAuthProvider as y, type NewPasskey as z };
|