@spfn/auth 0.3.0-beta.6 → 0.3.0-beta.8
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 +442 -0
- package/dist/client-proof.d.ts +23 -12
- package/dist/client-proof.js +143 -4
- package/dist/client-proof.js.map +1 -1
- package/dist/config.d.ts +20 -0
- package/dist/config.js +9 -0
- package/dist/config.js.map +1 -1
- package/dist/errors.d.ts +76 -3
- package/dist/errors.js +45 -0
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +42 -2
- package/dist/index.js +75 -0
- package/dist/index.js.map +1 -1
- package/dist/{authenticate-98lBIMxP.d.ts → machine-principals-Bd5hp76H.d.ts} +394 -4
- package/dist/nextjs/api.js +190 -9
- package/dist/nextjs/api.js.map +1 -1
- package/dist/nextjs/server.js +59 -8
- package/dist/nextjs/server.js.map +1 -1
- package/dist/server.d.ts +646 -16
- package/dist/server.js +1401 -441
- package/dist/server.js.map +1 -1
- package/migrations/20260901091716_fine_arclight/migration.sql +21 -0
- package/migrations/20260901091716_fine_arclight/snapshot.json +3849 -0
- package/package.json +2 -2
package/dist/server.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as AuthInitOptions,
|
|
2
|
-
export {
|
|
1
|
+
import { A as AuthInitOptions, o as OAuthProvider, l as VerificationPurpose, j as PermissionCategory, p as AuthContext } from './machine-principals-Bd5hp76H.js';
|
|
2
|
+
export { q as ApproveDeviceAuthParams, r as AuthProfileOutcome, s as AuthProfileVerifier, t as ChangePasswordParams, u as CompleteSignupParams, v as ConfirmSignupLinkParams, C as ConfirmSignupLinkResult, w as DenyDeviceAuthParams, x as DeviceAuthApprovedResult, y as DeviceAuthInfoParams, D as DeviceAuthInfoResult, z as DeviceAuthPendingResult, B as DeviceAuthPollResponseSchema, E as DeviceNameSchema, F as EmailSchema, G as FingerprintSchema, I as IssueOneTimeTokenResult, H as KEY_FINGERPRINT_PREFIX_LENGTH, J as KeyIdSchema, K as KeySummary, M as LoginParams, L as LoginResult, N as LogoutParams, Q as MachinePrincipal, T as MachineVerifierRegistration, W as NativeVerifyOptions, X as NormalizedIdentity, Y as OAuthCallbackParams, Z as OAuthCallbackResult, _ as OAuthCodeExchangeOptions, $ as OAuthNativeParams, f as OAuthNativeResult, a0 as OAuthStartParams, O as OAuthStartResult, a1 as OAuthTokens, a2 as PasswordSchema, a3 as PhoneSchema, a4 as PlatformSchema, a5 as PollDeviceAuthParams, a6 as PollDeviceAuthResult, a7 as PublicKeySchema, a8 as RegisterParams, a9 as RegisterPublicKeyParams, a as RegisterResult, aa as RequestSignupLinkParams, b as RequestSignupLinkResult, ab as RevokeAllKeysParams, e as RevokeAllKeysResult, ac as RevokeKeyParams, ad as RotateKeyParams, d as RotateKeyResult, ae as SendVerificationCodeParams, S as SendVerificationCodeResult, af as StartDeviceAuthParams, c as StartDeviceAuthResult, ag as TargetTypeSchema, ah as UnlinkNotification, ai as UnlinkNotifyRejection, aj as UnlinkNotifyRequest, ak as UnlinkNotifyResult, al as UserCodeSchema, V as VERIFICATION_PURPOSES, k as VERIFICATION_TARGET_TYPES, am as VerificationPurposeSchema, n as VerificationTargetType, an as VerifyCodeParams, ao as VerifyCodeResult, ap as approveDeviceAuthService, m as authRouter, aq as authenticate, ar as buildOAuthErrorUrl, as as changePasswordService, at as completeSignupService, au as confirmSignupLinkService, av as denyDeviceAuthService, aw as getDeviceAuthInfoService, ax as getEnabledOAuthProviders, ay as getGoogleAccessToken, az as getMachinePrincipal, aA as getOAuthProvider, aB as getRegisteredProviders, aC as isOAuthProviderEnabled, aD as isSafeReturnPath, aE as issueOneTimeTokenService, aF as listKeysService, aG as loginService, aH as logoutService, aI as machineAuth, aJ as oauthCallbackService, aK as oauthNativeService, aL as oauthStartService, aM as oauthUnlinkNotifyService, aN as optionalAuth, aO as pollDeviceAuthService, aP as registerAuthProfile, aQ as registerMachineVerifier, aR as registerOAuthProvider, aS as registerPublicKeyService, aT as registerService, aU as requestSignupLinkService, aV as requireEnabledProvider, aW as requireMachineScope, aX as resolveAuthenticatedUser, aY as revokeAllKeysService, aZ as revokeKeyService, a_ as rotateKeyService, a$ as runAuthProfile, b0 as selectAuthProfile, b1 as sendVerificationCodeService, b2 as startDeviceAuthService, b3 as verifyCodeService, b4 as verifyOneTimeTokenService } from './machine-principals-Bd5hp76H.js';
|
|
3
3
|
import * as drizzle_orm_pg_core from 'drizzle-orm/pg-core';
|
|
4
4
|
import { K as KeyAlgorithmType, d as InvitationStatus, j as SocialProvider, c as AccountDeletionRequestedBy, i as PurgeStrategy } from './types-DYyhze28.js';
|
|
5
5
|
export { A as ACCOUNT_DELETION_REQUESTED_BY, a as ACCOUNT_DELETION_REQUEST_STATUSES, b as AccountDeletionRequestStatus, I as INVITATION_STATUSES, e as KEY_ALGORITHM, f as KEY_DEVICE_NAME_MAX_LENGTH, g as KEY_PLATFORM, h as KeyPlatformType, P as PURGE_STRATEGIES, S as SOCIAL_PROVIDERS, U as USER_STATUSES, k as UserStatus } from './types-DYyhze28.js';
|
|
@@ -3296,6 +3296,288 @@ declare const signupLinkTokens: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
3296
3296
|
type SignupLinkToken = typeof signupLinkTokens.$inferSelect;
|
|
3297
3297
|
type NewSignupLinkToken = typeof signupLinkTokens.$inferInsert;
|
|
3298
3298
|
|
|
3299
|
+
/**
|
|
3300
|
+
* @spfn/auth - Device Authorizations Entity
|
|
3301
|
+
*
|
|
3302
|
+
* Backs device-code login: a device with no key on file yet parks its public key
|
|
3303
|
+
* here, shows a short code, and an already-authenticated device approves it. The
|
|
3304
|
+
* parked key is registered against the approving user's account when the waiting
|
|
3305
|
+
* device polls.
|
|
3306
|
+
*
|
|
3307
|
+
* The pending key material duplicates the `user_public_keys` columns rather than
|
|
3308
|
+
* writing a row there early. A key in `user_public_keys` can sign requests, and
|
|
3309
|
+
* nothing about a row here has been approved yet — one table means "registered"
|
|
3310
|
+
* and the other means "asked to be", and neither is a state of the other.
|
|
3311
|
+
*
|
|
3312
|
+
* Nothing sweeps this table. Rows are judged by `expiresAt` on read, so a stale
|
|
3313
|
+
* row cannot authorize anything; it only keeps its user code out of circulation,
|
|
3314
|
+
* and 31^8 codes do not run out.
|
|
3315
|
+
*/
|
|
3316
|
+
/**
|
|
3317
|
+
* Lifecycle of one device-code request.
|
|
3318
|
+
*
|
|
3319
|
+
* Every transition is one-way and the two terminal states differ: `denied` is a
|
|
3320
|
+
* refusal the waiting device is told about, `consumed` is a completed login that
|
|
3321
|
+
* answers as if it never existed. `pending -> approved -> consumed` is the only
|
|
3322
|
+
* path to a registered key.
|
|
3323
|
+
*
|
|
3324
|
+
* `denied` is also where a global revocation puts a record — a revoke-all, a
|
|
3325
|
+
* password change, a deletion request — including one already approved. The
|
|
3326
|
+
* account has withdrawn it, and what the waiting device is owed for that is the
|
|
3327
|
+
* answer a refusal gives.
|
|
3328
|
+
*/
|
|
3329
|
+
declare const DEVICE_AUTH_STATUSES: readonly ["pending", "approved", "denied", "consumed"];
|
|
3330
|
+
type DeviceAuthStatus = typeof DEVICE_AUTH_STATUSES[number];
|
|
3331
|
+
declare const deviceAuthorizations: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
3332
|
+
name: "device_authorizations";
|
|
3333
|
+
schema: string;
|
|
3334
|
+
columns: {
|
|
3335
|
+
createdAt: drizzle_orm_pg_core.PgBuildColumn<"device_authorizations", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>, {
|
|
3336
|
+
name: string;
|
|
3337
|
+
tableName: "device_authorizations";
|
|
3338
|
+
dataType: "object date";
|
|
3339
|
+
data: Date;
|
|
3340
|
+
driverParam: string;
|
|
3341
|
+
notNull: true;
|
|
3342
|
+
hasDefault: true;
|
|
3343
|
+
isPrimaryKey: false;
|
|
3344
|
+
isAutoincrement: false;
|
|
3345
|
+
hasRuntimeDefault: false;
|
|
3346
|
+
enumValues: undefined;
|
|
3347
|
+
identity: undefined;
|
|
3348
|
+
generated: undefined;
|
|
3349
|
+
}>;
|
|
3350
|
+
updatedAt: drizzle_orm_pg_core.PgBuildColumn<"device_authorizations", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>>, {
|
|
3351
|
+
name: string;
|
|
3352
|
+
tableName: "device_authorizations";
|
|
3353
|
+
dataType: "object date";
|
|
3354
|
+
data: Date;
|
|
3355
|
+
driverParam: string;
|
|
3356
|
+
notNull: true;
|
|
3357
|
+
hasDefault: true;
|
|
3358
|
+
isPrimaryKey: false;
|
|
3359
|
+
isAutoincrement: false;
|
|
3360
|
+
hasRuntimeDefault: false;
|
|
3361
|
+
enumValues: undefined;
|
|
3362
|
+
identity: undefined;
|
|
3363
|
+
generated: undefined;
|
|
3364
|
+
}>;
|
|
3365
|
+
id: drizzle_orm_pg_core.PgBuildColumn<"device_authorizations", drizzle_orm_pg_core.SetIsPrimaryKey<drizzle_orm_pg_core.PgBigSerial53Builder>, {
|
|
3366
|
+
name: string;
|
|
3367
|
+
tableName: "device_authorizations";
|
|
3368
|
+
dataType: "number int53";
|
|
3369
|
+
data: number;
|
|
3370
|
+
driverParam: number;
|
|
3371
|
+
notNull: true;
|
|
3372
|
+
hasDefault: true;
|
|
3373
|
+
isPrimaryKey: false;
|
|
3374
|
+
isAutoincrement: false;
|
|
3375
|
+
hasRuntimeDefault: false;
|
|
3376
|
+
enumValues: undefined;
|
|
3377
|
+
identity: undefined;
|
|
3378
|
+
generated: undefined;
|
|
3379
|
+
}>;
|
|
3380
|
+
deviceCodeHash: drizzle_orm_pg_core.PgBuildColumn<"device_authorizations", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
|
|
3381
|
+
name: string;
|
|
3382
|
+
tableName: "device_authorizations";
|
|
3383
|
+
dataType: "string";
|
|
3384
|
+
data: string;
|
|
3385
|
+
driverParam: string;
|
|
3386
|
+
notNull: true;
|
|
3387
|
+
hasDefault: false;
|
|
3388
|
+
isPrimaryKey: false;
|
|
3389
|
+
isAutoincrement: false;
|
|
3390
|
+
hasRuntimeDefault: false;
|
|
3391
|
+
enumValues: undefined;
|
|
3392
|
+
identity: undefined;
|
|
3393
|
+
generated: undefined;
|
|
3394
|
+
}>;
|
|
3395
|
+
userCode: drizzle_orm_pg_core.PgBuildColumn<"device_authorizations", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
|
|
3396
|
+
name: string;
|
|
3397
|
+
tableName: "device_authorizations";
|
|
3398
|
+
dataType: "string";
|
|
3399
|
+
data: string;
|
|
3400
|
+
driverParam: string;
|
|
3401
|
+
notNull: true;
|
|
3402
|
+
hasDefault: false;
|
|
3403
|
+
isPrimaryKey: false;
|
|
3404
|
+
isAutoincrement: false;
|
|
3405
|
+
hasRuntimeDefault: false;
|
|
3406
|
+
enumValues: undefined;
|
|
3407
|
+
identity: undefined;
|
|
3408
|
+
generated: undefined;
|
|
3409
|
+
}>;
|
|
3410
|
+
publicKey: drizzle_orm_pg_core.PgBuildColumn<"device_authorizations", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
|
|
3411
|
+
name: string;
|
|
3412
|
+
tableName: "device_authorizations";
|
|
3413
|
+
dataType: "string";
|
|
3414
|
+
data: string;
|
|
3415
|
+
driverParam: string;
|
|
3416
|
+
notNull: true;
|
|
3417
|
+
hasDefault: false;
|
|
3418
|
+
isPrimaryKey: false;
|
|
3419
|
+
isAutoincrement: false;
|
|
3420
|
+
hasRuntimeDefault: false;
|
|
3421
|
+
enumValues: undefined;
|
|
3422
|
+
identity: undefined;
|
|
3423
|
+
generated: undefined;
|
|
3424
|
+
}>;
|
|
3425
|
+
keyId: drizzle_orm_pg_core.PgBuildColumn<"device_authorizations", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
|
|
3426
|
+
name: string;
|
|
3427
|
+
tableName: "device_authorizations";
|
|
3428
|
+
dataType: "string";
|
|
3429
|
+
data: string;
|
|
3430
|
+
driverParam: string;
|
|
3431
|
+
notNull: true;
|
|
3432
|
+
hasDefault: false;
|
|
3433
|
+
isPrimaryKey: false;
|
|
3434
|
+
isAutoincrement: false;
|
|
3435
|
+
hasRuntimeDefault: false;
|
|
3436
|
+
enumValues: undefined;
|
|
3437
|
+
identity: undefined;
|
|
3438
|
+
generated: undefined;
|
|
3439
|
+
}>;
|
|
3440
|
+
fingerprint: drizzle_orm_pg_core.PgBuildColumn<"device_authorizations", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
|
|
3441
|
+
name: string;
|
|
3442
|
+
tableName: "device_authorizations";
|
|
3443
|
+
dataType: "string";
|
|
3444
|
+
data: string;
|
|
3445
|
+
driverParam: string;
|
|
3446
|
+
notNull: true;
|
|
3447
|
+
hasDefault: false;
|
|
3448
|
+
isPrimaryKey: false;
|
|
3449
|
+
isAutoincrement: false;
|
|
3450
|
+
hasRuntimeDefault: false;
|
|
3451
|
+
enumValues: undefined;
|
|
3452
|
+
identity: undefined;
|
|
3453
|
+
generated: undefined;
|
|
3454
|
+
}>;
|
|
3455
|
+
algorithm: drizzle_orm_pg_core.PgBuildColumn<"device_authorizations", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<["ES256", "RS256"] & [string, ...string[]]>>>, {
|
|
3456
|
+
name: string;
|
|
3457
|
+
tableName: "device_authorizations";
|
|
3458
|
+
dataType: "string enum";
|
|
3459
|
+
data: "ES256" | "RS256";
|
|
3460
|
+
driverParam: string;
|
|
3461
|
+
notNull: true;
|
|
3462
|
+
hasDefault: true;
|
|
3463
|
+
isPrimaryKey: false;
|
|
3464
|
+
isAutoincrement: false;
|
|
3465
|
+
hasRuntimeDefault: false;
|
|
3466
|
+
enumValues: ["ES256", "RS256"] & [string, ...string[]];
|
|
3467
|
+
identity: undefined;
|
|
3468
|
+
generated: undefined;
|
|
3469
|
+
}>;
|
|
3470
|
+
deviceName: drizzle_orm_pg_core.PgBuildColumn<"device_authorizations", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
|
|
3471
|
+
name: string;
|
|
3472
|
+
tableName: "device_authorizations";
|
|
3473
|
+
dataType: "string";
|
|
3474
|
+
data: string;
|
|
3475
|
+
driverParam: string;
|
|
3476
|
+
notNull: false;
|
|
3477
|
+
hasDefault: false;
|
|
3478
|
+
isPrimaryKey: false;
|
|
3479
|
+
isAutoincrement: false;
|
|
3480
|
+
hasRuntimeDefault: false;
|
|
3481
|
+
enumValues: undefined;
|
|
3482
|
+
identity: undefined;
|
|
3483
|
+
generated: undefined;
|
|
3484
|
+
}>;
|
|
3485
|
+
platform: drizzle_orm_pg_core.PgBuildColumn<"device_authorizations", drizzle_orm_pg_core.PgTextBuilder<["ios", "android", "web", "desktop"] & [string, ...string[]]>, {
|
|
3486
|
+
name: string;
|
|
3487
|
+
tableName: "device_authorizations";
|
|
3488
|
+
dataType: "string enum";
|
|
3489
|
+
data: "ios" | "android" | "web" | "desktop";
|
|
3490
|
+
driverParam: string;
|
|
3491
|
+
notNull: false;
|
|
3492
|
+
hasDefault: false;
|
|
3493
|
+
isPrimaryKey: false;
|
|
3494
|
+
isAutoincrement: false;
|
|
3495
|
+
hasRuntimeDefault: false;
|
|
3496
|
+
enumValues: ["ios", "android", "web", "desktop"] & [string, ...string[]];
|
|
3497
|
+
identity: undefined;
|
|
3498
|
+
generated: undefined;
|
|
3499
|
+
}>;
|
|
3500
|
+
status: drizzle_orm_pg_core.PgBuildColumn<"device_authorizations", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<["pending", "approved", "denied", "consumed"] & [string, ...string[]]>>>, {
|
|
3501
|
+
name: string;
|
|
3502
|
+
tableName: "device_authorizations";
|
|
3503
|
+
dataType: "string enum";
|
|
3504
|
+
data: "pending" | "approved" | "denied" | "consumed";
|
|
3505
|
+
driverParam: string;
|
|
3506
|
+
notNull: true;
|
|
3507
|
+
hasDefault: true;
|
|
3508
|
+
isPrimaryKey: false;
|
|
3509
|
+
isAutoincrement: false;
|
|
3510
|
+
hasRuntimeDefault: false;
|
|
3511
|
+
enumValues: ["pending", "approved", "denied", "consumed"] & [string, ...string[]];
|
|
3512
|
+
identity: undefined;
|
|
3513
|
+
generated: undefined;
|
|
3514
|
+
}>;
|
|
3515
|
+
userId: drizzle_orm_pg_core.PgBuildColumn<"device_authorizations", drizzle_orm_pg_core.PgBigInt53Builder, {
|
|
3516
|
+
name: string;
|
|
3517
|
+
tableName: "device_authorizations";
|
|
3518
|
+
dataType: "number int53";
|
|
3519
|
+
data: number;
|
|
3520
|
+
driverParam: string | number;
|
|
3521
|
+
notNull: false;
|
|
3522
|
+
hasDefault: false;
|
|
3523
|
+
isPrimaryKey: false;
|
|
3524
|
+
isAutoincrement: false;
|
|
3525
|
+
hasRuntimeDefault: false;
|
|
3526
|
+
enumValues: undefined;
|
|
3527
|
+
identity: undefined;
|
|
3528
|
+
generated: undefined;
|
|
3529
|
+
}>;
|
|
3530
|
+
expiresAt: drizzle_orm_pg_core.PgBuildColumn<"device_authorizations", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTimestampBuilder>, {
|
|
3531
|
+
name: string;
|
|
3532
|
+
tableName: "device_authorizations";
|
|
3533
|
+
dataType: "object date";
|
|
3534
|
+
data: Date;
|
|
3535
|
+
driverParam: string;
|
|
3536
|
+
notNull: true;
|
|
3537
|
+
hasDefault: false;
|
|
3538
|
+
isPrimaryKey: false;
|
|
3539
|
+
isAutoincrement: false;
|
|
3540
|
+
hasRuntimeDefault: false;
|
|
3541
|
+
enumValues: undefined;
|
|
3542
|
+
identity: undefined;
|
|
3543
|
+
generated: undefined;
|
|
3544
|
+
}>;
|
|
3545
|
+
approvedAt: drizzle_orm_pg_core.PgBuildColumn<"device_authorizations", drizzle_orm_pg_core.PgTimestampBuilder, {
|
|
3546
|
+
name: string;
|
|
3547
|
+
tableName: "device_authorizations";
|
|
3548
|
+
dataType: "object date";
|
|
3549
|
+
data: Date;
|
|
3550
|
+
driverParam: string;
|
|
3551
|
+
notNull: false;
|
|
3552
|
+
hasDefault: false;
|
|
3553
|
+
isPrimaryKey: false;
|
|
3554
|
+
isAutoincrement: false;
|
|
3555
|
+
hasRuntimeDefault: false;
|
|
3556
|
+
enumValues: undefined;
|
|
3557
|
+
identity: undefined;
|
|
3558
|
+
generated: undefined;
|
|
3559
|
+
}>;
|
|
3560
|
+
consumedAt: drizzle_orm_pg_core.PgBuildColumn<"device_authorizations", drizzle_orm_pg_core.PgTimestampBuilder, {
|
|
3561
|
+
name: string;
|
|
3562
|
+
tableName: "device_authorizations";
|
|
3563
|
+
dataType: "object date";
|
|
3564
|
+
data: Date;
|
|
3565
|
+
driverParam: string;
|
|
3566
|
+
notNull: false;
|
|
3567
|
+
hasDefault: false;
|
|
3568
|
+
isPrimaryKey: false;
|
|
3569
|
+
isAutoincrement: false;
|
|
3570
|
+
hasRuntimeDefault: false;
|
|
3571
|
+
enumValues: undefined;
|
|
3572
|
+
identity: undefined;
|
|
3573
|
+
generated: undefined;
|
|
3574
|
+
}>;
|
|
3575
|
+
};
|
|
3576
|
+
dialect: "pg";
|
|
3577
|
+
}>;
|
|
3578
|
+
type DeviceAuthorization = typeof deviceAuthorizations.$inferSelect;
|
|
3579
|
+
type NewDeviceAuthorization = typeof deviceAuthorizations.$inferInsert;
|
|
3580
|
+
|
|
3299
3581
|
/**
|
|
3300
3582
|
* @spfn/auth - Permissions Entity
|
|
3301
3583
|
*
|
|
@@ -4026,15 +4308,15 @@ declare class UsersRepository extends BaseRepository {
|
|
|
4026
4308
|
create(data: NewUser): Promise<{
|
|
4027
4309
|
email: string | null;
|
|
4028
4310
|
phone: string | null;
|
|
4029
|
-
|
|
4311
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
4030
4312
|
publicId: string;
|
|
4313
|
+
passwordChangeRequired: boolean;
|
|
4314
|
+
id: number;
|
|
4031
4315
|
username: string | null;
|
|
4032
4316
|
passwordHash: string | null;
|
|
4033
|
-
passwordChangeRequired: boolean;
|
|
4034
4317
|
roleId: number;
|
|
4035
4318
|
createdAt: Date;
|
|
4036
4319
|
updatedAt: Date;
|
|
4037
|
-
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
4038
4320
|
emailVerifiedAt: Date | null;
|
|
4039
4321
|
phoneVerifiedAt: Date | null;
|
|
4040
4322
|
lastLoginAt: Date | null;
|
|
@@ -4186,15 +4468,15 @@ declare class UsersRepository extends BaseRepository {
|
|
|
4186
4468
|
deleteById(id: number): Promise<{
|
|
4187
4469
|
email: string | null;
|
|
4188
4470
|
phone: string | null;
|
|
4189
|
-
|
|
4471
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
4190
4472
|
publicId: string;
|
|
4473
|
+
passwordChangeRequired: boolean;
|
|
4474
|
+
id: number;
|
|
4191
4475
|
username: string | null;
|
|
4192
4476
|
passwordHash: string | null;
|
|
4193
|
-
passwordChangeRequired: boolean;
|
|
4194
4477
|
roleId: number;
|
|
4195
4478
|
createdAt: Date;
|
|
4196
4479
|
updatedAt: Date;
|
|
4197
|
-
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
4198
4480
|
emailVerifiedAt: Date | null;
|
|
4199
4481
|
phoneVerifiedAt: Date | null;
|
|
4200
4482
|
lastLoginAt: Date | null;
|
|
@@ -4813,6 +5095,118 @@ declare class SignupLinkTokensRepository extends BaseRepository {
|
|
|
4813
5095
|
}
|
|
4814
5096
|
declare const signupLinkTokensRepository: SignupLinkTokensRepository;
|
|
4815
5097
|
|
|
5098
|
+
/**
|
|
5099
|
+
* Device Authorizations Repository
|
|
5100
|
+
*
|
|
5101
|
+
* Data access for device-code login. Extends BaseRepository for transaction
|
|
5102
|
+
* context detection and read/write splitting.
|
|
5103
|
+
*
|
|
5104
|
+
* Every state change here is a conditional UPDATE naming the exact state it is
|
|
5105
|
+
* allowed to move from, returning the row only when THIS call was the one that
|
|
5106
|
+
* moved it. That is what makes the operations safe to race: two polls on one
|
|
5107
|
+
* approved record, or a deny arriving while an approve is in flight, produce one
|
|
5108
|
+
* winner and one refusal instead of both callers acting on a row they each read
|
|
5109
|
+
* as still theirs to move. Read-then-write would lose that.
|
|
5110
|
+
*
|
|
5111
|
+
* Each of those transitions also carries the TTL, as `now()` evaluated by the
|
|
5112
|
+
* database. The service reads a record before it acts on it, and a record can
|
|
5113
|
+
* pass its expiry in the gap between that read and this UPDATE — an approved one
|
|
5114
|
+
* most of all, since the poll that spends it is the one call that registers a
|
|
5115
|
+
* key. Judging the clock only in the read would let that key be registered after
|
|
5116
|
+
* the code was dead.
|
|
5117
|
+
*/
|
|
5118
|
+
|
|
5119
|
+
declare class DeviceAuthorizationsRepository extends BaseRepository {
|
|
5120
|
+
/**
|
|
5121
|
+
* Insert a pending authorization, unless one of its codes is already taken.
|
|
5122
|
+
*
|
|
5123
|
+
* `onConflictDoNothing` rather than letting the unique index raise: the start
|
|
5124
|
+
* route runs inside a transaction, and a raised unique violation aborts it,
|
|
5125
|
+
* so the retry that a generated-code collision calls for could not run — every
|
|
5126
|
+
* statement after it would fail with "current transaction is aborted" instead.
|
|
5127
|
+
* An empty result is the collision signal, and the caller draws a fresh code.
|
|
5128
|
+
*
|
|
5129
|
+
* Write primary.
|
|
5130
|
+
*
|
|
5131
|
+
* @returns the inserted row, or null if either code collided
|
|
5132
|
+
*/
|
|
5133
|
+
create(data: NewDeviceAuthorization): Promise<DeviceAuthorization | null>;
|
|
5134
|
+
/**
|
|
5135
|
+
* Find a record by its normalized user code, in any state.
|
|
5136
|
+
*
|
|
5137
|
+
* Deliberately unfiltered: which refusal a caller is owed — expired, already
|
|
5138
|
+
* handled, unknown — is the service's decision, and a row filtered out here
|
|
5139
|
+
* would be indistinguishable from a code that was never issued.
|
|
5140
|
+
*
|
|
5141
|
+
* Read replica.
|
|
5142
|
+
*/
|
|
5143
|
+
findByUserCode(userCode: string): Promise<DeviceAuthorization | null>;
|
|
5144
|
+
/**
|
|
5145
|
+
* Find a record by the hash of a device code, in any state.
|
|
5146
|
+
* Unfiltered for the same reason as `findByUserCode`.
|
|
5147
|
+
*
|
|
5148
|
+
* Read replica.
|
|
5149
|
+
*/
|
|
5150
|
+
findByDeviceCodeHash(deviceCodeHash: string): Promise<DeviceAuthorization | null>;
|
|
5151
|
+
/**
|
|
5152
|
+
* Bind the approving user and move the record to `approved`, but only from
|
|
5153
|
+
* `pending`.
|
|
5154
|
+
*
|
|
5155
|
+
* `userId` comes from the approver's authenticated session — never from a
|
|
5156
|
+
* request body — so this is the point where the record gains an owner.
|
|
5157
|
+
*
|
|
5158
|
+
* @returns the updated row, or null if it was no longer pending, or expired
|
|
5159
|
+
*/
|
|
5160
|
+
approve(id: number, userId: number): Promise<DeviceAuthorization | null>;
|
|
5161
|
+
/**
|
|
5162
|
+
* Move the record to `denied`, but only from `pending`.
|
|
5163
|
+
*
|
|
5164
|
+
* No user is bound: a refusal should leave no record of who was asked.
|
|
5165
|
+
*
|
|
5166
|
+
* @returns the updated row, or null if it was no longer pending, or expired
|
|
5167
|
+
*/
|
|
5168
|
+
deny(id: number): Promise<DeviceAuthorization | null>;
|
|
5169
|
+
/**
|
|
5170
|
+
* Refuse every authorization a user still has in flight.
|
|
5171
|
+
*
|
|
5172
|
+
* This is the device-code half of a global revocation. Revoking
|
|
5173
|
+
* `user_public_keys` alone leaves an approved-but-uncollected record behind,
|
|
5174
|
+
* and the next poll on it registers a brand-new active key — so "sign every
|
|
5175
|
+
* device out" would hand one straight back to whoever was still waiting,
|
|
5176
|
+
* which is exactly the person a revoke-all is usually aimed at.
|
|
5177
|
+
*
|
|
5178
|
+
* `denied` rather than a status of its own: a swept record owes its holder
|
|
5179
|
+
* the answer a refused one owes, on all four operations — the waiting device
|
|
5180
|
+
* is told no and stops polling, and info/approve/deny say the request was
|
|
5181
|
+
* already answered. Adding a fourth live status would add a migration and a
|
|
5182
|
+
* case-table row to record a distinction nothing acts on.
|
|
5183
|
+
*
|
|
5184
|
+
* Expired rows are swept too. A global revoke is not the place to reason
|
|
5185
|
+
* about which dead rows were about to die anyway.
|
|
5186
|
+
*
|
|
5187
|
+
* `pending` alongside `approved` even though a pending row carries no
|
|
5188
|
+
* `userId` today, so only approved rows can match: the state list says which
|
|
5189
|
+
* states this is meant to close, and a later change that binds the user
|
|
5190
|
+
* earlier should not silently reopen the hole.
|
|
5191
|
+
*
|
|
5192
|
+
* @returns the rows this call refused
|
|
5193
|
+
*/
|
|
5194
|
+
denyAllActiveByUserId(userId: number): Promise<DeviceAuthorization[]>;
|
|
5195
|
+
/**
|
|
5196
|
+
* Spend an approved record, but only from `approved`, and address it by the
|
|
5197
|
+
* device code hash the caller actually presented.
|
|
5198
|
+
*
|
|
5199
|
+
* This is the one-shot: the winner of two concurrent polls registers the key,
|
|
5200
|
+
* and the loser sees no approved record and is answered as if the code were
|
|
5201
|
+
* unknown. Matching on the hash rather than on an id read a moment ago keeps
|
|
5202
|
+
* the whole decision in one statement.
|
|
5203
|
+
*
|
|
5204
|
+
* @returns the spent row, or null if it was not approved (any more), or expired
|
|
5205
|
+
*/
|
|
5206
|
+
consumeApproved(deviceCodeHash: string): Promise<DeviceAuthorization | null>;
|
|
5207
|
+
}
|
|
5208
|
+
declare const deviceAuthorizationsRepository: DeviceAuthorizationsRepository;
|
|
5209
|
+
|
|
4816
5210
|
/**
|
|
4817
5211
|
* Roles Repository
|
|
4818
5212
|
*
|
|
@@ -5531,11 +5925,11 @@ declare class InvitationsRepository extends BaseRepository {
|
|
|
5531
5925
|
email: string;
|
|
5532
5926
|
metadata: Record<string, any> | null;
|
|
5533
5927
|
token: string;
|
|
5928
|
+
status: "pending" | "accepted" | "expired" | "cancelled";
|
|
5534
5929
|
id: number;
|
|
5535
5930
|
roleId: number;
|
|
5536
5931
|
createdAt: Date;
|
|
5537
5932
|
updatedAt: Date;
|
|
5538
|
-
status: "pending" | "accepted" | "expired" | "cancelled";
|
|
5539
5933
|
invitedBy: number;
|
|
5540
5934
|
expiresAt: Date;
|
|
5541
5935
|
acceptedAt: Date | null;
|
|
@@ -5565,11 +5959,11 @@ declare class InvitationsRepository extends BaseRepository {
|
|
|
5565
5959
|
email: string;
|
|
5566
5960
|
metadata: Record<string, any> | null;
|
|
5567
5961
|
token: string;
|
|
5962
|
+
status: "pending" | "accepted" | "expired" | "cancelled";
|
|
5568
5963
|
id: number;
|
|
5569
5964
|
roleId: number;
|
|
5570
5965
|
createdAt: Date;
|
|
5571
5966
|
updatedAt: Date;
|
|
5572
|
-
status: "pending" | "accepted" | "expired" | "cancelled";
|
|
5573
5967
|
invitedBy: number;
|
|
5574
5968
|
expiresAt: Date;
|
|
5575
5969
|
acceptedAt: Date | null;
|
|
@@ -5958,11 +6352,11 @@ declare class AccountDeletionRequestsRepository extends BaseRepository {
|
|
|
5958
6352
|
* Write primary 사용
|
|
5959
6353
|
*/
|
|
5960
6354
|
create(data: NewAccountDeletionRequest): Promise<{
|
|
6355
|
+
status: "pending" | "cancelled" | "completed";
|
|
5961
6356
|
userId: number | null;
|
|
5962
6357
|
id: number;
|
|
5963
6358
|
createdAt: Date;
|
|
5964
6359
|
updatedAt: Date;
|
|
5965
|
-
status: "pending" | "cancelled" | "completed";
|
|
5966
6360
|
reason: string | null;
|
|
5967
6361
|
purgeScheduledAt: Date;
|
|
5968
6362
|
cancelledAt: Date | null;
|
|
@@ -6552,15 +6946,15 @@ declare function getUser(c: Context | {
|
|
|
6552
6946
|
}): {
|
|
6553
6947
|
email: string | null;
|
|
6554
6948
|
phone: string | null;
|
|
6555
|
-
|
|
6949
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
6556
6950
|
publicId: string;
|
|
6951
|
+
passwordChangeRequired: boolean;
|
|
6952
|
+
id: number;
|
|
6557
6953
|
username: string | null;
|
|
6558
6954
|
passwordHash: string | null;
|
|
6559
|
-
passwordChangeRequired: boolean;
|
|
6560
6955
|
roleId: number;
|
|
6561
6956
|
createdAt: Date;
|
|
6562
6957
|
updatedAt: Date;
|
|
6563
|
-
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
6564
6958
|
emailVerifiedAt: Date | null;
|
|
6565
6959
|
phoneVerifiedAt: Date | null;
|
|
6566
6960
|
lastLoginAt: Date | null;
|
|
@@ -6651,6 +7045,8 @@ declare const COOKIE_NAMES: {
|
|
|
6651
7045
|
readonly OAUTH_CSRF: string;
|
|
6652
7046
|
/** Password-setup session for verified-email signup — temporary, single-purpose */
|
|
6653
7047
|
readonly SIGNUP_SETUP: string;
|
|
7048
|
+
/** CSRF token — the only cookie here the browser can read */
|
|
7049
|
+
readonly CSRF: string;
|
|
6654
7050
|
};
|
|
6655
7051
|
/**
|
|
6656
7052
|
* OAuth CSRF 쿠키를 PORT 접미사와 무관하게 전부 수집한다.
|
|
@@ -6712,6 +7108,36 @@ interface BeforeRegisterContext {
|
|
|
6712
7108
|
/** App-supplied registration metadata (register params / OAuth start params / invitation) */
|
|
6713
7109
|
metadata?: Record<string, unknown>;
|
|
6714
7110
|
}
|
|
7111
|
+
/**
|
|
7112
|
+
* How the Next.js proxy treats a cookie-authenticated mutation that arrives
|
|
7113
|
+
* without a valid CSRF header.
|
|
7114
|
+
*
|
|
7115
|
+
* - `off`: no check
|
|
7116
|
+
* - `warn`: allow it through, log one line per request that would be refused
|
|
7117
|
+
* - `enforce`: refuse it with 403
|
|
7118
|
+
*/
|
|
7119
|
+
type CsrfMode = 'off' | 'warn' | 'enforce';
|
|
7120
|
+
/**
|
|
7121
|
+
* CSRF configuration for the Next.js proxy
|
|
7122
|
+
*/
|
|
7123
|
+
interface AuthCsrfConfig {
|
|
7124
|
+
/**
|
|
7125
|
+
* @default 'warn' — an existing app gets signal before it gets breakage.
|
|
7126
|
+
* `SPFN_AUTH_CSRF` sets it when this is not; new apps scaffolded by
|
|
7127
|
+
* `spfn init` are given `enforce`.
|
|
7128
|
+
*/
|
|
7129
|
+
mode?: CsrfMode;
|
|
7130
|
+
/**
|
|
7131
|
+
* Backend paths that skip the check, matched exactly.
|
|
7132
|
+
*
|
|
7133
|
+
* These are route paths as the backend sees them (`/webhooks/stripe`), not
|
|
7134
|
+
* `/api/rpc/...` URLs, with route params already substituted. Intended for
|
|
7135
|
+
* endpoints a browser session never calls — webhook receivers and the like.
|
|
7136
|
+
* A path listed here is unprotected for cookie callers too, so list only
|
|
7137
|
+
* endpoints that carry their own authentication.
|
|
7138
|
+
*/
|
|
7139
|
+
exemptPaths?: string[];
|
|
7140
|
+
}
|
|
6715
7141
|
/**
|
|
6716
7142
|
* Auth configuration
|
|
6717
7143
|
*/
|
|
@@ -6755,6 +7181,17 @@ interface AuthConfig {
|
|
|
6755
7181
|
* ```
|
|
6756
7182
|
*/
|
|
6757
7183
|
beforeRegister?: (context: BeforeRegisterContext) => void | Promise<void>;
|
|
7184
|
+
/**
|
|
7185
|
+
* CSRF protection for cookie-session mutations, enforced in the Next.js proxy.
|
|
7186
|
+
*
|
|
7187
|
+
* @example
|
|
7188
|
+
* ```typescript
|
|
7189
|
+
* configureAuth({
|
|
7190
|
+
* csrf: { mode: 'enforce', exemptPaths: ['/webhooks/stripe'] },
|
|
7191
|
+
* });
|
|
7192
|
+
* ```
|
|
7193
|
+
*/
|
|
7194
|
+
csrf?: AuthCsrfConfig;
|
|
6758
7195
|
}
|
|
6759
7196
|
/**
|
|
6760
7197
|
* Configure global auth settings
|
|
@@ -6796,6 +7233,64 @@ declare function runBeforeRegister(context: BeforeRegisterContext): Promise<void
|
|
|
6796
7233
|
* 4. Default (7 days)
|
|
6797
7234
|
*/
|
|
6798
7235
|
declare function getSessionTtl(override?: string | number): number;
|
|
7236
|
+
/**
|
|
7237
|
+
* Get the CSRF mode
|
|
7238
|
+
*
|
|
7239
|
+
* Priority:
|
|
7240
|
+
* 1. Global config (configureAuth)
|
|
7241
|
+
* 2. Environment variable (SPFN_AUTH_CSRF)
|
|
7242
|
+
* 3. Default ('warn')
|
|
7243
|
+
*
|
|
7244
|
+
* An unrecognized value is a typo in the one setting that turns the check on;
|
|
7245
|
+
* it resolves to `enforce` and says so, rather than quietly leaving mutations
|
|
7246
|
+
* unprotected. It says so once per process: this runs on every mutation, so a
|
|
7247
|
+
* per-call error would be pure repetition burying the rest of the log.
|
|
7248
|
+
*/
|
|
7249
|
+
declare function getCsrfMode(): CsrfMode;
|
|
7250
|
+
/**
|
|
7251
|
+
* Get the paths exempted from the CSRF check (exact match, backend route paths)
|
|
7252
|
+
*/
|
|
7253
|
+
declare function getCsrfExemptPaths(): string[];
|
|
7254
|
+
|
|
7255
|
+
/**
|
|
7256
|
+
* @spfn/auth - CSRF token derivation
|
|
7257
|
+
*
|
|
7258
|
+
* The token is an HMAC of the session's key id under a subkey derived from the
|
|
7259
|
+
* session secret. Two properties follow from that shape:
|
|
7260
|
+
*
|
|
7261
|
+
* - The proxy recomputes it from the session it just unsealed, so a value an
|
|
7262
|
+
* attacker planted in the readable cookie (sibling-subdomain cookie tossing)
|
|
7263
|
+
* never verifies. Nothing here compares a cookie against a header.
|
|
7264
|
+
* - It is bound to the key id, so rotating the session key invalidates it.
|
|
7265
|
+
*
|
|
7266
|
+
* No new secret: the subkey is a labelled HMAC of SPFN_AUTH_SESSION_SECRET, so
|
|
7267
|
+
* the key that encrypts sessions is never used verbatim as the token key.
|
|
7268
|
+
*/
|
|
7269
|
+
/** Header the readable CSRF cookie is mirrored into by the client. */
|
|
7270
|
+
declare const CSRF_HEADER = "x-spfn-csrf";
|
|
7271
|
+
/**
|
|
7272
|
+
* Derive the CSRF token for a session key id.
|
|
7273
|
+
*
|
|
7274
|
+
* @param keyId - Session key id (`SessionData.keyId`)
|
|
7275
|
+
* @returns 64-char hex token — safe to put in a readable cookie, it reveals
|
|
7276
|
+
* neither the secret nor the key id
|
|
7277
|
+
*/
|
|
7278
|
+
declare function deriveCsrfToken(keyId: string): Promise<string>;
|
|
7279
|
+
/**
|
|
7280
|
+
* Whether a presented header value carries the expected token.
|
|
7281
|
+
*
|
|
7282
|
+
* The header may carry several comma-separated candidates — a browser sees every
|
|
7283
|
+
* `spfn_csrf*` cookie set on the host and cannot tell which dev instance owns
|
|
7284
|
+
* which, nor which of two same-named cookies a sibling subdomain tossed in.
|
|
7285
|
+
*
|
|
7286
|
+
* Every candidate the client is allowed to send is checked — the cap here is the
|
|
7287
|
+
* one it selects against — so a genuine value is never evicted by tossed ones
|
|
7288
|
+
* that happen to sort ahead of it. A header longer than that can only come from a
|
|
7289
|
+
* client that ignored the shared bound, and its surplus is dropped. Accepting any
|
|
7290
|
+
* match is no weaker than accepting one: a candidate the attacker chose still has
|
|
7291
|
+
* to equal a value recomputed from the session.
|
|
7292
|
+
*/
|
|
7293
|
+
declare function matchesCsrfToken(expected: string, presented: string | null | undefined): boolean;
|
|
6799
7294
|
|
|
6800
7295
|
/**
|
|
6801
7296
|
* One-Time Token Manager
|
|
@@ -6898,6 +7393,107 @@ declare function configureDeletion(options?: Partial<AuthDeletionConfig>): void;
|
|
|
6898
7393
|
*/
|
|
6899
7394
|
declare function getDeletionConfig(): AuthDeletionConfig;
|
|
6900
7395
|
|
|
7396
|
+
/**
|
|
7397
|
+
* Device-code Login Configuration
|
|
7398
|
+
*
|
|
7399
|
+
* Singleton config for device-code login, mirroring `lib/deletion-config.ts`: a
|
|
7400
|
+
* mutable module-level value, set once during `createAuthLifecycle()` and read at
|
|
7401
|
+
* request time.
|
|
7402
|
+
*
|
|
7403
|
+
* Both knobs are answers the server hands to a client that cannot ask again — the
|
|
7404
|
+
* waiting device is told its expiry and its poll interval in the `start` response
|
|
7405
|
+
* and then just obeys them — so they have to be resolved before any request is
|
|
7406
|
+
* served, not read from an environment variable at each use.
|
|
7407
|
+
*/
|
|
7408
|
+
interface AuthDeviceAuthConfig {
|
|
7409
|
+
/** How long a device code stays usable, in milliseconds. */
|
|
7410
|
+
ttlMs: number;
|
|
7411
|
+
/** How long the waiting device should wait between polls, in milliseconds. */
|
|
7412
|
+
intervalMs: number;
|
|
7413
|
+
}
|
|
7414
|
+
/**
|
|
7415
|
+
* Ten minutes. Long enough to walk to the other device and type the code, short
|
|
7416
|
+
* enough that a code glimpsed over a shoulder is worthless soon after.
|
|
7417
|
+
*/
|
|
7418
|
+
declare const DEFAULT_DEVICE_AUTH_TTL_MS: number;
|
|
7419
|
+
/** Five seconds. The interval the server asks for; the rate limit is what enforces it. */
|
|
7420
|
+
declare const DEFAULT_DEVICE_AUTH_INTERVAL_MS: number;
|
|
7421
|
+
/**
|
|
7422
|
+
* Set the resolved device-auth config. Called synchronously from
|
|
7423
|
+
* `createAuthLifecycle()` for the same reason `configureDeletion` is: it must
|
|
7424
|
+
* take effect before any handler that reads `getDeviceAuthConfig()` can run.
|
|
7425
|
+
*
|
|
7426
|
+
* Both knobs are whole millisecond counts, and one that is not is refused here
|
|
7427
|
+
* rather than served. `intervalMs` is the reason the check exists: it leaves as
|
|
7428
|
+
* `intervalMillis` in the start and poll answers, which `DeviceAuthPollResponseSchema`
|
|
7429
|
+
* declares an integer and the mobile contract exports as one, so a fractional
|
|
7430
|
+
* value configured here would reach a generated client as a number its decoder
|
|
7431
|
+
* refuses — at the one moment the flow depends on that client asking again.
|
|
7432
|
+
*/
|
|
7433
|
+
declare function configureDeviceAuth(options?: Partial<AuthDeviceAuthConfig>): void;
|
|
7434
|
+
/**
|
|
7435
|
+
* Read the current device-auth config. Safe to call any time — defaults apply
|
|
7436
|
+
* even if `createAuthLifecycle()` was never given a `deviceAuth` block.
|
|
7437
|
+
*/
|
|
7438
|
+
declare function getDeviceAuthConfig(): AuthDeviceAuthConfig;
|
|
7439
|
+
|
|
7440
|
+
/**
|
|
7441
|
+
* @spfn/auth - Device code generation
|
|
7442
|
+
*
|
|
7443
|
+
* The two codes device-code login runs on, and the one-way function between the
|
|
7444
|
+
* device code and what is stored for it.
|
|
7445
|
+
*
|
|
7446
|
+
* They are different kinds of value and are treated differently everywhere. The
|
|
7447
|
+
* device code is a credential: the polling device holds it, nothing else proves
|
|
7448
|
+
* who that device is, so it is long, random, and stored only as a hash. The user
|
|
7449
|
+
* code is read off one screen and typed on another, so it is short and stored in
|
|
7450
|
+
* the clear — it authorizes nothing on its own, since only an already
|
|
7451
|
+
* authenticated caller can act on it.
|
|
7452
|
+
*/
|
|
7453
|
+
/**
|
|
7454
|
+
* Alphabet the user code is drawn from.
|
|
7455
|
+
*
|
|
7456
|
+
* 0/O and 1/I/L are the pairs a person mistypes when copying a code between two
|
|
7457
|
+
* screens, so neither member of either pair is in the set. 31 characters over 8
|
|
7458
|
+
* positions is ~2^39 codes, which the per-IP and per-account rate limits on
|
|
7459
|
+
* every route that accepts one make unguessable in the ten minutes a code lives.
|
|
7460
|
+
*/
|
|
7461
|
+
declare const USER_CODE_ALPHABET = "ABCDEFGHJKMNPQRSTUVWXYZ23456789";
|
|
7462
|
+
/** Characters in a user code, not counting the dash it is displayed with. */
|
|
7463
|
+
declare const USER_CODE_LENGTH = 8;
|
|
7464
|
+
/**
|
|
7465
|
+
* A fresh user code in stored form — uppercase, no dash.
|
|
7466
|
+
*
|
|
7467
|
+
* `randomInt` rather than `Math.random`: this is drawn from the same CSPRNG the
|
|
7468
|
+
* device code is, and a predictable user code lets an attacker have a victim
|
|
7469
|
+
* approve a code the attacker is polling.
|
|
7470
|
+
*/
|
|
7471
|
+
declare function generateUserCode(): string;
|
|
7472
|
+
/** The stored form as a person reads it: `XXXX-XXXX`. */
|
|
7473
|
+
declare function formatUserCode(userCode: string): string;
|
|
7474
|
+
/**
|
|
7475
|
+
* Fold a typed user code back to the stored form.
|
|
7476
|
+
*
|
|
7477
|
+
* Someone reading `WXYZ-2345` off a screen types the dash, or spaces, or lower
|
|
7478
|
+
* case. All of those name the same code, so all of them have to reach the same
|
|
7479
|
+
* lookup — the alternative is a code that is on screen and still refused.
|
|
7480
|
+
*
|
|
7481
|
+
* Only the user code is normalized. The device code is a credential and is
|
|
7482
|
+
* matched byte for byte: accepting variant spellings of a credential is how one
|
|
7483
|
+
* token becomes several strings and a one-shot record is used twice.
|
|
7484
|
+
*/
|
|
7485
|
+
declare function normalizeUserCode(input: string): string;
|
|
7486
|
+
/** A fresh device code, returned to the new device once and never stored. */
|
|
7487
|
+
declare function generateDeviceCode(): string;
|
|
7488
|
+
/**
|
|
7489
|
+
* What the row holds instead of the device code.
|
|
7490
|
+
*
|
|
7491
|
+
* Unsalted SHA-256, as with the ops-token and signup-link secrets: the input is
|
|
7492
|
+
* 256 bits of CSPRNG output, so there is no dictionary to defend against and a
|
|
7493
|
+
* per-row salt would only stop the exact-match lookup this has to support.
|
|
7494
|
+
*/
|
|
7495
|
+
declare function hashDeviceCode(deviceCode: string): string;
|
|
7496
|
+
|
|
6901
7497
|
/**
|
|
6902
7498
|
* @spfn/auth - Centralized Logger
|
|
6903
7499
|
*
|
|
@@ -6911,6 +7507,7 @@ declare const authLogger: {
|
|
|
6911
7507
|
login: _spfn_core_logger.Logger;
|
|
6912
7508
|
keyRotation: _spfn_core_logger.Logger;
|
|
6913
7509
|
oauth: _spfn_core_logger.Logger;
|
|
7510
|
+
csrf: _spfn_core_logger.Logger;
|
|
6914
7511
|
};
|
|
6915
7512
|
session: _spfn_core_logger.Logger;
|
|
6916
7513
|
service: _spfn_core_logger.Logger;
|
|
@@ -7103,6 +7700,38 @@ interface AuthLifecycleOptions extends AuthInitOptions {
|
|
|
7103
7700
|
*/
|
|
7104
7701
|
onBeforePurge?: (user: AccountDeletionPurgeUser) => Promise<void>;
|
|
7105
7702
|
};
|
|
7703
|
+
/**
|
|
7704
|
+
* Device-code login configuration
|
|
7705
|
+
*
|
|
7706
|
+
* Controls the codes handed out by `POST /_auth/device/start` and obeyed by
|
|
7707
|
+
* `POST /_auth/device/poll`. Both values are announced to the waiting device
|
|
7708
|
+
* in the start response, so they are resolved here rather than read per call,
|
|
7709
|
+
* and both must be a positive whole number of milliseconds — the interval is
|
|
7710
|
+
* published as an integer and a fraction is refused at startup rather than
|
|
7711
|
+
* handed to a client that cannot decode it.
|
|
7712
|
+
*
|
|
7713
|
+
* @example
|
|
7714
|
+
* ```typescript
|
|
7715
|
+
* createAuthLifecycle({
|
|
7716
|
+
* deviceAuth: {
|
|
7717
|
+
* ttlMs: 5 * 60 * 1000, // a code lives 5 minutes
|
|
7718
|
+
* intervalMs: 3000, // poll every 3 seconds
|
|
7719
|
+
* },
|
|
7720
|
+
* })
|
|
7721
|
+
* ```
|
|
7722
|
+
*/
|
|
7723
|
+
deviceAuth?: {
|
|
7724
|
+
/**
|
|
7725
|
+
* How long a device code stays usable, in milliseconds.
|
|
7726
|
+
* @default 600000 (10 minutes)
|
|
7727
|
+
*/
|
|
7728
|
+
ttlMs?: number;
|
|
7729
|
+
/**
|
|
7730
|
+
* Poll interval the server asks the waiting device to keep, in milliseconds.
|
|
7731
|
+
* @default 5000
|
|
7732
|
+
*/
|
|
7733
|
+
intervalMs?: number;
|
|
7734
|
+
};
|
|
7106
7735
|
}
|
|
7107
7736
|
declare function createAuthLifecycle(options?: AuthLifecycleOptions): AuthLifecycleConfig;
|
|
7108
7737
|
|
|
@@ -7181,6 +7810,7 @@ declare const AuthProviderSchema: _sinclair_typebox.TUnion<[_sinclair_typebox.TL
|
|
|
7181
7810
|
* 발행 시점:
|
|
7182
7811
|
* - 이메일/전화 로그인 성공 시
|
|
7183
7812
|
* - OAuth 기존 사용자 로그인 시
|
|
7813
|
+
* - 기기 코드 승인이 소비되어 새 기기 키가 등록될 때 (provider: 'device')
|
|
7184
7814
|
*
|
|
7185
7815
|
* @example
|
|
7186
7816
|
* ```typescript
|
|
@@ -7193,7 +7823,7 @@ declare const authLoginEvent: _spfn_core_event.EventDef<{
|
|
|
7193
7823
|
email?: string | undefined;
|
|
7194
7824
|
phone?: string | undefined;
|
|
7195
7825
|
userId: string;
|
|
7196
|
-
provider: "email" | "phone" | "google" | "apple" | "github" | "kakao" | "naver" | "superself";
|
|
7826
|
+
provider: "email" | "phone" | "google" | "apple" | "github" | "kakao" | "naver" | "superself" | "device";
|
|
7197
7827
|
}>;
|
|
7198
7828
|
/**
|
|
7199
7829
|
* auth.register - 회원가입 성공 이벤트
|
|
@@ -7350,4 +7980,4 @@ type AuthDeletionCancelledPayload = typeof authDeletionCancelledEvent._payload;
|
|
|
7350
7980
|
type AuthDeletionCompletedPayload = typeof authDeletionCompletedEvent._payload;
|
|
7351
7981
|
type OAuthUnlinkedPayload = typeof oauthUnlinkedEvent._payload;
|
|
7352
7982
|
|
|
7353
|
-
export { type AccountDeletionPurgeUser, type AccountDeletionRequest, AccountDeletionRequestedBy, AccountDeletionRequestsRepository, type AuthConfig, AuthContext, type AuthDeletionCancelledPayload, type AuthDeletionCompletedPayload, type AuthDeletionConfig, type AuthDeletionRequestedPayload, type AuthLifecycleConfig, type AuthLifecycleOptions, type AuthLoginPayload, type AuthMetadataEntity, AuthMetadataRepository, AuthProviderSchema, type AuthRegisterPayload, type BeforeRegisterContext, COOKIE_NAMES, type CancelAccountDeletionParams, type CancelAccountDeletionResult, type CreateOAuthStateParams, DEFAULT_DELETION_ALLOW_SELF_IMMEDIATE, DEFAULT_DELETION_GRACE_PERIOD_DAYS, DEFAULT_DELETION_PURGE_CRON, DEFAULT_DELETION_PURGE_STRATEGY, DEFAULT_DELETION_SEND_NOTIFICATIONS, type DecryptedOAuthToken, type EmailNormalizationResult, EnvironmentKeyringTokenCipher, type GoogleTokenResponse, type GoogleUserInfo, type Invitation, type InvitationAcceptedPayload, type InvitationCreatedPayload, InvitationStatus, InvitationsRepository, type IssuedOpsToken, KeyAlgorithmType, KeysRepository, type NewAccountDeletionRequest, type NewAuthMetadataEntity, type NewInvitation, type NewOpsToken, type NewPermission, type NewPermissionEntity, type NewRole, type NewRoleEntity, type NewRolePermission, type NewSignupLinkToken, type NewUser, type NewUserPermission, type NewUserProfile, type NewUserPublicKey, type NewUserSocialAccount, type NewVerificationCode, OAuthProvider, type OAuthState, type OAuthTokenCipher, type OAuthTokenContext, type OAuthTokenType, type OAuthUnlinkedPayload, type OpsToken, OpsTokensRepository, type PendingDeletionInfo, type Permission, type PermissionEntity, PermissionsRepository, PurgeStrategy, type PurgeUserResult, type RegisterChannel, type RequestAccountDeletionParams, type RequestAccountDeletionResult, type Role, type RoleEntity, type RoleGuardOptions, type RolePermission, RolePermissionsRepository, RolesRepository, type SessionPayload, type SignupLinkToken, SignupLinkTokensRepository, SocialAccountsRepository, SocialProvider, type SweepDuePurgesResult, type TokenPayload, type UpdateProfileParams, type User, type UserPermission, UserPermissionsRepository, type UserProfile, UserProfilesRepository, type UserPublicKey, type UserSocialAccount, UsersRepository, type VerificationCode, VerificationCodesRepository, VerificationPurpose, type VerifiedOpsToken, type VerifyIdTokenParams, acceptInvitation, accountDeletionRequests, accountDeletionRequestsRepository, addPermissionToRole, appleProvider, assertCanAssignRole, authDeletionCancelledEvent, authDeletionCompletedEvent, authDeletionRequestedEvent, authJobRouter, authLogger, authLoginEvent, authMetadata, authMetadataRepository, authRegisterEvent, authSchema, cancelAccountDeletionService, cancelInvitation, checkUsernameAvailableService, configureAuth, configureDeletion, configureOAuthTokenCipher, createAuthDeletionJobRouter, createAuthDeletionPurgeJob, createAuthLifecycle, createInvitation, createOAuthState, createRole, decodeToken, decryptToken, deleteInvitation, deleteRole, encryptToken, exchangeCodeForTokens, expireOldInvitations, generateOAuthNonce, generateToken, getAllRoles, getAuth, getAuthConfig, getAuthSessionService, getDeletionConfig, getDummyPasswordHash, getGoogleAuthUrl, getGoogleOAuthConfig, getGoogleUserInfo, getInvitationByToken, getInvitationWithDetails, getKeyId, getLocale, getOneTimeTokenManager, getOpsToken, getOptionalAuth, getPendingDeletionInfo, getRole, getRoleByName, getRolePermissions, getSessionTtl, getUser, getUserByEmailService, getUserByIdService, getUserByPhoneService, getUserId, getUserPermissions, getUserProfileService, getUserRole, githubProvider, googleProvider, hasAllPermissions, hasAnyPermission, hasAnyRole, hasPermission, hasRole, hashPassword, initOneTimeTokenManager, initializeAuth, invitationAcceptedEvent, invitationCreatedEvent, invitationsRepository, isEncrypted, isGoogleOAuthEnabled, issueOpsTokenService, kakaoProvider, keysRepository, listInvitations, listOpsTokensService, matchOAuthCsrfCookies, naverProvider, normalizeEmail, normalizeOptionalEmail, normalizeStoredEmails, oauthUnlinkedEvent, oneTimeTokenAuth, opsTokenAuth, opsTokens, opsTokensRepository, parseDuration, permissions, permissionsRepository, purgeUserService, refreshAccessToken, removePermissionFromRole, requestAccountDeletionService, requireAnyPermission, requireOpsScope, requirePermissions, requireRole, resendInvitation, revokeOpsTokenService, roleGuard, rolePermissions, rolePermissionsRepository, roles, rolesRepository, runBeforeRegister, setRolePermissions, signupLinkTokens, signupLinkTokensRepository, socialAccountsRepository, sweepDuePurges, updateLastLoginService, updateLocaleService, updateRole, updateUserProfileService, updateUserService, updateUsernameService, userInvitations, userPermissions, userPermissionsRepository, userProfiles, userProfilesRepository, userPublicKeys, userSocialAccounts, users, usersRepository, validateInvitation, validatePasswordStrength, verificationCodes, verificationCodesRepository, verifyClientToken, verifyIdToken, verifyKeyFingerprint, verifyOAuthState, verifyOpsTokenService, verifyPassword, verifyToken };
|
|
7983
|
+
export { type AccountDeletionPurgeUser, type AccountDeletionRequest, AccountDeletionRequestedBy, AccountDeletionRequestsRepository, type AuthConfig, AuthContext, type AuthCsrfConfig, type AuthDeletionCancelledPayload, type AuthDeletionCompletedPayload, type AuthDeletionConfig, type AuthDeletionRequestedPayload, type AuthDeviceAuthConfig, type AuthLifecycleConfig, type AuthLifecycleOptions, type AuthLoginPayload, type AuthMetadataEntity, AuthMetadataRepository, AuthProviderSchema, type AuthRegisterPayload, type BeforeRegisterContext, COOKIE_NAMES, CSRF_HEADER, type CancelAccountDeletionParams, type CancelAccountDeletionResult, type CreateOAuthStateParams, type CsrfMode, DEFAULT_DELETION_ALLOW_SELF_IMMEDIATE, DEFAULT_DELETION_GRACE_PERIOD_DAYS, DEFAULT_DELETION_PURGE_CRON, DEFAULT_DELETION_PURGE_STRATEGY, DEFAULT_DELETION_SEND_NOTIFICATIONS, DEFAULT_DEVICE_AUTH_INTERVAL_MS, DEFAULT_DEVICE_AUTH_TTL_MS, DEVICE_AUTH_STATUSES, type DecryptedOAuthToken, type DeviceAuthStatus, type DeviceAuthorization, DeviceAuthorizationsRepository, type EmailNormalizationResult, EnvironmentKeyringTokenCipher, type GoogleTokenResponse, type GoogleUserInfo, type Invitation, type InvitationAcceptedPayload, type InvitationCreatedPayload, InvitationStatus, InvitationsRepository, type IssuedOpsToken, KeyAlgorithmType, KeysRepository, type NewAccountDeletionRequest, type NewAuthMetadataEntity, type NewDeviceAuthorization, type NewInvitation, type NewOpsToken, type NewPermission, type NewPermissionEntity, type NewRole, type NewRoleEntity, type NewRolePermission, type NewSignupLinkToken, type NewUser, type NewUserPermission, type NewUserProfile, type NewUserPublicKey, type NewUserSocialAccount, type NewVerificationCode, OAuthProvider, type OAuthState, type OAuthTokenCipher, type OAuthTokenContext, type OAuthTokenType, type OAuthUnlinkedPayload, type OpsToken, OpsTokensRepository, type PendingDeletionInfo, type Permission, type PermissionEntity, PermissionsRepository, PurgeStrategy, type PurgeUserResult, type RegisterChannel, type RequestAccountDeletionParams, type RequestAccountDeletionResult, type Role, type RoleEntity, type RoleGuardOptions, type RolePermission, RolePermissionsRepository, RolesRepository, type SessionPayload, type SignupLinkToken, SignupLinkTokensRepository, SocialAccountsRepository, SocialProvider, type SweepDuePurgesResult, type TokenPayload, USER_CODE_ALPHABET, USER_CODE_LENGTH, type UpdateProfileParams, type User, type UserPermission, UserPermissionsRepository, type UserProfile, UserProfilesRepository, type UserPublicKey, type UserSocialAccount, UsersRepository, type VerificationCode, VerificationCodesRepository, VerificationPurpose, type VerifiedOpsToken, type VerifyIdTokenParams, acceptInvitation, accountDeletionRequests, accountDeletionRequestsRepository, addPermissionToRole, appleProvider, assertCanAssignRole, authDeletionCancelledEvent, authDeletionCompletedEvent, authDeletionRequestedEvent, authJobRouter, authLogger, authLoginEvent, authMetadata, authMetadataRepository, authRegisterEvent, authSchema, cancelAccountDeletionService, cancelInvitation, checkUsernameAvailableService, configureAuth, configureDeletion, configureDeviceAuth, configureOAuthTokenCipher, createAuthDeletionJobRouter, createAuthDeletionPurgeJob, createAuthLifecycle, createInvitation, createOAuthState, createRole, decodeToken, decryptToken, deleteInvitation, deleteRole, deriveCsrfToken, deviceAuthorizations, deviceAuthorizationsRepository, encryptToken, exchangeCodeForTokens, expireOldInvitations, formatUserCode, generateDeviceCode, generateOAuthNonce, generateToken, generateUserCode, getAllRoles, getAuth, getAuthConfig, getAuthSessionService, getCsrfExemptPaths, getCsrfMode, getDeletionConfig, getDeviceAuthConfig, getDummyPasswordHash, getGoogleAuthUrl, getGoogleOAuthConfig, getGoogleUserInfo, getInvitationByToken, getInvitationWithDetails, getKeyId, getLocale, getOneTimeTokenManager, getOpsToken, getOptionalAuth, getPendingDeletionInfo, getRole, getRoleByName, getRolePermissions, getSessionTtl, getUser, getUserByEmailService, getUserByIdService, getUserByPhoneService, getUserId, getUserPermissions, getUserProfileService, getUserRole, githubProvider, googleProvider, hasAllPermissions, hasAnyPermission, hasAnyRole, hasPermission, hasRole, hashDeviceCode, hashPassword, initOneTimeTokenManager, initializeAuth, invitationAcceptedEvent, invitationCreatedEvent, invitationsRepository, isEncrypted, isGoogleOAuthEnabled, issueOpsTokenService, kakaoProvider, keysRepository, listInvitations, listOpsTokensService, matchOAuthCsrfCookies, matchesCsrfToken, naverProvider, normalizeEmail, normalizeOptionalEmail, normalizeStoredEmails, normalizeUserCode, oauthUnlinkedEvent, oneTimeTokenAuth, opsTokenAuth, opsTokens, opsTokensRepository, parseDuration, permissions, permissionsRepository, purgeUserService, refreshAccessToken, removePermissionFromRole, requestAccountDeletionService, requireAnyPermission, requireOpsScope, requirePermissions, requireRole, resendInvitation, revokeOpsTokenService, roleGuard, rolePermissions, rolePermissionsRepository, roles, rolesRepository, runBeforeRegister, setRolePermissions, signupLinkTokens, signupLinkTokensRepository, socialAccountsRepository, sweepDuePurges, updateLastLoginService, updateLocaleService, updateRole, updateUserProfileService, updateUserService, updateUsernameService, userInvitations, userPermissions, userPermissionsRepository, userProfiles, userProfilesRepository, userPublicKeys, userSocialAccounts, users, usersRepository, validateInvitation, validatePasswordStrength, verificationCodes, verificationCodesRepository, verifyClientToken, verifyIdToken, verifyKeyFingerprint, verifyOAuthState, verifyOpsTokenService, verifyPassword, verifyToken };
|