@spfn/auth 0.2.0-beta.8 → 0.2.0-beta.81
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/LICENSE +1 -1
- package/README.md +561 -1827
- package/dist/authenticate-ofdEmk6x.d.ts +1109 -0
- package/dist/config.d.ts +431 -39
- package/dist/config.js +217 -29
- package/dist/config.js.map +1 -1
- package/dist/errors.d.ts +117 -3
- package/dist/errors.js +83 -1
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +351 -109
- package/dist/index.js +124 -7
- package/dist/index.js.map +1 -1
- package/dist/nextjs/api.js +591 -61
- package/dist/nextjs/api.js.map +1 -1
- package/dist/nextjs/client.d.ts +28 -0
- package/dist/nextjs/client.js +80 -0
- package/dist/nextjs/client.js.map +1 -0
- package/dist/nextjs/server.d.ts +92 -3
- package/dist/nextjs/server.js +288 -24
- package/dist/nextjs/server.js.map +1 -1
- package/dist/server.d.ts +2019 -511
- package/dist/server.js +4435 -1157
- package/dist/server.js.map +1 -1
- package/dist/session-CGxgH3C9.d.ts +53 -0
- package/dist/types-1BMx0OX1.d.ts +84 -0
- package/migrations/0001_smooth_the_fury.sql +3 -0
- package/migrations/0002_deep_iceman.sql +11 -0
- package/migrations/0003_perfect_deathbird.sql +3 -0
- package/migrations/0004_concerned_rawhide_kid.sql +5 -0
- package/migrations/0005_lethal_lifeguard.sql +32 -0
- package/migrations/0006_easy_hardball.sql +24 -0
- package/migrations/0007_glossy_major_mapleleaf.sql +1 -0
- package/migrations/meta/0001_snapshot.json +1660 -0
- package/migrations/meta/0002_snapshot.json +1660 -0
- package/migrations/meta/0003_snapshot.json +1689 -0
- package/migrations/meta/0004_snapshot.json +1721 -0
- package/migrations/meta/0005_snapshot.json +1721 -0
- package/migrations/meta/0006_snapshot.json +1921 -0
- package/migrations/meta/0007_snapshot.json +1916 -0
- package/migrations/meta/_journal.json +49 -0
- package/package.json +43 -39
- package/dist/dto-lZmWuObc.d.ts +0 -645
package/dist/server.d.ts
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export {
|
|
1
|
+
import { j as AuthInitOptions, k as OAuthProvider, e as VerificationPurpose, i as PermissionCategory, l as AuthContext } from './authenticate-ofdEmk6x.js';
|
|
2
|
+
export { C as ChangePasswordParams, a3 as EmailSchema, I as IssueOneTimeTokenResult, s as LoginParams, L as LoginResult, t as LogoutParams, aa as NativeVerifyOptions, a8 as NormalizedIdentity, Z as OAuthCallbackParams, _ as OAuthCallbackResult, ab as OAuthCodeExchangeOptions, a0 as OAuthNativeParams, c as OAuthNativeResult, Y as OAuthStartParams, O as OAuthStartResult, a9 as OAuthTokens, a5 as PasswordSchema, a4 as PhoneSchema, q as RegisterParams, E as RegisterPublicKeyParams, a as RegisterResult, G as RevokeKeyParams, F as RotateKeyParams, b as RotateKeyResult, w as SendVerificationCodeParams, S as SendVerificationCodeResult, a6 as TargetTypeSchema, g as VERIFICATION_PURPOSES, f as VERIFICATION_TARGET_TYPES, a7 as VerificationPurposeSchema, V as VerificationTargetType, x as VerifyCodeParams, y as VerifyCodeResult, m as authRouter, a1 as authenticate, N as buildOAuthErrorUrl, p as changePasswordService, W as getEnabledOAuthProviders, X as getGoogleAccessToken, ad as getOAuthProvider, ae as getRegisteredProviders, Q as isOAuthProviderEnabled, H as issueOneTimeTokenService, n as loginService, o as logoutService, M as oauthCallbackService, $ as oauthNativeService, K as oauthStartService, a2 as optionalAuth, ac as registerOAuthProvider, z as registerPublicKeyService, r as registerService, T as requireEnabledProvider, D as revokeKeyService, B as rotateKeyService, u as sendVerificationCodeService, v as verifyCodeService, J as verifyOneTimeTokenService } from './authenticate-ofdEmk6x.js';
|
|
3
3
|
import * as drizzle_orm_pg_core from 'drizzle-orm/pg-core';
|
|
4
|
+
import { K as KeyAlgorithmType, c as InvitationStatus, e as SocialProvider, g as AccountDeletionRequestedBy, h as PurgeStrategy } from './types-1BMx0OX1.js';
|
|
5
|
+
export { b as ACCOUNT_DELETION_REQUESTED_BY, A as ACCOUNT_DELETION_REQUEST_STATUSES, f as AccountDeletionRequestStatus, I as INVITATION_STATUSES, a as KEY_ALGORITHM, P as PURGE_STRATEGIES, S as SOCIAL_PROVIDERS, U as USER_STATUSES, d as UserStatus } from './types-1BMx0OX1.js';
|
|
4
6
|
import { UserProfile as UserProfile$1, ProfileInfo } from '@spfn/auth';
|
|
5
7
|
import { BaseRepository } from '@spfn/core/db';
|
|
6
8
|
import { Context } from 'hono';
|
|
7
9
|
import * as _spfn_core_route from '@spfn/core/route';
|
|
8
10
|
import { Algorithm } from 'jsonwebtoken';
|
|
11
|
+
export { S as SessionData, g as getSessionInfo, s as sealSession, a as shouldRefreshSession, u as unsealSession } from './session-CGxgH3C9.js';
|
|
12
|
+
import { JWTPayload } from 'jose';
|
|
13
|
+
import { SSETokenStore, SSETokenManager } from '@spfn/core/event/sse';
|
|
9
14
|
import * as _spfn_core_logger from '@spfn/core/logger';
|
|
10
|
-
import '@
|
|
15
|
+
import * as _spfn_core_job from '@spfn/core/job';
|
|
16
|
+
import * as _spfn_core_event from '@spfn/core/event';
|
|
17
|
+
import * as _sinclair_typebox from '@sinclair/typebox';
|
|
11
18
|
import '@spfn/auth/server';
|
|
12
19
|
|
|
13
20
|
/**
|
|
@@ -27,6 +34,40 @@ declare const users: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
27
34
|
name: "users";
|
|
28
35
|
schema: string;
|
|
29
36
|
columns: {
|
|
37
|
+
deletedAt: drizzle_orm_pg_core.PgColumn<{
|
|
38
|
+
name: "deleted_at";
|
|
39
|
+
tableName: "users";
|
|
40
|
+
dataType: "date";
|
|
41
|
+
columnType: "PgTimestamp";
|
|
42
|
+
data: Date;
|
|
43
|
+
driverParam: string;
|
|
44
|
+
notNull: false;
|
|
45
|
+
hasDefault: false;
|
|
46
|
+
isPrimaryKey: false;
|
|
47
|
+
isAutoincrement: false;
|
|
48
|
+
hasRuntimeDefault: false;
|
|
49
|
+
enumValues: undefined;
|
|
50
|
+
baseColumn: never;
|
|
51
|
+
identity: undefined;
|
|
52
|
+
generated: undefined;
|
|
53
|
+
}, {}, {}>;
|
|
54
|
+
deletedBy: drizzle_orm_pg_core.PgColumn<{
|
|
55
|
+
name: "deleted_by";
|
|
56
|
+
tableName: "users";
|
|
57
|
+
dataType: "string";
|
|
58
|
+
columnType: "PgText";
|
|
59
|
+
data: string;
|
|
60
|
+
driverParam: string;
|
|
61
|
+
notNull: false;
|
|
62
|
+
hasDefault: false;
|
|
63
|
+
isPrimaryKey: false;
|
|
64
|
+
isAutoincrement: false;
|
|
65
|
+
hasRuntimeDefault: false;
|
|
66
|
+
enumValues: [string, ...string[]];
|
|
67
|
+
baseColumn: never;
|
|
68
|
+
identity: undefined;
|
|
69
|
+
generated: undefined;
|
|
70
|
+
}, {}, {}>;
|
|
30
71
|
createdAt: drizzle_orm_pg_core.PgColumn<{
|
|
31
72
|
name: "created_at";
|
|
32
73
|
tableName: "users";
|
|
@@ -78,6 +119,23 @@ declare const users: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
78
119
|
identity: undefined;
|
|
79
120
|
generated: undefined;
|
|
80
121
|
}, {}, {}>;
|
|
122
|
+
publicId: drizzle_orm_pg_core.PgColumn<{
|
|
123
|
+
name: "public_id";
|
|
124
|
+
tableName: "users";
|
|
125
|
+
dataType: "string";
|
|
126
|
+
columnType: "PgUUID";
|
|
127
|
+
data: string;
|
|
128
|
+
driverParam: string;
|
|
129
|
+
notNull: true;
|
|
130
|
+
hasDefault: true;
|
|
131
|
+
isPrimaryKey: false;
|
|
132
|
+
isAutoincrement: false;
|
|
133
|
+
hasRuntimeDefault: false;
|
|
134
|
+
enumValues: undefined;
|
|
135
|
+
baseColumn: never;
|
|
136
|
+
identity: undefined;
|
|
137
|
+
generated: undefined;
|
|
138
|
+
}, {}, {}>;
|
|
81
139
|
email: drizzle_orm_pg_core.PgColumn<{
|
|
82
140
|
name: "email";
|
|
83
141
|
tableName: "users";
|
|
@@ -112,6 +170,23 @@ declare const users: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
112
170
|
identity: undefined;
|
|
113
171
|
generated: undefined;
|
|
114
172
|
}, {}, {}>;
|
|
173
|
+
username: drizzle_orm_pg_core.PgColumn<{
|
|
174
|
+
name: "username";
|
|
175
|
+
tableName: "users";
|
|
176
|
+
dataType: "string";
|
|
177
|
+
columnType: "PgText";
|
|
178
|
+
data: string;
|
|
179
|
+
driverParam: string;
|
|
180
|
+
notNull: false;
|
|
181
|
+
hasDefault: false;
|
|
182
|
+
isPrimaryKey: false;
|
|
183
|
+
isAutoincrement: false;
|
|
184
|
+
hasRuntimeDefault: false;
|
|
185
|
+
enumValues: [string, ...string[]];
|
|
186
|
+
baseColumn: never;
|
|
187
|
+
identity: undefined;
|
|
188
|
+
generated: undefined;
|
|
189
|
+
}, {}, {}>;
|
|
115
190
|
passwordHash: drizzle_orm_pg_core.PgColumn<{
|
|
116
191
|
name: "password_hash";
|
|
117
192
|
tableName: "users";
|
|
@@ -150,11 +225,11 @@ declare const users: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
150
225
|
name: `${string}_id`;
|
|
151
226
|
tableName: "users";
|
|
152
227
|
dataType: "number";
|
|
153
|
-
columnType: "
|
|
228
|
+
columnType: "PgBigInt53";
|
|
154
229
|
data: number;
|
|
155
|
-
driverParam: number;
|
|
230
|
+
driverParam: string | number;
|
|
156
231
|
notNull: true;
|
|
157
|
-
hasDefault:
|
|
232
|
+
hasDefault: false;
|
|
158
233
|
isPrimaryKey: false;
|
|
159
234
|
isAutoincrement: false;
|
|
160
235
|
hasRuntimeDefault: false;
|
|
@@ -168,14 +243,14 @@ declare const users: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
168
243
|
tableName: "users";
|
|
169
244
|
dataType: "string";
|
|
170
245
|
columnType: "PgText";
|
|
171
|
-
data: "active" | "inactive" | "suspended";
|
|
246
|
+
data: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
172
247
|
driverParam: string;
|
|
173
248
|
notNull: true;
|
|
174
249
|
hasDefault: true;
|
|
175
250
|
isPrimaryKey: false;
|
|
176
251
|
isAutoincrement: false;
|
|
177
252
|
hasRuntimeDefault: false;
|
|
178
|
-
enumValues: ["active", "inactive", "suspended"] & [string, ...string[]];
|
|
253
|
+
enumValues: ["active", "inactive", "suspended", "pending_deletion", "deleted"] & [string, ...string[]];
|
|
179
254
|
baseColumn: never;
|
|
180
255
|
identity: undefined;
|
|
181
256
|
generated: undefined;
|
|
@@ -247,15 +322,19 @@ type NewUser = typeof users.$inferInsert;
|
|
|
247
322
|
* Get user by ID
|
|
248
323
|
*/
|
|
249
324
|
declare function getUserByIdService(userId: number): Promise<{
|
|
325
|
+
deletedAt: Date | null;
|
|
326
|
+
deletedBy: string | null;
|
|
250
327
|
createdAt: Date;
|
|
251
328
|
updatedAt: Date;
|
|
252
329
|
id: number;
|
|
330
|
+
publicId: string;
|
|
253
331
|
email: string | null;
|
|
254
332
|
phone: string | null;
|
|
333
|
+
username: string | null;
|
|
255
334
|
passwordHash: string | null;
|
|
256
335
|
passwordChangeRequired: boolean;
|
|
257
336
|
roleId: number;
|
|
258
|
-
status: "active" | "inactive" | "suspended";
|
|
337
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
259
338
|
emailVerifiedAt: Date | null;
|
|
260
339
|
phoneVerifiedAt: Date | null;
|
|
261
340
|
lastLoginAt: Date | null;
|
|
@@ -264,15 +343,19 @@ declare function getUserByIdService(userId: number): Promise<{
|
|
|
264
343
|
* Get user by email
|
|
265
344
|
*/
|
|
266
345
|
declare function getUserByEmailService(email: string): Promise<{
|
|
346
|
+
deletedAt: Date | null;
|
|
347
|
+
deletedBy: string | null;
|
|
267
348
|
createdAt: Date;
|
|
268
349
|
updatedAt: Date;
|
|
269
350
|
id: number;
|
|
351
|
+
publicId: string;
|
|
270
352
|
email: string | null;
|
|
271
353
|
phone: string | null;
|
|
354
|
+
username: string | null;
|
|
272
355
|
passwordHash: string | null;
|
|
273
356
|
passwordChangeRequired: boolean;
|
|
274
357
|
roleId: number;
|
|
275
|
-
status: "active" | "inactive" | "suspended";
|
|
358
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
276
359
|
emailVerifiedAt: Date | null;
|
|
277
360
|
phoneVerifiedAt: Date | null;
|
|
278
361
|
lastLoginAt: Date | null;
|
|
@@ -281,15 +364,19 @@ declare function getUserByEmailService(email: string): Promise<{
|
|
|
281
364
|
* Get user by phone
|
|
282
365
|
*/
|
|
283
366
|
declare function getUserByPhoneService(phone: string): Promise<{
|
|
367
|
+
deletedAt: Date | null;
|
|
368
|
+
deletedBy: string | null;
|
|
284
369
|
createdAt: Date;
|
|
285
370
|
updatedAt: Date;
|
|
286
371
|
id: number;
|
|
372
|
+
publicId: string;
|
|
287
373
|
email: string | null;
|
|
288
374
|
phone: string | null;
|
|
375
|
+
username: string | null;
|
|
289
376
|
passwordHash: string | null;
|
|
290
377
|
passwordChangeRequired: boolean;
|
|
291
378
|
roleId: number;
|
|
292
|
-
status: "active" | "inactive" | "suspended";
|
|
379
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
293
380
|
emailVerifiedAt: Date | null;
|
|
294
381
|
phoneVerifiedAt: Date | null;
|
|
295
382
|
lastLoginAt: Date | null;
|
|
@@ -302,17 +389,51 @@ declare function updateLastLoginService(userId: number): Promise<void>;
|
|
|
302
389
|
* Update user data
|
|
303
390
|
*/
|
|
304
391
|
declare function updateUserService(userId: number, updates: Partial<NewUser>): Promise<void>;
|
|
392
|
+
/**
|
|
393
|
+
* Check if username is available
|
|
394
|
+
*
|
|
395
|
+
* @returns true if the username is available (not taken and not reserved)
|
|
396
|
+
*/
|
|
397
|
+
declare function checkUsernameAvailableService(username: string): Promise<boolean>;
|
|
398
|
+
/**
|
|
399
|
+
* Update username with reserved word and duplicate check
|
|
400
|
+
*
|
|
401
|
+
* @param userId - User ID (string, number, or bigint)
|
|
402
|
+
* @param username - New username or null to clear
|
|
403
|
+
* @throws ReservedUsernameError if username is reserved
|
|
404
|
+
* @throws UsernameAlreadyTakenError if username is already in use by another user
|
|
405
|
+
*/
|
|
406
|
+
declare function updateUsernameService(userId: string | number | bigint, username: string | null): Promise<{
|
|
407
|
+
deletedAt: Date | null;
|
|
408
|
+
deletedBy: string | null;
|
|
409
|
+
createdAt: Date;
|
|
410
|
+
updatedAt: Date;
|
|
411
|
+
id: number;
|
|
412
|
+
publicId: string;
|
|
413
|
+
email: string | null;
|
|
414
|
+
phone: string | null;
|
|
415
|
+
username: string | null;
|
|
416
|
+
passwordHash: string | null;
|
|
417
|
+
passwordChangeRequired: boolean;
|
|
418
|
+
roleId: number;
|
|
419
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
420
|
+
emailVerifiedAt: Date | null;
|
|
421
|
+
phoneVerifiedAt: Date | null;
|
|
422
|
+
lastLoginAt: Date | null;
|
|
423
|
+
}>;
|
|
305
424
|
|
|
306
425
|
/**
|
|
307
426
|
* @spfn/auth - RBAC Initialization Service
|
|
308
427
|
*
|
|
309
428
|
* Initialize roles, permissions, and their mappings
|
|
429
|
+
* Uses hash-based change detection to skip unnecessary DB operations
|
|
310
430
|
*/
|
|
311
431
|
|
|
312
432
|
/**
|
|
313
433
|
* Initialize auth package with RBAC system
|
|
314
434
|
*
|
|
315
|
-
* Creates built-in roles, permissions, and custom configurations
|
|
435
|
+
* Creates built-in roles, permissions, and custom configurations.
|
|
436
|
+
* Uses hash-based change detection - skips DB operations when config is unchanged.
|
|
316
437
|
*
|
|
317
438
|
* @param options - Initialization options
|
|
318
439
|
*
|
|
@@ -442,6 +563,24 @@ declare function hasRole(userId: string | number | bigint, roleName: string): Pr
|
|
|
442
563
|
* @returns true if user has at least one role
|
|
443
564
|
*/
|
|
444
565
|
declare function hasAnyRole(userId: string | number | bigint, roleNames: string[]): Promise<boolean>;
|
|
566
|
+
/**
|
|
567
|
+
* Assert that the caller is allowed to assign the given role.
|
|
568
|
+
*
|
|
569
|
+
* Centralizes the privilege rule shared by direct role assignment and invitation:
|
|
570
|
+
* a non-superadmin caller may never grant the superadmin role, and may grant the
|
|
571
|
+
* admin role only when they hold the `admin:promote` permission. Assigning any
|
|
572
|
+
* lower role is unrestricted (the route's own permission guard still applies).
|
|
573
|
+
*
|
|
574
|
+
* @param callerUserId - User performing the assignment
|
|
575
|
+
* @param targetRoleId - Role being assigned
|
|
576
|
+
* @throws ForbiddenError when the caller lacks the authority for the target role
|
|
577
|
+
*
|
|
578
|
+
* @example
|
|
579
|
+
* ```typescript
|
|
580
|
+
* await assertCanAssignRole(auth.userId, body.roleId);
|
|
581
|
+
* ```
|
|
582
|
+
*/
|
|
583
|
+
declare function assertCanAssignRole(callerUserId: string | number | bigint, targetRoleId: number): Promise<void>;
|
|
445
584
|
|
|
446
585
|
/**
|
|
447
586
|
* @spfn/auth - Roles Entity
|
|
@@ -880,11 +1019,11 @@ declare const userInvitations: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
880
1019
|
name: `${string}_id`;
|
|
881
1020
|
tableName: "user_invitations";
|
|
882
1021
|
dataType: "number";
|
|
883
|
-
columnType: "
|
|
1022
|
+
columnType: "PgBigInt53";
|
|
884
1023
|
data: number;
|
|
885
|
-
driverParam: number;
|
|
1024
|
+
driverParam: string | number;
|
|
886
1025
|
notNull: true;
|
|
887
|
-
hasDefault:
|
|
1026
|
+
hasDefault: false;
|
|
888
1027
|
isPrimaryKey: false;
|
|
889
1028
|
isAutoincrement: false;
|
|
890
1029
|
hasRuntimeDefault: false;
|
|
@@ -897,11 +1036,11 @@ declare const userInvitations: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
897
1036
|
name: `${string}_id`;
|
|
898
1037
|
tableName: "user_invitations";
|
|
899
1038
|
dataType: "number";
|
|
900
|
-
columnType: "
|
|
1039
|
+
columnType: "PgBigInt53";
|
|
901
1040
|
data: number;
|
|
902
|
-
driverParam: number;
|
|
1041
|
+
driverParam: string | number;
|
|
903
1042
|
notNull: true;
|
|
904
|
-
hasDefault:
|
|
1043
|
+
hasDefault: false;
|
|
905
1044
|
isPrimaryKey: false;
|
|
906
1045
|
isAutoincrement: false;
|
|
907
1046
|
hasRuntimeDefault: false;
|
|
@@ -1032,6 +1171,7 @@ declare function createInvitation(params: {
|
|
|
1032
1171
|
roleId: number;
|
|
1033
1172
|
invitedBy: number;
|
|
1034
1173
|
expiresInDays?: number;
|
|
1174
|
+
expiresAt?: Date;
|
|
1035
1175
|
metadata?: Record<string, any>;
|
|
1036
1176
|
}): Promise<Invitation>;
|
|
1037
1177
|
/**
|
|
@@ -1255,12 +1395,14 @@ declare function getAuthSessionService(userId: string | number | bigint): Promis
|
|
|
1255
1395
|
id: number;
|
|
1256
1396
|
name: string;
|
|
1257
1397
|
displayName: string;
|
|
1258
|
-
category: "
|
|
1398
|
+
category: "auth" | "custom" | "user" | "rbac" | "system" | undefined;
|
|
1259
1399
|
}[];
|
|
1260
1400
|
userId: number;
|
|
1401
|
+
publicId: string;
|
|
1261
1402
|
email: string | null;
|
|
1262
1403
|
emailVerified: boolean;
|
|
1263
1404
|
phoneVerified: boolean;
|
|
1405
|
+
hasPassword: boolean;
|
|
1264
1406
|
}>;
|
|
1265
1407
|
|
|
1266
1408
|
/**
|
|
@@ -1305,6 +1447,16 @@ interface UpdateProfileParams {
|
|
|
1305
1447
|
* ```
|
|
1306
1448
|
*/
|
|
1307
1449
|
declare function getUserProfileService(userId: string | number | bigint): Promise<UserProfile$1>;
|
|
1450
|
+
/**
|
|
1451
|
+
* Update user locale
|
|
1452
|
+
*
|
|
1453
|
+
* @param userId - User ID
|
|
1454
|
+
* @param locale - Locale code (e.g., 'en', 'ko', 'ja')
|
|
1455
|
+
* @returns Updated locale
|
|
1456
|
+
*/
|
|
1457
|
+
declare function updateLocaleService(userId: string | number | bigint, locale: string): Promise<{
|
|
1458
|
+
locale: string;
|
|
1459
|
+
}>;
|
|
1308
1460
|
/**
|
|
1309
1461
|
* Update user profile (upsert)
|
|
1310
1462
|
*
|
|
@@ -1327,385 +1479,582 @@ declare function getUserProfileService(userId: string | number | bigint): Promis
|
|
|
1327
1479
|
declare function updateUserProfileService(userId: string | number | bigint, params: UpdateProfileParams): Promise<ProfileInfo>;
|
|
1328
1480
|
|
|
1329
1481
|
/**
|
|
1330
|
-
*
|
|
1482
|
+
* Google OAuth 2.0 Client
|
|
1331
1483
|
*
|
|
1332
|
-
*
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
*
|
|
1484
|
+
* Authorization Code Flow 구현
|
|
1485
|
+
* - getGoogleAuthUrl: Google 로그인 URL 생성
|
|
1486
|
+
* - exchangeCodeForTokens: Code를 Token으로 교환
|
|
1487
|
+
* - getGoogleUserInfo: 사용자 정보 조회
|
|
1336
1488
|
*/
|
|
1337
|
-
interface
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1489
|
+
interface GoogleTokenResponse {
|
|
1490
|
+
access_token: string;
|
|
1491
|
+
expires_in: number;
|
|
1492
|
+
refresh_token?: string;
|
|
1493
|
+
scope: string;
|
|
1494
|
+
token_type: string;
|
|
1495
|
+
id_token?: string;
|
|
1341
1496
|
}
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1497
|
+
interface GoogleUserInfo {
|
|
1498
|
+
id: string;
|
|
1499
|
+
email: string;
|
|
1500
|
+
verified_email: boolean;
|
|
1501
|
+
name?: string;
|
|
1502
|
+
given_name?: string;
|
|
1503
|
+
family_name?: string;
|
|
1504
|
+
picture?: string;
|
|
1505
|
+
locale?: string;
|
|
1350
1506
|
}
|
|
1351
1507
|
/**
|
|
1352
|
-
*
|
|
1353
|
-
*
|
|
1354
|
-
* Implement this interface to create custom email templates
|
|
1355
|
-
*
|
|
1356
|
-
* @example
|
|
1357
|
-
* ```typescript
|
|
1358
|
-
* import { registerEmailTemplates } from '@spfn/auth/server';
|
|
1359
|
-
*
|
|
1360
|
-
* registerEmailTemplates({
|
|
1361
|
-
* verificationCode: (params) => ({
|
|
1362
|
-
* subject: 'Your Code',
|
|
1363
|
-
* text: `Code: ${params.code}`,
|
|
1364
|
-
* html: `<h1>Code: ${params.code}</h1>`,
|
|
1365
|
-
* }),
|
|
1366
|
-
* });
|
|
1367
|
-
* ```
|
|
1508
|
+
* Google OAuth가 활성화되어 있는지 확인
|
|
1368
1509
|
*/
|
|
1369
|
-
|
|
1370
|
-
/**
|
|
1371
|
-
* Verification code email template
|
|
1372
|
-
*/
|
|
1373
|
-
verificationCode?(params: VerificationCodeParams): EmailTemplateResult;
|
|
1374
|
-
/**
|
|
1375
|
-
* Welcome email template (after registration)
|
|
1376
|
-
*/
|
|
1377
|
-
welcome?(params: {
|
|
1378
|
-
email: string;
|
|
1379
|
-
appName?: string;
|
|
1380
|
-
}): EmailTemplateResult;
|
|
1381
|
-
/**
|
|
1382
|
-
* Password reset email template
|
|
1383
|
-
*/
|
|
1384
|
-
passwordReset?(params: {
|
|
1385
|
-
resetLink: string;
|
|
1386
|
-
expiresInMinutes?: number;
|
|
1387
|
-
appName?: string;
|
|
1388
|
-
}): EmailTemplateResult;
|
|
1389
|
-
/**
|
|
1390
|
-
* Invitation email template
|
|
1391
|
-
*/
|
|
1392
|
-
invitation?(params: {
|
|
1393
|
-
inviteLink: string;
|
|
1394
|
-
inviterName?: string;
|
|
1395
|
-
roleName?: string;
|
|
1396
|
-
appName?: string;
|
|
1397
|
-
}): EmailTemplateResult;
|
|
1398
|
-
}
|
|
1399
|
-
|
|
1510
|
+
declare function isGoogleOAuthEnabled(): boolean;
|
|
1400
1511
|
/**
|
|
1401
|
-
*
|
|
1402
|
-
*
|
|
1403
|
-
* Manages custom email template registration and fallback to defaults
|
|
1512
|
+
* Google OAuth 설정 가져오기
|
|
1404
1513
|
*/
|
|
1405
|
-
|
|
1514
|
+
declare function getGoogleOAuthConfig(): {
|
|
1515
|
+
clientId: string;
|
|
1516
|
+
clientSecret: string;
|
|
1517
|
+
redirectUri: string;
|
|
1518
|
+
};
|
|
1406
1519
|
/**
|
|
1407
|
-
*
|
|
1520
|
+
* Google 로그인 URL 생성
|
|
1408
1521
|
*
|
|
1409
|
-
*
|
|
1410
|
-
*
|
|
1411
|
-
* @param templates - Custom template implementations
|
|
1412
|
-
*
|
|
1413
|
-
* @example
|
|
1414
|
-
* ```typescript
|
|
1415
|
-
* import { registerEmailTemplates } from '@spfn/auth/server';
|
|
1416
|
-
*
|
|
1417
|
-
* // Override verification code template with custom design
|
|
1418
|
-
* registerEmailTemplates({
|
|
1419
|
-
* verificationCode: ({ code, purpose, expiresInMinutes }) => ({
|
|
1420
|
-
* subject: `[MyApp] Your verification code`,
|
|
1421
|
-
* text: `Your code is: ${code}`,
|
|
1422
|
-
* html: `
|
|
1423
|
-
* <div style="font-family: Arial;">
|
|
1424
|
-
* <h1>Welcome to MyApp!</h1>
|
|
1425
|
-
* <p>Your code: <strong>${code}</strong></p>
|
|
1426
|
-
* </div>
|
|
1427
|
-
* `,
|
|
1428
|
-
* }),
|
|
1429
|
-
* });
|
|
1430
|
-
* ```
|
|
1522
|
+
* @param state - CSRF 방지용 state 파라미터 (암호화된 returnUrl + nonce 포함)
|
|
1523
|
+
* @param scopes - 요청할 OAuth scopes (기본: env 또는 email, profile)
|
|
1431
1524
|
*/
|
|
1432
|
-
declare function
|
|
1525
|
+
declare function getGoogleAuthUrl(state: string, scopes?: string[]): string;
|
|
1433
1526
|
/**
|
|
1434
|
-
*
|
|
1527
|
+
* Authorization Code를 Token으로 교환
|
|
1435
1528
|
*
|
|
1436
|
-
*
|
|
1437
|
-
*/
|
|
1438
|
-
declare function getVerificationCodeTemplate(params: VerificationCodeParams): EmailTemplateResult;
|
|
1439
|
-
/**
|
|
1440
|
-
* Get welcome template
|
|
1529
|
+
* @param code - Google에서 받은 authorization code
|
|
1441
1530
|
*/
|
|
1442
|
-
declare function
|
|
1443
|
-
email: string;
|
|
1444
|
-
appName?: string;
|
|
1445
|
-
}): EmailTemplateResult;
|
|
1531
|
+
declare function exchangeCodeForTokens(code: string): Promise<GoogleTokenResponse>;
|
|
1446
1532
|
/**
|
|
1447
|
-
*
|
|
1533
|
+
* Access Token으로 Google 사용자 정보 조회
|
|
1534
|
+
*
|
|
1535
|
+
* @param accessToken - Google access token
|
|
1448
1536
|
*/
|
|
1449
|
-
declare function
|
|
1450
|
-
resetLink: string;
|
|
1451
|
-
expiresInMinutes?: number;
|
|
1452
|
-
appName?: string;
|
|
1453
|
-
}): EmailTemplateResult;
|
|
1537
|
+
declare function getGoogleUserInfo(accessToken: string): Promise<GoogleUserInfo>;
|
|
1454
1538
|
/**
|
|
1455
|
-
*
|
|
1539
|
+
* Refresh Token으로 새 Access Token 획득
|
|
1540
|
+
*
|
|
1541
|
+
* @param refreshToken - Google refresh token
|
|
1456
1542
|
*/
|
|
1457
|
-
declare function
|
|
1458
|
-
inviteLink: string;
|
|
1459
|
-
inviterName?: string;
|
|
1460
|
-
roleName?: string;
|
|
1461
|
-
appName?: string;
|
|
1462
|
-
}): EmailTemplateResult;
|
|
1543
|
+
declare function refreshAccessToken(refreshToken: string): Promise<GoogleTokenResponse>;
|
|
1463
1544
|
|
|
1464
1545
|
/**
|
|
1465
|
-
*
|
|
1546
|
+
* OAuth State Management
|
|
1466
1547
|
*
|
|
1467
|
-
*
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
*
|
|
1548
|
+
* CSRF 방지를 위한 state 파라미터 암호화/복호화
|
|
1549
|
+
* - returnUrl: OAuth 성공 후 리다이렉트할 URL
|
|
1550
|
+
* - nonce: CSRF 방지용 일회용 토큰
|
|
1551
|
+
* - provider: OAuth provider (google, github 등)
|
|
1552
|
+
* - publicKey, keyId, fingerprint, algorithm: 클라이언트 키 정보
|
|
1553
|
+
* - expiresAt: state 만료 시간
|
|
1471
1554
|
*/
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
* Plain text content
|
|
1483
|
-
*/
|
|
1484
|
-
text?: string;
|
|
1485
|
-
/**
|
|
1486
|
-
* HTML content
|
|
1487
|
-
*/
|
|
1488
|
-
html?: string;
|
|
1489
|
-
/**
|
|
1490
|
-
* Purpose of the email (for logging)
|
|
1491
|
-
*/
|
|
1492
|
-
purpose?: string;
|
|
1555
|
+
|
|
1556
|
+
interface OAuthState {
|
|
1557
|
+
returnUrl: string;
|
|
1558
|
+
nonce: string;
|
|
1559
|
+
provider: string;
|
|
1560
|
+
publicKey: string;
|
|
1561
|
+
keyId: string;
|
|
1562
|
+
fingerprint: string;
|
|
1563
|
+
algorithm: KeyAlgorithmType;
|
|
1564
|
+
metadata?: Record<string, unknown>;
|
|
1493
1565
|
}
|
|
1494
1566
|
/**
|
|
1495
|
-
*
|
|
1567
|
+
* Generate a CSRF nonce for the OAuth flow. The caller passes it to
|
|
1568
|
+
* createOAuthState AND sets it as the oauth_csrf cookie, so the callback can
|
|
1569
|
+
* double-submit-verify the flow was initiated by this same browser.
|
|
1496
1570
|
*/
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1571
|
+
declare function generateOAuthNonce(): string;
|
|
1572
|
+
interface CreateOAuthStateParams {
|
|
1573
|
+
provider: string;
|
|
1574
|
+
returnUrl: string;
|
|
1575
|
+
publicKey: string;
|
|
1576
|
+
keyId: string;
|
|
1577
|
+
fingerprint: string;
|
|
1578
|
+
algorithm: KeyAlgorithmType;
|
|
1579
|
+
metadata?: Record<string, unknown>;
|
|
1506
1580
|
/**
|
|
1507
|
-
*
|
|
1581
|
+
* CSRF nonce bound into the state. Pass the same value as the oauth_csrf
|
|
1582
|
+
* cookie. Defaults to a fresh nonce (unbound — legacy/no-CSRF callers).
|
|
1508
1583
|
*/
|
|
1509
|
-
|
|
1584
|
+
nonce?: string;
|
|
1510
1585
|
}
|
|
1511
1586
|
/**
|
|
1512
|
-
*
|
|
1513
|
-
*
|
|
1514
|
-
* Implement this interface to create custom email providers
|
|
1515
|
-
*
|
|
1516
|
-
* @example
|
|
1517
|
-
* ```typescript
|
|
1518
|
-
* import { EmailProvider, registerEmailProvider } from '@spfn/auth/server/services/email';
|
|
1519
|
-
*
|
|
1520
|
-
* const sendgridProvider: EmailProvider = {
|
|
1521
|
-
* name: 'sendgrid',
|
|
1522
|
-
* sendEmail: async (params) => {
|
|
1523
|
-
* // Your SendGrid implementation
|
|
1524
|
-
* return { success: true, messageId: '...' };
|
|
1525
|
-
* }
|
|
1526
|
-
* };
|
|
1587
|
+
* OAuth state 생성 및 암호화
|
|
1527
1588
|
*
|
|
1528
|
-
*
|
|
1529
|
-
*
|
|
1589
|
+
* @param params - state 생성에 필요한 파라미터
|
|
1590
|
+
* @returns 암호화된 state 문자열
|
|
1530
1591
|
*/
|
|
1531
|
-
|
|
1532
|
-
/**
|
|
1533
|
-
* Provider name (e.g., 'aws-ses', 'sendgrid', 'custom')
|
|
1534
|
-
*/
|
|
1535
|
-
name: string;
|
|
1536
|
-
/**
|
|
1537
|
-
* Send email via this provider
|
|
1538
|
-
*
|
|
1539
|
-
* @param params - Email parameters
|
|
1540
|
-
* @returns Send result
|
|
1541
|
-
*/
|
|
1542
|
-
sendEmail(params: SendEmailParams): Promise<SendEmailResult>;
|
|
1543
|
-
}
|
|
1544
|
-
|
|
1592
|
+
declare function createOAuthState(params: CreateOAuthStateParams): Promise<string>;
|
|
1545
1593
|
/**
|
|
1546
|
-
*
|
|
1594
|
+
* OAuth state 복호화 및 검증
|
|
1547
1595
|
*
|
|
1548
|
-
*
|
|
1596
|
+
* @param encryptedState - 암호화된 state 문자열
|
|
1597
|
+
* @returns 복호화된 state 객체
|
|
1598
|
+
* @throws Error if state is invalid or expired (JWE exp claim으로 자동 검증)
|
|
1549
1599
|
*/
|
|
1600
|
+
declare function verifyOAuthState(encryptedState: string): Promise<OAuthState>;
|
|
1550
1601
|
|
|
1551
1602
|
/**
|
|
1552
|
-
*
|
|
1553
|
-
*
|
|
1554
|
-
* @param provider - Custom email provider implementation
|
|
1555
|
-
*
|
|
1556
|
-
* @example
|
|
1557
|
-
* ```typescript
|
|
1558
|
-
* import { registerEmailProvider } from '@spfn/auth/server/services/email';
|
|
1559
|
-
*
|
|
1560
|
-
* const sendgridProvider = {
|
|
1561
|
-
* name: 'sendgrid',
|
|
1562
|
-
* sendEmail: async (params) => {
|
|
1563
|
-
* // SendGrid implementation
|
|
1564
|
-
* return { success: true, messageId: '...' };
|
|
1565
|
-
* }
|
|
1566
|
-
* };
|
|
1567
|
-
*
|
|
1568
|
-
* registerEmailProvider(sendgridProvider);
|
|
1569
|
-
* ```
|
|
1570
|
-
*/
|
|
1571
|
-
declare function registerEmailProvider(provider: EmailProvider): void;
|
|
1572
|
-
/**
|
|
1573
|
-
* Send email using the registered provider
|
|
1603
|
+
* 소셜 provider id_token JWKS 검증
|
|
1574
1604
|
*
|
|
1575
|
-
*
|
|
1576
|
-
*
|
|
1577
|
-
*
|
|
1578
|
-
* @returns Send result
|
|
1605
|
+
* 네이티브/웹 SDK가 받은 id_token을 provider의 공개키(JWKS)로 검증한다.
|
|
1606
|
+
* authorization code 교환이 없으므로 client secret을 쓰지 않는다.
|
|
1607
|
+
* 서명·issuer·audience·만료는 jose가, nonce는 여기서 직접 대조한다.
|
|
1579
1608
|
*/
|
|
1580
|
-
declare function sendEmail(params: SendEmailParams): Promise<SendEmailResult>;
|
|
1581
1609
|
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
/**
|
|
1588
|
-
|
|
1589
|
-
*/
|
|
1590
|
-
|
|
1591
|
-
/**
|
|
1592
|
-
* Phone number in E.164 format (e.g., +821012345678)
|
|
1593
|
-
*/
|
|
1594
|
-
phone: string;
|
|
1595
|
-
/**
|
|
1596
|
-
* SMS message content
|
|
1597
|
-
*/
|
|
1598
|
-
message: string;
|
|
1599
|
-
/**
|
|
1600
|
-
* Purpose of the SMS (for logging)
|
|
1601
|
-
*/
|
|
1602
|
-
purpose?: string;
|
|
1610
|
+
interface VerifyIdTokenParams {
|
|
1611
|
+
idToken: string;
|
|
1612
|
+
jwksUri: string;
|
|
1613
|
+
issuer: string | string[];
|
|
1614
|
+
audiences: string[];
|
|
1615
|
+
/** 허용 서명 알고리즘 화이트리스트(alg confusion 방어). Google/Apple은 'RS256'. */
|
|
1616
|
+
algorithms: string[];
|
|
1617
|
+
/** id_token의 nonce claim과 정확히 일치해야 하는 값(provider별로 raw 또는 SHA-256 해시). */
|
|
1618
|
+
expectedNonce: string;
|
|
1603
1619
|
}
|
|
1604
1620
|
/**
|
|
1605
|
-
*
|
|
1621
|
+
* id_token을 검증하고 claims(payload)를 반환한다.
|
|
1622
|
+
*
|
|
1623
|
+
* @throws InvalidSocialTokenError 서명/issuer/audience/만료/알고리즘/nonce/sub 검증 실패 시
|
|
1606
1624
|
*/
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
* Whether SMS was sent successfully
|
|
1610
|
-
*/
|
|
1611
|
-
success: boolean;
|
|
1612
|
-
/**
|
|
1613
|
-
* Message ID from SMS provider (if successful)
|
|
1614
|
-
*/
|
|
1615
|
-
messageId?: string;
|
|
1616
|
-
/**
|
|
1617
|
-
* Error message (if failed)
|
|
1618
|
-
*/
|
|
1619
|
-
error?: string;
|
|
1620
|
-
}
|
|
1625
|
+
declare function verifyIdToken(params: VerifyIdTokenParams): Promise<JWTPayload>;
|
|
1626
|
+
|
|
1621
1627
|
/**
|
|
1622
|
-
*
|
|
1628
|
+
* @spfn/auth - Social Token At-Rest Encryption
|
|
1623
1629
|
*
|
|
1624
|
-
*
|
|
1630
|
+
* OAuth access/refresh tokens are encrypted with a backend-only rotating keyring.
|
|
1631
|
+
* The active key is the first entry in SPFN_AUTH_TOKEN_ENCRYPTION_KEYS; remaining
|
|
1632
|
+
* keys are decrypt-only grace keys. Ciphertext is bound to its provider account
|
|
1633
|
+
* and token type with AES-GCM additional authenticated data (AAD).
|
|
1625
1634
|
*
|
|
1626
|
-
*
|
|
1627
|
-
*
|
|
1628
|
-
* import { SMSProvider, registerSMSProvider } from '@spfn/auth/server/services/sms';
|
|
1629
|
-
*
|
|
1630
|
-
* const twilioProvider: SMSProvider = {
|
|
1631
|
-
* name: 'twilio',
|
|
1632
|
-
* sendSMS: async (params) => {
|
|
1633
|
-
* // Your Twilio implementation
|
|
1634
|
-
* return { success: true, messageId: '...' };
|
|
1635
|
-
* }
|
|
1636
|
-
* };
|
|
1635
|
+
* v2 format: enc:v2:<keyId>:<base64url(iv | authTag | ciphertext)>
|
|
1636
|
+
* v1 format: enc:v1:<base64(iv | authTag | ciphertext)> (legacy session-secret key)
|
|
1637
1637
|
*
|
|
1638
|
-
*
|
|
1639
|
-
* ```
|
|
1638
|
+
* Never include token or key material in logs or error messages.
|
|
1640
1639
|
*/
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
* Send SMS via this provider
|
|
1648
|
-
*
|
|
1649
|
-
* @param params - SMS parameters
|
|
1650
|
-
* @returns Send result
|
|
1651
|
-
*/
|
|
1652
|
-
sendSMS(params: SendSMSParams): Promise<SendSMSResult>;
|
|
1640
|
+
|
|
1641
|
+
type OAuthTokenType = 'access' | 'refresh';
|
|
1642
|
+
interface OAuthTokenContext {
|
|
1643
|
+
provider: SocialProvider;
|
|
1644
|
+
providerUserId: string;
|
|
1645
|
+
tokenType: OAuthTokenType;
|
|
1653
1646
|
}
|
|
1647
|
+
interface DecryptedOAuthToken {
|
|
1648
|
+
value: string;
|
|
1649
|
+
/** True for plaintext, v1, or ciphertext encrypted with a non-active v2 key. */
|
|
1650
|
+
needsRotation: boolean;
|
|
1651
|
+
}
|
|
1652
|
+
interface OAuthTokenCipher {
|
|
1653
|
+
encrypt(value: string, context: OAuthTokenContext): Promise<string>;
|
|
1654
|
+
decrypt(value: string, context: OAuthTokenContext): Promise<DecryptedOAuthToken>;
|
|
1655
|
+
}
|
|
1656
|
+
declare class EnvironmentKeyringTokenCipher implements OAuthTokenCipher {
|
|
1657
|
+
encrypt(value: string, context: OAuthTokenContext): Promise<string>;
|
|
1658
|
+
decrypt(value: string, context: OAuthTokenContext): Promise<DecryptedOAuthToken>;
|
|
1659
|
+
}
|
|
1660
|
+
/** Configure an external/KMS-backed cipher before the auth server starts. */
|
|
1661
|
+
declare function configureOAuthTokenCipher(cipher: OAuthTokenCipher): void;
|
|
1662
|
+
declare function encryptToken(value: string, context: OAuthTokenContext): Promise<string>;
|
|
1663
|
+
declare function decryptToken(value: string, context: OAuthTokenContext): Promise<DecryptedOAuthToken>;
|
|
1664
|
+
declare function isEncrypted(value: string): boolean;
|
|
1654
1665
|
|
|
1655
1666
|
/**
|
|
1656
|
-
*
|
|
1667
|
+
* Google OAuthProvider 구현
|
|
1668
|
+
*
|
|
1669
|
+
* 기존 google.ts의 함수를 OAuthProvider 인터페이스로 래핑한다.
|
|
1670
|
+
* google.ts 자체는 그대로 유지(테스트·google 전용 route가 직접 의존).
|
|
1657
1671
|
*
|
|
1658
|
-
*
|
|
1672
|
+
* 이 모듈을 import 하는 것만으로 google provider가 registry에 자기 등록된다.
|
|
1659
1673
|
*/
|
|
1660
1674
|
|
|
1675
|
+
declare const googleProvider: OAuthProvider;
|
|
1676
|
+
|
|
1661
1677
|
/**
|
|
1662
|
-
*
|
|
1663
|
-
*
|
|
1664
|
-
* @param provider - Custom SMS provider implementation
|
|
1678
|
+
* Apple OAuthProvider 구현 (native id_token 검증 전용)
|
|
1665
1679
|
*
|
|
1666
|
-
*
|
|
1667
|
-
*
|
|
1668
|
-
*
|
|
1680
|
+
* Apple은 Android·웹에 네이티브 SDK를 제공하지 않으므로, web 흐름(Sign in with Apple JS,
|
|
1681
|
+
* Android Custom Tab)도 결국 id_token을 클라이언트가 받아 서버로 보낸다. 서버는 그 id_token을
|
|
1682
|
+
* Apple JWKS로 검증만 한다(authorization code 교환·client secret 없음).
|
|
1669
1683
|
*
|
|
1670
|
-
*
|
|
1671
|
-
* name: 'twilio',
|
|
1672
|
-
* sendSMS: async (params) => {
|
|
1673
|
-
* // Twilio implementation
|
|
1674
|
-
* return { success: true, messageId: '...' };
|
|
1675
|
-
* }
|
|
1676
|
-
* };
|
|
1684
|
+
* 따라서 web 메서드(getAuthUrl/exchangeCodeForTokens/getUserInfo)는 지원하지 않는다.
|
|
1677
1685
|
*
|
|
1678
|
-
*
|
|
1679
|
-
* ```
|
|
1686
|
+
* 이 모듈을 import 하는 것만으로 apple provider가 registry에 자기 등록된다.
|
|
1680
1687
|
*/
|
|
1681
|
-
|
|
1688
|
+
|
|
1689
|
+
declare const appleProvider: OAuthProvider;
|
|
1690
|
+
|
|
1682
1691
|
/**
|
|
1683
|
-
*
|
|
1684
|
-
*
|
|
1685
|
-
* Falls back to development mode (console only) if no provider is registered
|
|
1686
|
-
*
|
|
1687
|
-
* @param params - SMS parameters
|
|
1688
|
-
* @returns Send result
|
|
1692
|
+
* GitHub OAuthProvider (web authorization-code flow).
|
|
1689
1693
|
*/
|
|
1690
|
-
|
|
1694
|
+
|
|
1695
|
+
declare const githubProvider: OAuthProvider;
|
|
1691
1696
|
|
|
1692
1697
|
/**
|
|
1693
|
-
*
|
|
1694
|
-
*
|
|
1695
|
-
* Defines the 'spfn_auth' PostgreSQL schema for all auth-related tables
|
|
1698
|
+
* Kakao Login OAuthProvider (web authorization-code flow).
|
|
1696
1699
|
*/
|
|
1700
|
+
|
|
1701
|
+
declare const kakaoProvider: OAuthProvider;
|
|
1702
|
+
|
|
1697
1703
|
/**
|
|
1698
|
-
*
|
|
1699
|
-
* Tables: users, roles, permissions, user_invitations, etc.
|
|
1704
|
+
* Naver Login OAuthProvider (web authorization-code flow).
|
|
1700
1705
|
*/
|
|
1701
|
-
|
|
1706
|
+
|
|
1707
|
+
declare const naverProvider: OAuthProvider;
|
|
1702
1708
|
|
|
1703
1709
|
/**
|
|
1704
|
-
* @spfn/auth -
|
|
1705
|
-
*
|
|
1706
|
-
* User profile information (separate from authentication data)
|
|
1710
|
+
* @spfn/auth - Account Deletion Requests Entity
|
|
1707
1711
|
*
|
|
1708
|
-
*
|
|
1712
|
+
* Audit trail for account deletion/recovery. Rows are never deleted — even after
|
|
1713
|
+
* a hard-delete purge — so "who requested/recovered/purged what, when" survives
|
|
1714
|
+
* the user row itself (개인정보보호법 제21조 3항 분리 보존 원칙).
|
|
1715
|
+
*/
|
|
1716
|
+
declare const accountDeletionRequests: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
1717
|
+
name: "account_deletion_requests";
|
|
1718
|
+
schema: string;
|
|
1719
|
+
columns: {
|
|
1720
|
+
createdAt: drizzle_orm_pg_core.PgColumn<{
|
|
1721
|
+
name: "created_at";
|
|
1722
|
+
tableName: "account_deletion_requests";
|
|
1723
|
+
dataType: "date";
|
|
1724
|
+
columnType: "PgTimestamp";
|
|
1725
|
+
data: Date;
|
|
1726
|
+
driverParam: string;
|
|
1727
|
+
notNull: true;
|
|
1728
|
+
hasDefault: true;
|
|
1729
|
+
isPrimaryKey: false;
|
|
1730
|
+
isAutoincrement: false;
|
|
1731
|
+
hasRuntimeDefault: false;
|
|
1732
|
+
enumValues: undefined;
|
|
1733
|
+
baseColumn: never;
|
|
1734
|
+
identity: undefined;
|
|
1735
|
+
generated: undefined;
|
|
1736
|
+
}, {}, {}>;
|
|
1737
|
+
updatedAt: drizzle_orm_pg_core.PgColumn<{
|
|
1738
|
+
name: "updated_at";
|
|
1739
|
+
tableName: "account_deletion_requests";
|
|
1740
|
+
dataType: "date";
|
|
1741
|
+
columnType: "PgTimestamp";
|
|
1742
|
+
data: Date;
|
|
1743
|
+
driverParam: string;
|
|
1744
|
+
notNull: true;
|
|
1745
|
+
hasDefault: true;
|
|
1746
|
+
isPrimaryKey: false;
|
|
1747
|
+
isAutoincrement: false;
|
|
1748
|
+
hasRuntimeDefault: false;
|
|
1749
|
+
enumValues: undefined;
|
|
1750
|
+
baseColumn: never;
|
|
1751
|
+
identity: undefined;
|
|
1752
|
+
generated: undefined;
|
|
1753
|
+
}, {}, {}>;
|
|
1754
|
+
id: drizzle_orm_pg_core.PgColumn<{
|
|
1755
|
+
name: "id";
|
|
1756
|
+
tableName: "account_deletion_requests";
|
|
1757
|
+
dataType: "number";
|
|
1758
|
+
columnType: "PgBigSerial53";
|
|
1759
|
+
data: number;
|
|
1760
|
+
driverParam: number;
|
|
1761
|
+
notNull: true;
|
|
1762
|
+
hasDefault: true;
|
|
1763
|
+
isPrimaryKey: true;
|
|
1764
|
+
isAutoincrement: false;
|
|
1765
|
+
hasRuntimeDefault: false;
|
|
1766
|
+
enumValues: undefined;
|
|
1767
|
+
baseColumn: never;
|
|
1768
|
+
identity: undefined;
|
|
1769
|
+
generated: undefined;
|
|
1770
|
+
}, {}, {}>;
|
|
1771
|
+
userId: drizzle_orm_pg_core.PgColumn<{
|
|
1772
|
+
name: `${string}_id`;
|
|
1773
|
+
tableName: "account_deletion_requests";
|
|
1774
|
+
dataType: "number";
|
|
1775
|
+
columnType: "PgBigInt53";
|
|
1776
|
+
data: number;
|
|
1777
|
+
driverParam: string | number;
|
|
1778
|
+
notNull: false;
|
|
1779
|
+
hasDefault: false;
|
|
1780
|
+
isPrimaryKey: false;
|
|
1781
|
+
isAutoincrement: false;
|
|
1782
|
+
hasRuntimeDefault: false;
|
|
1783
|
+
enumValues: undefined;
|
|
1784
|
+
baseColumn: never;
|
|
1785
|
+
identity: undefined;
|
|
1786
|
+
generated: undefined;
|
|
1787
|
+
}, {}, {}>;
|
|
1788
|
+
userPublicId: drizzle_orm_pg_core.PgColumn<{
|
|
1789
|
+
name: "user_public_id";
|
|
1790
|
+
tableName: "account_deletion_requests";
|
|
1791
|
+
dataType: "string";
|
|
1792
|
+
columnType: "PgText";
|
|
1793
|
+
data: string;
|
|
1794
|
+
driverParam: string;
|
|
1795
|
+
notNull: true;
|
|
1796
|
+
hasDefault: false;
|
|
1797
|
+
isPrimaryKey: false;
|
|
1798
|
+
isAutoincrement: false;
|
|
1799
|
+
hasRuntimeDefault: false;
|
|
1800
|
+
enumValues: [string, ...string[]];
|
|
1801
|
+
baseColumn: never;
|
|
1802
|
+
identity: undefined;
|
|
1803
|
+
generated: undefined;
|
|
1804
|
+
}, {}, {}>;
|
|
1805
|
+
requestedAt: drizzle_orm_pg_core.PgColumn<{
|
|
1806
|
+
name: string;
|
|
1807
|
+
tableName: "account_deletion_requests";
|
|
1808
|
+
dataType: "date";
|
|
1809
|
+
columnType: "PgTimestamp";
|
|
1810
|
+
data: Date;
|
|
1811
|
+
driverParam: string;
|
|
1812
|
+
notNull: true;
|
|
1813
|
+
hasDefault: true;
|
|
1814
|
+
isPrimaryKey: false;
|
|
1815
|
+
isAutoincrement: false;
|
|
1816
|
+
hasRuntimeDefault: false;
|
|
1817
|
+
enumValues: undefined;
|
|
1818
|
+
baseColumn: never;
|
|
1819
|
+
identity: undefined;
|
|
1820
|
+
generated: undefined;
|
|
1821
|
+
}, {}, {}>;
|
|
1822
|
+
purgeScheduledAt: drizzle_orm_pg_core.PgColumn<{
|
|
1823
|
+
name: string;
|
|
1824
|
+
tableName: "account_deletion_requests";
|
|
1825
|
+
dataType: "date";
|
|
1826
|
+
columnType: "PgTimestamp";
|
|
1827
|
+
data: Date;
|
|
1828
|
+
driverParam: string;
|
|
1829
|
+
notNull: true;
|
|
1830
|
+
hasDefault: false;
|
|
1831
|
+
isPrimaryKey: false;
|
|
1832
|
+
isAutoincrement: false;
|
|
1833
|
+
hasRuntimeDefault: false;
|
|
1834
|
+
enumValues: undefined;
|
|
1835
|
+
baseColumn: never;
|
|
1836
|
+
identity: undefined;
|
|
1837
|
+
generated: undefined;
|
|
1838
|
+
}, {}, {}>;
|
|
1839
|
+
status: drizzle_orm_pg_core.PgColumn<{
|
|
1840
|
+
name: string;
|
|
1841
|
+
tableName: "account_deletion_requests";
|
|
1842
|
+
dataType: "string";
|
|
1843
|
+
columnType: "PgText";
|
|
1844
|
+
data: "pending" | "cancelled" | "completed";
|
|
1845
|
+
driverParam: string;
|
|
1846
|
+
notNull: true;
|
|
1847
|
+
hasDefault: true;
|
|
1848
|
+
isPrimaryKey: false;
|
|
1849
|
+
isAutoincrement: false;
|
|
1850
|
+
hasRuntimeDefault: false;
|
|
1851
|
+
enumValues: ["pending", "cancelled", "completed"] & [string, ...string[]];
|
|
1852
|
+
baseColumn: never;
|
|
1853
|
+
identity: undefined;
|
|
1854
|
+
generated: undefined;
|
|
1855
|
+
}, {}, {}>;
|
|
1856
|
+
requestedBy: drizzle_orm_pg_core.PgColumn<{
|
|
1857
|
+
name: string;
|
|
1858
|
+
tableName: "account_deletion_requests";
|
|
1859
|
+
dataType: "string";
|
|
1860
|
+
columnType: "PgText";
|
|
1861
|
+
data: "admin" | "self";
|
|
1862
|
+
driverParam: string;
|
|
1863
|
+
notNull: true;
|
|
1864
|
+
hasDefault: true;
|
|
1865
|
+
isPrimaryKey: false;
|
|
1866
|
+
isAutoincrement: false;
|
|
1867
|
+
hasRuntimeDefault: false;
|
|
1868
|
+
enumValues: ["self", "admin"] & [string, ...string[]];
|
|
1869
|
+
baseColumn: never;
|
|
1870
|
+
identity: undefined;
|
|
1871
|
+
generated: undefined;
|
|
1872
|
+
}, {}, {}>;
|
|
1873
|
+
reason: drizzle_orm_pg_core.PgColumn<{
|
|
1874
|
+
name: "reason";
|
|
1875
|
+
tableName: "account_deletion_requests";
|
|
1876
|
+
dataType: "string";
|
|
1877
|
+
columnType: "PgText";
|
|
1878
|
+
data: string;
|
|
1879
|
+
driverParam: string;
|
|
1880
|
+
notNull: false;
|
|
1881
|
+
hasDefault: false;
|
|
1882
|
+
isPrimaryKey: false;
|
|
1883
|
+
isAutoincrement: false;
|
|
1884
|
+
hasRuntimeDefault: false;
|
|
1885
|
+
enumValues: [string, ...string[]];
|
|
1886
|
+
baseColumn: never;
|
|
1887
|
+
identity: undefined;
|
|
1888
|
+
generated: undefined;
|
|
1889
|
+
}, {}, {}>;
|
|
1890
|
+
cancelledAt: drizzle_orm_pg_core.PgColumn<{
|
|
1891
|
+
name: string;
|
|
1892
|
+
tableName: "account_deletion_requests";
|
|
1893
|
+
dataType: "date";
|
|
1894
|
+
columnType: "PgTimestamp";
|
|
1895
|
+
data: Date;
|
|
1896
|
+
driverParam: string;
|
|
1897
|
+
notNull: false;
|
|
1898
|
+
hasDefault: false;
|
|
1899
|
+
isPrimaryKey: false;
|
|
1900
|
+
isAutoincrement: false;
|
|
1901
|
+
hasRuntimeDefault: false;
|
|
1902
|
+
enumValues: undefined;
|
|
1903
|
+
baseColumn: never;
|
|
1904
|
+
identity: undefined;
|
|
1905
|
+
generated: undefined;
|
|
1906
|
+
}, {}, {}>;
|
|
1907
|
+
completedAt: drizzle_orm_pg_core.PgColumn<{
|
|
1908
|
+
name: string;
|
|
1909
|
+
tableName: "account_deletion_requests";
|
|
1910
|
+
dataType: "date";
|
|
1911
|
+
columnType: "PgTimestamp";
|
|
1912
|
+
data: Date;
|
|
1913
|
+
driverParam: string;
|
|
1914
|
+
notNull: false;
|
|
1915
|
+
hasDefault: false;
|
|
1916
|
+
isPrimaryKey: false;
|
|
1917
|
+
isAutoincrement: false;
|
|
1918
|
+
hasRuntimeDefault: false;
|
|
1919
|
+
enumValues: undefined;
|
|
1920
|
+
baseColumn: never;
|
|
1921
|
+
identity: undefined;
|
|
1922
|
+
generated: undefined;
|
|
1923
|
+
}, {}, {}>;
|
|
1924
|
+
purgeStrategy: drizzle_orm_pg_core.PgColumn<{
|
|
1925
|
+
name: string;
|
|
1926
|
+
tableName: "account_deletion_requests";
|
|
1927
|
+
dataType: "string";
|
|
1928
|
+
columnType: "PgText";
|
|
1929
|
+
data: "anonymize" | "hard-delete";
|
|
1930
|
+
driverParam: string;
|
|
1931
|
+
notNull: false;
|
|
1932
|
+
hasDefault: false;
|
|
1933
|
+
isPrimaryKey: false;
|
|
1934
|
+
isAutoincrement: false;
|
|
1935
|
+
hasRuntimeDefault: false;
|
|
1936
|
+
enumValues: ["anonymize", "hard-delete"] & [string, ...string[]];
|
|
1937
|
+
baseColumn: never;
|
|
1938
|
+
identity: undefined;
|
|
1939
|
+
generated: undefined;
|
|
1940
|
+
}, {}, {}>;
|
|
1941
|
+
};
|
|
1942
|
+
dialect: "pg";
|
|
1943
|
+
}>;
|
|
1944
|
+
type AccountDeletionRequest = typeof accountDeletionRequests.$inferSelect;
|
|
1945
|
+
type NewAccountDeletionRequest = typeof accountDeletionRequests.$inferInsert;
|
|
1946
|
+
|
|
1947
|
+
/**
|
|
1948
|
+
* @spfn/auth - Account Deletion Service
|
|
1949
|
+
*
|
|
1950
|
+
* Request / cancel (recovery) / purge for the account deletion lifecycle.
|
|
1951
|
+
*
|
|
1952
|
+
* Lifecycle:
|
|
1953
|
+
* active --request(reauth)--> pending_deletion --grace expires(cron)--> deleted (anonymize) | row removed (hard-delete)
|
|
1954
|
+
* ^ |
|
|
1955
|
+
* +-------cancel(reauth)---------+ immediate = grace period of 0, same pipeline
|
|
1956
|
+
*/
|
|
1957
|
+
|
|
1958
|
+
interface PendingDeletionInfo {
|
|
1959
|
+
purgeScheduledAt: Date;
|
|
1960
|
+
}
|
|
1961
|
+
/**
|
|
1962
|
+
* Look up the pending deletion request's `purgeScheduledAt` for a `pending_deletion`
|
|
1963
|
+
* user, so login/OAuth/authenticate can surface it on `AccountPendingDeletionError`.
|
|
1964
|
+
* Returns null if there is no pending row (shouldn't normally happen for a user in
|
|
1965
|
+
* that status, but the caller falls back to an error without the date).
|
|
1966
|
+
*
|
|
1967
|
+
* Reads from the write primary, not a replica — this backs the same status gates
|
|
1968
|
+
* that decide whether to grant a session, so a replica-lag window must not let a
|
|
1969
|
+
* just-requested deletion go unnoticed.
|
|
1970
|
+
*/
|
|
1971
|
+
declare function getPendingDeletionInfo(userId: number): Promise<PendingDeletionInfo | null>;
|
|
1972
|
+
interface RequestAccountDeletionParams {
|
|
1973
|
+
/** Who initiated this — 'self' requires re-auth via password/verificationToken; 'admin' does not. */
|
|
1974
|
+
requestedBy: AccountDeletionRequestedBy;
|
|
1975
|
+
password?: string;
|
|
1976
|
+
verificationToken?: string;
|
|
1977
|
+
reason?: string;
|
|
1978
|
+
/** Skip the grace period and purge inline. Self-service requires `deletion.allowSelfImmediate`. */
|
|
1979
|
+
immediate?: boolean;
|
|
1980
|
+
}
|
|
1981
|
+
interface RequestAccountDeletionResult {
|
|
1982
|
+
requestId: number;
|
|
1983
|
+
purgeScheduledAt: Date;
|
|
1984
|
+
}
|
|
1985
|
+
/**
|
|
1986
|
+
* Request account deletion (self-service re-auth gate, or trusted admin/DSR call).
|
|
1987
|
+
*
|
|
1988
|
+
* Sets status -> 'pending_deletion', records the request, revokes every active
|
|
1989
|
+
* session key, and (after commit) emits `auth.deletion.requested` and sends the
|
|
1990
|
+
* request-received notice. With a zero grace period (explicit `immediate: true`,
|
|
1991
|
+
* gated for self-service by `allowSelfImmediate`), purges inline instead of
|
|
1992
|
+
* waiting for the cron sweep.
|
|
1993
|
+
*/
|
|
1994
|
+
declare function requestAccountDeletionService(userId: number, params: RequestAccountDeletionParams): Promise<RequestAccountDeletionResult>;
|
|
1995
|
+
interface CancelAccountDeletionParams {
|
|
1996
|
+
email?: string;
|
|
1997
|
+
phone?: string;
|
|
1998
|
+
password?: string;
|
|
1999
|
+
verificationToken?: string;
|
|
2000
|
+
}
|
|
2001
|
+
interface CancelAccountDeletionResult {
|
|
2002
|
+
userId: string;
|
|
2003
|
+
}
|
|
2004
|
+
/**
|
|
2005
|
+
* Cancel a pending deletion (recovery). All sessions were revoked at request
|
|
2006
|
+
* time, so this is credential-based (no Bearer token) rather than auth-context-based.
|
|
2007
|
+
*
|
|
2008
|
+
* Credential verification always runs before the caller learns whether the
|
|
2009
|
+
* account exists / is pending deletion — otherwise this endpoint could be used
|
|
2010
|
+
* to enumerate accounts or probe deletion status without a valid credential,
|
|
2011
|
+
* the same posture `loginService` takes for missing accounts.
|
|
2012
|
+
*
|
|
2013
|
+
* Claims the `account_deletion_requests` row (conditional `WHERE status =
|
|
2014
|
+
* 'pending'`) before touching `users` — see the comment at that claim below for
|
|
2015
|
+
* why the `users` row must not move until the claim succeeds.
|
|
2016
|
+
*/
|
|
2017
|
+
declare function cancelAccountDeletionService(params: CancelAccountDeletionParams): Promise<CancelAccountDeletionResult>;
|
|
2018
|
+
interface PurgeUserResult {
|
|
2019
|
+
outcome: 'purged' | 'skipped' | 'not-found';
|
|
2020
|
+
}
|
|
2021
|
+
/**
|
|
2022
|
+
* Purge a user's pending deletion request immediately, ignoring `purgeScheduledAt`.
|
|
2023
|
+
* Admin/GDPR-response entry point — the route (if any) belongs to the app.
|
|
2024
|
+
*
|
|
2025
|
+
* @throws DeletionNotRequestedError if the user has no pending request
|
|
2026
|
+
*/
|
|
2027
|
+
declare function purgeUserService(userId: number): Promise<PurgeUserResult>;
|
|
2028
|
+
interface SweepDuePurgesResult {
|
|
2029
|
+
processed: number;
|
|
2030
|
+
purged: number;
|
|
2031
|
+
skipped: number;
|
|
2032
|
+
}
|
|
2033
|
+
/**
|
|
2034
|
+
* Sweep every request whose grace period has elapsed and purge it. Called by the
|
|
2035
|
+
* `auth.deletion.purge` cron job. One user's failure (thrown error, not just an
|
|
2036
|
+
* `onBeforePurge` skip) is logged and does not stop the rest of the sweep — it
|
|
2037
|
+
* stays `pending` and is retried on the next tick.
|
|
2038
|
+
*/
|
|
2039
|
+
declare function sweepDuePurges(now?: Date): Promise<SweepDuePurgesResult>;
|
|
2040
|
+
|
|
2041
|
+
/**
|
|
2042
|
+
* @spfn/auth - Database Schema Definition
|
|
2043
|
+
*
|
|
2044
|
+
* Defines the 'spfn_auth' PostgreSQL schema for all auth-related tables
|
|
2045
|
+
*/
|
|
2046
|
+
/**
|
|
2047
|
+
* Auth schema for all authentication and authorization tables
|
|
2048
|
+
* Tables: users, roles, permissions, user_invitations, etc.
|
|
2049
|
+
*/
|
|
2050
|
+
declare const authSchema: drizzle_orm_pg_core.PgSchema<string>;
|
|
2051
|
+
|
|
2052
|
+
/**
|
|
2053
|
+
* @spfn/auth - User Profiles Entity
|
|
2054
|
+
*
|
|
2055
|
+
* User profile information (separate from authentication data)
|
|
2056
|
+
*
|
|
2057
|
+
* Features:
|
|
1709
2058
|
* - Display name and personal information
|
|
1710
2059
|
* - Avatar and bio
|
|
1711
2060
|
* - Locale and timezone preferences
|
|
@@ -1771,11 +2120,11 @@ declare const userProfiles: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
1771
2120
|
name: `${string}_id`;
|
|
1772
2121
|
tableName: "user_profiles";
|
|
1773
2122
|
dataType: "number";
|
|
1774
|
-
columnType: "
|
|
2123
|
+
columnType: "PgBigInt53";
|
|
1775
2124
|
data: number;
|
|
1776
|
-
driverParam: number;
|
|
2125
|
+
driverParam: string | number;
|
|
1777
2126
|
notNull: true;
|
|
1778
|
-
hasDefault:
|
|
2127
|
+
hasDefault: false;
|
|
1779
2128
|
isPrimaryKey: false;
|
|
1780
2129
|
isAutoincrement: false;
|
|
1781
2130
|
hasRuntimeDefault: false;
|
|
@@ -1791,7 +2140,7 @@ declare const userProfiles: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
1791
2140
|
columnType: "PgText";
|
|
1792
2141
|
data: string;
|
|
1793
2142
|
driverParam: string;
|
|
1794
|
-
notNull:
|
|
2143
|
+
notNull: false;
|
|
1795
2144
|
hasDefault: false;
|
|
1796
2145
|
isPrimaryKey: false;
|
|
1797
2146
|
isAutoincrement: false;
|
|
@@ -2065,11 +2414,11 @@ declare const userPublicKeys: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
2065
2414
|
name: `${string}_id`;
|
|
2066
2415
|
tableName: "user_public_keys";
|
|
2067
2416
|
dataType: "number";
|
|
2068
|
-
columnType: "
|
|
2417
|
+
columnType: "PgBigInt53";
|
|
2069
2418
|
data: number;
|
|
2070
|
-
driverParam: number;
|
|
2419
|
+
driverParam: string | number;
|
|
2071
2420
|
notNull: true;
|
|
2072
|
-
hasDefault:
|
|
2421
|
+
hasDefault: false;
|
|
2073
2422
|
isPrimaryKey: false;
|
|
2074
2423
|
isAutoincrement: false;
|
|
2075
2424
|
hasRuntimeDefault: false;
|
|
@@ -2318,11 +2667,11 @@ declare const userSocialAccounts: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
2318
2667
|
name: `${string}_id`;
|
|
2319
2668
|
tableName: "user_social_accounts";
|
|
2320
2669
|
dataType: "number";
|
|
2321
|
-
columnType: "
|
|
2670
|
+
columnType: "PgBigInt53";
|
|
2322
2671
|
data: number;
|
|
2323
|
-
driverParam: number;
|
|
2672
|
+
driverParam: string | number;
|
|
2324
2673
|
notNull: true;
|
|
2325
|
-
hasDefault:
|
|
2674
|
+
hasDefault: false;
|
|
2326
2675
|
isPrimaryKey: false;
|
|
2327
2676
|
isAutoincrement: false;
|
|
2328
2677
|
hasRuntimeDefault: false;
|
|
@@ -2336,14 +2685,14 @@ declare const userSocialAccounts: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
2336
2685
|
tableName: "user_social_accounts";
|
|
2337
2686
|
dataType: "string";
|
|
2338
2687
|
columnType: "PgText";
|
|
2339
|
-
data: "google" | "github" | "kakao" | "naver";
|
|
2688
|
+
data: "google" | "apple" | "github" | "kakao" | "naver" | "superself";
|
|
2340
2689
|
driverParam: string;
|
|
2341
2690
|
notNull: true;
|
|
2342
2691
|
hasDefault: false;
|
|
2343
2692
|
isPrimaryKey: false;
|
|
2344
2693
|
isAutoincrement: false;
|
|
2345
2694
|
hasRuntimeDefault: false;
|
|
2346
|
-
enumValues: ["google", "github", "kakao", "naver"] & [string, ...string[]];
|
|
2695
|
+
enumValues: ["google", "apple", "github", "kakao", "naver", "superself"] & [string, ...string[]];
|
|
2347
2696
|
baseColumn: never;
|
|
2348
2697
|
identity: undefined;
|
|
2349
2698
|
generated: undefined;
|
|
@@ -2556,14 +2905,14 @@ declare const verificationCodes: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
2556
2905
|
tableName: "verification_codes";
|
|
2557
2906
|
dataType: "string";
|
|
2558
2907
|
columnType: "PgText";
|
|
2559
|
-
data: "registration" | "login" | "password_reset" | "email_change" | "phone_change";
|
|
2908
|
+
data: "registration" | "login" | "password_reset" | "email_change" | "phone_change" | "account_deletion";
|
|
2560
2909
|
driverParam: string;
|
|
2561
2910
|
notNull: true;
|
|
2562
2911
|
hasDefault: false;
|
|
2563
2912
|
isPrimaryKey: false;
|
|
2564
2913
|
isAutoincrement: false;
|
|
2565
2914
|
hasRuntimeDefault: false;
|
|
2566
|
-
enumValues: ["registration", "login", "password_reset", "email_change", "phone_change"] & [string, ...string[]];
|
|
2915
|
+
enumValues: ["registration", "login", "password_reset", "email_change", "phone_change", "account_deletion"] & [string, ...string[]];
|
|
2567
2916
|
baseColumn: never;
|
|
2568
2917
|
identity: undefined;
|
|
2569
2918
|
generated: undefined;
|
|
@@ -2747,7 +3096,7 @@ declare const permissions: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
2747
3096
|
tableName: "permissions";
|
|
2748
3097
|
dataType: "string";
|
|
2749
3098
|
columnType: "PgText";
|
|
2750
|
-
data: "
|
|
3099
|
+
data: "auth" | "custom" | "user" | "rbac" | "system";
|
|
2751
3100
|
driverParam: string;
|
|
2752
3101
|
notNull: false;
|
|
2753
3102
|
hasDefault: false;
|
|
@@ -2905,11 +3254,11 @@ declare const rolePermissions: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
2905
3254
|
name: `${string}_id`;
|
|
2906
3255
|
tableName: "role_permissions";
|
|
2907
3256
|
dataType: "number";
|
|
2908
|
-
columnType: "
|
|
3257
|
+
columnType: "PgBigInt53";
|
|
2909
3258
|
data: number;
|
|
2910
|
-
driverParam: number;
|
|
3259
|
+
driverParam: string | number;
|
|
2911
3260
|
notNull: true;
|
|
2912
|
-
hasDefault:
|
|
3261
|
+
hasDefault: false;
|
|
2913
3262
|
isPrimaryKey: false;
|
|
2914
3263
|
isAutoincrement: false;
|
|
2915
3264
|
hasRuntimeDefault: false;
|
|
@@ -2922,11 +3271,11 @@ declare const rolePermissions: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
2922
3271
|
name: `${string}_id`;
|
|
2923
3272
|
tableName: "role_permissions";
|
|
2924
3273
|
dataType: "number";
|
|
2925
|
-
columnType: "
|
|
3274
|
+
columnType: "PgBigInt53";
|
|
2926
3275
|
data: number;
|
|
2927
|
-
driverParam: number;
|
|
3276
|
+
driverParam: string | number;
|
|
2928
3277
|
notNull: true;
|
|
2929
|
-
hasDefault:
|
|
3278
|
+
hasDefault: false;
|
|
2930
3279
|
isPrimaryKey: false;
|
|
2931
3280
|
isAutoincrement: false;
|
|
2932
3281
|
hasRuntimeDefault: false;
|
|
@@ -3014,11 +3363,11 @@ declare const userPermissions: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
3014
3363
|
name: `${string}_id`;
|
|
3015
3364
|
tableName: "user_permissions";
|
|
3016
3365
|
dataType: "number";
|
|
3017
|
-
columnType: "
|
|
3366
|
+
columnType: "PgBigInt53";
|
|
3018
3367
|
data: number;
|
|
3019
|
-
driverParam: number;
|
|
3368
|
+
driverParam: string | number;
|
|
3020
3369
|
notNull: true;
|
|
3021
|
-
hasDefault:
|
|
3370
|
+
hasDefault: false;
|
|
3022
3371
|
isPrimaryKey: false;
|
|
3023
3372
|
isAutoincrement: false;
|
|
3024
3373
|
hasRuntimeDefault: false;
|
|
@@ -3031,11 +3380,11 @@ declare const userPermissions: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
3031
3380
|
name: `${string}_id`;
|
|
3032
3381
|
tableName: "user_permissions";
|
|
3033
3382
|
dataType: "number";
|
|
3034
|
-
columnType: "
|
|
3383
|
+
columnType: "PgBigInt53";
|
|
3035
3384
|
data: number;
|
|
3036
|
-
driverParam: number;
|
|
3385
|
+
driverParam: string | number;
|
|
3037
3386
|
notNull: true;
|
|
3038
|
-
hasDefault:
|
|
3387
|
+
hasDefault: false;
|
|
3039
3388
|
isPrimaryKey: false;
|
|
3040
3389
|
isAutoincrement: false;
|
|
3041
3390
|
hasRuntimeDefault: false;
|
|
@@ -3101,6 +3450,73 @@ declare const userPermissions: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
3101
3450
|
type UserPermission = typeof userPermissions.$inferSelect;
|
|
3102
3451
|
type NewUserPermission = typeof userPermissions.$inferInsert;
|
|
3103
3452
|
|
|
3453
|
+
/**
|
|
3454
|
+
* @spfn/auth - Auth Metadata Entity
|
|
3455
|
+
*
|
|
3456
|
+
* Key-value store for auth system metadata
|
|
3457
|
+
* Used for storing RBAC config hash and other system-level settings
|
|
3458
|
+
*/
|
|
3459
|
+
declare const authMetadata: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
3460
|
+
name: "auth_metadata";
|
|
3461
|
+
schema: string;
|
|
3462
|
+
columns: {
|
|
3463
|
+
key: drizzle_orm_pg_core.PgColumn<{
|
|
3464
|
+
name: "key";
|
|
3465
|
+
tableName: "auth_metadata";
|
|
3466
|
+
dataType: "string";
|
|
3467
|
+
columnType: "PgText";
|
|
3468
|
+
data: string;
|
|
3469
|
+
driverParam: string;
|
|
3470
|
+
notNull: true;
|
|
3471
|
+
hasDefault: false;
|
|
3472
|
+
isPrimaryKey: true;
|
|
3473
|
+
isAutoincrement: false;
|
|
3474
|
+
hasRuntimeDefault: false;
|
|
3475
|
+
enumValues: [string, ...string[]];
|
|
3476
|
+
baseColumn: never;
|
|
3477
|
+
identity: undefined;
|
|
3478
|
+
generated: undefined;
|
|
3479
|
+
}, {}, {}>;
|
|
3480
|
+
value: drizzle_orm_pg_core.PgColumn<{
|
|
3481
|
+
name: "value";
|
|
3482
|
+
tableName: "auth_metadata";
|
|
3483
|
+
dataType: "string";
|
|
3484
|
+
columnType: "PgText";
|
|
3485
|
+
data: string;
|
|
3486
|
+
driverParam: string;
|
|
3487
|
+
notNull: true;
|
|
3488
|
+
hasDefault: false;
|
|
3489
|
+
isPrimaryKey: false;
|
|
3490
|
+
isAutoincrement: false;
|
|
3491
|
+
hasRuntimeDefault: false;
|
|
3492
|
+
enumValues: [string, ...string[]];
|
|
3493
|
+
baseColumn: never;
|
|
3494
|
+
identity: undefined;
|
|
3495
|
+
generated: undefined;
|
|
3496
|
+
}, {}, {}>;
|
|
3497
|
+
updatedAt: drizzle_orm_pg_core.PgColumn<{
|
|
3498
|
+
name: "updated_at";
|
|
3499
|
+
tableName: "auth_metadata";
|
|
3500
|
+
dataType: "date";
|
|
3501
|
+
columnType: "PgTimestamp";
|
|
3502
|
+
data: Date;
|
|
3503
|
+
driverParam: string;
|
|
3504
|
+
notNull: true;
|
|
3505
|
+
hasDefault: true;
|
|
3506
|
+
isPrimaryKey: false;
|
|
3507
|
+
isAutoincrement: false;
|
|
3508
|
+
hasRuntimeDefault: false;
|
|
3509
|
+
enumValues: undefined;
|
|
3510
|
+
baseColumn: never;
|
|
3511
|
+
identity: undefined;
|
|
3512
|
+
generated: undefined;
|
|
3513
|
+
}, {}, {}>;
|
|
3514
|
+
};
|
|
3515
|
+
dialect: "pg";
|
|
3516
|
+
}>;
|
|
3517
|
+
type AuthMetadataEntity = typeof authMetadata.$inferSelect;
|
|
3518
|
+
type NewAuthMetadataEntity = typeof authMetadata.$inferInsert;
|
|
3519
|
+
|
|
3104
3520
|
/**
|
|
3105
3521
|
* Users Repository
|
|
3106
3522
|
*
|
|
@@ -3122,15 +3538,44 @@ declare class UsersRepository extends BaseRepository {
|
|
|
3122
3538
|
* Read replica 사용
|
|
3123
3539
|
*/
|
|
3124
3540
|
findById(id: number): Promise<{
|
|
3541
|
+
deletedAt: Date | null;
|
|
3542
|
+
deletedBy: string | null;
|
|
3543
|
+
createdAt: Date;
|
|
3544
|
+
updatedAt: Date;
|
|
3545
|
+
id: number;
|
|
3546
|
+
publicId: string;
|
|
3547
|
+
email: string | null;
|
|
3548
|
+
phone: string | null;
|
|
3549
|
+
username: string | null;
|
|
3550
|
+
passwordHash: string | null;
|
|
3551
|
+
passwordChangeRequired: boolean;
|
|
3552
|
+
roleId: number;
|
|
3553
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
3554
|
+
emailVerifiedAt: Date | null;
|
|
3555
|
+
phoneVerifiedAt: Date | null;
|
|
3556
|
+
lastLoginAt: Date | null;
|
|
3557
|
+
}>;
|
|
3558
|
+
/**
|
|
3559
|
+
* ID로 사용자 조회 — Write primary에서 직접 읽는다.
|
|
3560
|
+
*
|
|
3561
|
+
* 복제 지연 창에서 status 전이(예: 삭제 요청으로 pending_deletion 전환)를 놓치면
|
|
3562
|
+
* 안 되는 게이트(OAuth 세션 발급 등)가 사용한다. 일반 조회는 `findById`(replica)를
|
|
3563
|
+
* 계속 사용할 것.
|
|
3564
|
+
*/
|
|
3565
|
+
findByIdOnPrimary(id: number): Promise<{
|
|
3566
|
+
deletedAt: Date | null;
|
|
3567
|
+
deletedBy: string | null;
|
|
3125
3568
|
createdAt: Date;
|
|
3126
3569
|
updatedAt: Date;
|
|
3127
3570
|
id: number;
|
|
3571
|
+
publicId: string;
|
|
3128
3572
|
email: string | null;
|
|
3129
3573
|
phone: string | null;
|
|
3574
|
+
username: string | null;
|
|
3130
3575
|
passwordHash: string | null;
|
|
3131
3576
|
passwordChangeRequired: boolean;
|
|
3132
3577
|
roleId: number;
|
|
3133
|
-
status: "active" | "inactive" | "suspended";
|
|
3578
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
3134
3579
|
emailVerifiedAt: Date | null;
|
|
3135
3580
|
phoneVerifiedAt: Date | null;
|
|
3136
3581
|
lastLoginAt: Date | null;
|
|
@@ -3140,15 +3585,19 @@ declare class UsersRepository extends BaseRepository {
|
|
|
3140
3585
|
* Read replica 사용
|
|
3141
3586
|
*/
|
|
3142
3587
|
findByEmail(email: string): Promise<{
|
|
3588
|
+
deletedAt: Date | null;
|
|
3589
|
+
deletedBy: string | null;
|
|
3143
3590
|
createdAt: Date;
|
|
3144
3591
|
updatedAt: Date;
|
|
3145
3592
|
id: number;
|
|
3593
|
+
publicId: string;
|
|
3146
3594
|
email: string | null;
|
|
3147
3595
|
phone: string | null;
|
|
3596
|
+
username: string | null;
|
|
3148
3597
|
passwordHash: string | null;
|
|
3149
3598
|
passwordChangeRequired: boolean;
|
|
3150
3599
|
roleId: number;
|
|
3151
|
-
status: "active" | "inactive" | "suspended";
|
|
3600
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
3152
3601
|
emailVerifiedAt: Date | null;
|
|
3153
3602
|
phoneVerifiedAt: Date | null;
|
|
3154
3603
|
lastLoginAt: Date | null;
|
|
@@ -3158,15 +3607,63 @@ declare class UsersRepository extends BaseRepository {
|
|
|
3158
3607
|
* Read replica 사용
|
|
3159
3608
|
*/
|
|
3160
3609
|
findByPhone(phone: string): Promise<{
|
|
3610
|
+
deletedAt: Date | null;
|
|
3611
|
+
deletedBy: string | null;
|
|
3161
3612
|
createdAt: Date;
|
|
3162
3613
|
updatedAt: Date;
|
|
3163
3614
|
id: number;
|
|
3615
|
+
publicId: string;
|
|
3164
3616
|
email: string | null;
|
|
3165
3617
|
phone: string | null;
|
|
3618
|
+
username: string | null;
|
|
3166
3619
|
passwordHash: string | null;
|
|
3167
3620
|
passwordChangeRequired: boolean;
|
|
3168
3621
|
roleId: number;
|
|
3169
|
-
status: "active" | "inactive" | "suspended";
|
|
3622
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
3623
|
+
emailVerifiedAt: Date | null;
|
|
3624
|
+
phoneVerifiedAt: Date | null;
|
|
3625
|
+
lastLoginAt: Date | null;
|
|
3626
|
+
}>;
|
|
3627
|
+
/**
|
|
3628
|
+
* 사용자명으로 사용자 조회
|
|
3629
|
+
* Read replica 사용
|
|
3630
|
+
*/
|
|
3631
|
+
findByUsername(username: string): Promise<{
|
|
3632
|
+
deletedAt: Date | null;
|
|
3633
|
+
deletedBy: string | null;
|
|
3634
|
+
createdAt: Date;
|
|
3635
|
+
updatedAt: Date;
|
|
3636
|
+
id: number;
|
|
3637
|
+
publicId: string;
|
|
3638
|
+
email: string | null;
|
|
3639
|
+
phone: string | null;
|
|
3640
|
+
username: string | null;
|
|
3641
|
+
passwordHash: string | null;
|
|
3642
|
+
passwordChangeRequired: boolean;
|
|
3643
|
+
roleId: number;
|
|
3644
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
3645
|
+
emailVerifiedAt: Date | null;
|
|
3646
|
+
phoneVerifiedAt: Date | null;
|
|
3647
|
+
lastLoginAt: Date | null;
|
|
3648
|
+
}>;
|
|
3649
|
+
/**
|
|
3650
|
+
* Public ID(UUID)로 사용자 조회
|
|
3651
|
+
* Read replica 사용
|
|
3652
|
+
*/
|
|
3653
|
+
findByPublicId(publicId: string): Promise<{
|
|
3654
|
+
deletedAt: Date | null;
|
|
3655
|
+
deletedBy: string | null;
|
|
3656
|
+
createdAt: Date;
|
|
3657
|
+
updatedAt: Date;
|
|
3658
|
+
id: number;
|
|
3659
|
+
publicId: string;
|
|
3660
|
+
email: string | null;
|
|
3661
|
+
phone: string | null;
|
|
3662
|
+
username: string | null;
|
|
3663
|
+
passwordHash: string | null;
|
|
3664
|
+
passwordChangeRequired: boolean;
|
|
3665
|
+
roleId: number;
|
|
3666
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
3170
3667
|
emailVerifiedAt: Date | null;
|
|
3171
3668
|
phoneVerifiedAt: Date | null;
|
|
3172
3669
|
lastLoginAt: Date | null;
|
|
@@ -3176,19 +3673,54 @@ declare class UsersRepository extends BaseRepository {
|
|
|
3176
3673
|
* Read replica 사용
|
|
3177
3674
|
*/
|
|
3178
3675
|
findByEmailOrPhone(email?: string, phone?: string): Promise<{
|
|
3676
|
+
deletedAt: Date | null;
|
|
3677
|
+
deletedBy: string | null;
|
|
3179
3678
|
createdAt: Date;
|
|
3180
3679
|
updatedAt: Date;
|
|
3181
3680
|
id: number;
|
|
3681
|
+
publicId: string;
|
|
3182
3682
|
email: string | null;
|
|
3183
3683
|
phone: string | null;
|
|
3684
|
+
username: string | null;
|
|
3184
3685
|
passwordHash: string | null;
|
|
3185
3686
|
passwordChangeRequired: boolean;
|
|
3186
3687
|
roleId: number;
|
|
3187
|
-
status: "active" | "inactive" | "suspended";
|
|
3688
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
3188
3689
|
emailVerifiedAt: Date | null;
|
|
3189
3690
|
phoneVerifiedAt: Date | null;
|
|
3190
3691
|
lastLoginAt: Date | null;
|
|
3191
3692
|
} | null>;
|
|
3693
|
+
/**
|
|
3694
|
+
* ID로 사용자 + Role 조회 (leftJoin)
|
|
3695
|
+
* Read replica 사용
|
|
3696
|
+
*
|
|
3697
|
+
* roleId가 null인 유저는 role: null 반환
|
|
3698
|
+
*/
|
|
3699
|
+
findByIdWithRole(id: number): Promise<{
|
|
3700
|
+
user: {
|
|
3701
|
+
deletedAt: Date | null;
|
|
3702
|
+
deletedBy: string | null;
|
|
3703
|
+
createdAt: Date;
|
|
3704
|
+
updatedAt: Date;
|
|
3705
|
+
id: number;
|
|
3706
|
+
publicId: string;
|
|
3707
|
+
email: string | null;
|
|
3708
|
+
phone: string | null;
|
|
3709
|
+
username: string | null;
|
|
3710
|
+
passwordHash: string | null;
|
|
3711
|
+
passwordChangeRequired: boolean;
|
|
3712
|
+
roleId: number;
|
|
3713
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
3714
|
+
emailVerifiedAt: Date | null;
|
|
3715
|
+
phoneVerifiedAt: Date | null;
|
|
3716
|
+
lastLoginAt: Date | null;
|
|
3717
|
+
};
|
|
3718
|
+
role: {
|
|
3719
|
+
name: string;
|
|
3720
|
+
displayName: string;
|
|
3721
|
+
priority: number;
|
|
3722
|
+
} | null;
|
|
3723
|
+
} | null>;
|
|
3192
3724
|
/**
|
|
3193
3725
|
* 사용자 생성
|
|
3194
3726
|
* Write primary 사용
|
|
@@ -3196,31 +3728,66 @@ declare class UsersRepository extends BaseRepository {
|
|
|
3196
3728
|
create(data: NewUser): Promise<{
|
|
3197
3729
|
email: string | null;
|
|
3198
3730
|
phone: string | null;
|
|
3199
|
-
status: "active" | "inactive" | "suspended";
|
|
3200
3731
|
id: number;
|
|
3201
|
-
|
|
3202
|
-
|
|
3732
|
+
publicId: string;
|
|
3733
|
+
username: string | null;
|
|
3203
3734
|
passwordHash: string | null;
|
|
3204
3735
|
passwordChangeRequired: boolean;
|
|
3205
3736
|
roleId: number;
|
|
3737
|
+
createdAt: Date;
|
|
3738
|
+
updatedAt: Date;
|
|
3739
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
3206
3740
|
emailVerifiedAt: Date | null;
|
|
3207
3741
|
phoneVerifiedAt: Date | null;
|
|
3208
3742
|
lastLoginAt: Date | null;
|
|
3743
|
+
deletedAt: Date | null;
|
|
3744
|
+
deletedBy: string | null;
|
|
3209
3745
|
}>;
|
|
3210
3746
|
/**
|
|
3211
3747
|
* 사용자 정보 업데이트
|
|
3212
3748
|
* Write primary 사용
|
|
3213
3749
|
*/
|
|
3214
3750
|
updateById(id: number, data: Partial<NewUser>): Promise<{
|
|
3751
|
+
deletedAt: Date | null;
|
|
3752
|
+
deletedBy: string | null;
|
|
3215
3753
|
createdAt: Date;
|
|
3216
3754
|
updatedAt: Date;
|
|
3217
3755
|
id: number;
|
|
3756
|
+
publicId: string;
|
|
3218
3757
|
email: string | null;
|
|
3219
3758
|
phone: string | null;
|
|
3759
|
+
username: string | null;
|
|
3220
3760
|
passwordHash: string | null;
|
|
3221
3761
|
passwordChangeRequired: boolean;
|
|
3222
3762
|
roleId: number;
|
|
3223
|
-
status: "active" | "inactive" | "suspended";
|
|
3763
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
3764
|
+
emailVerifiedAt: Date | null;
|
|
3765
|
+
phoneVerifiedAt: Date | null;
|
|
3766
|
+
lastLoginAt: Date | null;
|
|
3767
|
+
}>;
|
|
3768
|
+
/**
|
|
3769
|
+
* 계정 삭제 취소(복구) — `WHERE status = 'pending_deletion'` 조건부 UPDATE.
|
|
3770
|
+
*
|
|
3771
|
+
* cancelAccountDeletionService가 account_deletion_requests claim(조건부
|
|
3772
|
+
* markCancelled)에 성공한 **이후에만** 호출한다. 그 claim이 이미 purge와의
|
|
3773
|
+
* 경합을 해결하므로 이 조건은 방어적 이중 안전장치 — 0 row 매치(= 이미
|
|
3774
|
+
* status가 바뀐 상태) 시 null을 반환하며 예외를 던지지 않는다.
|
|
3775
|
+
* Write primary 사용
|
|
3776
|
+
*/
|
|
3777
|
+
reactivateFromPendingDeletion(id: number): Promise<{
|
|
3778
|
+
deletedAt: Date | null;
|
|
3779
|
+
deletedBy: string | null;
|
|
3780
|
+
createdAt: Date;
|
|
3781
|
+
updatedAt: Date;
|
|
3782
|
+
id: number;
|
|
3783
|
+
publicId: string;
|
|
3784
|
+
email: string | null;
|
|
3785
|
+
phone: string | null;
|
|
3786
|
+
username: string | null;
|
|
3787
|
+
passwordHash: string | null;
|
|
3788
|
+
passwordChangeRequired: boolean;
|
|
3789
|
+
roleId: number;
|
|
3790
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
3224
3791
|
emailVerifiedAt: Date | null;
|
|
3225
3792
|
phoneVerifiedAt: Date | null;
|
|
3226
3793
|
lastLoginAt: Date | null;
|
|
@@ -3230,15 +3797,19 @@ declare class UsersRepository extends BaseRepository {
|
|
|
3230
3797
|
* Write primary 사용
|
|
3231
3798
|
*/
|
|
3232
3799
|
updatePassword(id: number, passwordHash: string, clearPasswordChangeRequired?: boolean): Promise<{
|
|
3800
|
+
deletedAt: Date | null;
|
|
3801
|
+
deletedBy: string | null;
|
|
3233
3802
|
createdAt: Date;
|
|
3234
3803
|
updatedAt: Date;
|
|
3235
3804
|
id: number;
|
|
3805
|
+
publicId: string;
|
|
3236
3806
|
email: string | null;
|
|
3237
3807
|
phone: string | null;
|
|
3808
|
+
username: string | null;
|
|
3238
3809
|
passwordHash: string | null;
|
|
3239
3810
|
passwordChangeRequired: boolean;
|
|
3240
3811
|
roleId: number;
|
|
3241
|
-
status: "active" | "inactive" | "suspended";
|
|
3812
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
3242
3813
|
emailVerifiedAt: Date | null;
|
|
3243
3814
|
phoneVerifiedAt: Date | null;
|
|
3244
3815
|
lastLoginAt: Date | null;
|
|
@@ -3248,15 +3819,19 @@ declare class UsersRepository extends BaseRepository {
|
|
|
3248
3819
|
* Write primary 사용
|
|
3249
3820
|
*/
|
|
3250
3821
|
updateLastLogin(id: number): Promise<{
|
|
3822
|
+
deletedAt: Date | null;
|
|
3823
|
+
deletedBy: string | null;
|
|
3251
3824
|
createdAt: Date;
|
|
3252
3825
|
updatedAt: Date;
|
|
3253
3826
|
id: number;
|
|
3827
|
+
publicId: string;
|
|
3254
3828
|
email: string | null;
|
|
3255
3829
|
phone: string | null;
|
|
3830
|
+
username: string | null;
|
|
3256
3831
|
passwordHash: string | null;
|
|
3257
3832
|
passwordChangeRequired: boolean;
|
|
3258
3833
|
roleId: number;
|
|
3259
|
-
status: "active" | "inactive" | "suspended";
|
|
3834
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
3260
3835
|
emailVerifiedAt: Date | null;
|
|
3261
3836
|
phoneVerifiedAt: Date | null;
|
|
3262
3837
|
lastLoginAt: Date | null;
|
|
@@ -3268,16 +3843,20 @@ declare class UsersRepository extends BaseRepository {
|
|
|
3268
3843
|
deleteById(id: number): Promise<{
|
|
3269
3844
|
email: string | null;
|
|
3270
3845
|
phone: string | null;
|
|
3271
|
-
status: "active" | "inactive" | "suspended";
|
|
3272
3846
|
id: number;
|
|
3273
|
-
|
|
3274
|
-
|
|
3847
|
+
publicId: string;
|
|
3848
|
+
username: string | null;
|
|
3275
3849
|
passwordHash: string | null;
|
|
3276
3850
|
passwordChangeRequired: boolean;
|
|
3277
3851
|
roleId: number;
|
|
3852
|
+
createdAt: Date;
|
|
3853
|
+
updatedAt: Date;
|
|
3854
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
3278
3855
|
emailVerifiedAt: Date | null;
|
|
3279
3856
|
phoneVerifiedAt: Date | null;
|
|
3280
3857
|
lastLoginAt: Date | null;
|
|
3858
|
+
deletedAt: Date | null;
|
|
3859
|
+
deletedBy: string | null;
|
|
3281
3860
|
}>;
|
|
3282
3861
|
/**
|
|
3283
3862
|
* User의 Role과 Permissions 조회 (JOIN)
|
|
@@ -3297,7 +3876,7 @@ declare class UsersRepository extends BaseRepository {
|
|
|
3297
3876
|
id: number;
|
|
3298
3877
|
name: string;
|
|
3299
3878
|
displayName: string;
|
|
3300
|
-
category: "
|
|
3879
|
+
category: "auth" | "custom" | "user" | "rbac" | "system" | undefined;
|
|
3301
3880
|
}[];
|
|
3302
3881
|
}>;
|
|
3303
3882
|
/**
|
|
@@ -3309,9 +3888,12 @@ declare class UsersRepository extends BaseRepository {
|
|
|
3309
3888
|
*/
|
|
3310
3889
|
fetchMinimalUserData(userId: number): Promise<{
|
|
3311
3890
|
userId: number;
|
|
3891
|
+
publicId: string;
|
|
3312
3892
|
email: string | null;
|
|
3893
|
+
username: string | null;
|
|
3313
3894
|
isEmailVerified: boolean;
|
|
3314
3895
|
isPhoneVerified: boolean;
|
|
3896
|
+
hasPassword: boolean;
|
|
3315
3897
|
}>;
|
|
3316
3898
|
/**
|
|
3317
3899
|
* Full user data 조회 (user profile용)
|
|
@@ -3322,7 +3904,9 @@ declare class UsersRepository extends BaseRepository {
|
|
|
3322
3904
|
*/
|
|
3323
3905
|
fetchFullUserData(userId: number): Promise<{
|
|
3324
3906
|
userId: number;
|
|
3907
|
+
publicId: string;
|
|
3325
3908
|
email: string | null;
|
|
3909
|
+
username: string | null;
|
|
3326
3910
|
isEmailVerified: boolean;
|
|
3327
3911
|
isPhoneVerified: boolean;
|
|
3328
3912
|
lastLoginAt: Date | null;
|
|
@@ -3407,24 +3991,45 @@ declare class KeysRepository extends BaseRepository {
|
|
|
3407
3991
|
* Write primary 사용
|
|
3408
3992
|
*/
|
|
3409
3993
|
create(data: NewUserPublicKey): Promise<{
|
|
3410
|
-
|
|
3994
|
+
publicKey: string;
|
|
3411
3995
|
keyId: string;
|
|
3996
|
+
fingerprint: string;
|
|
3997
|
+
algorithm: "ES256" | "RS256";
|
|
3998
|
+
userId: number;
|
|
3412
3999
|
id: number;
|
|
3413
4000
|
isActive: boolean;
|
|
3414
4001
|
createdAt: Date;
|
|
4002
|
+
expiresAt: Date | null;
|
|
4003
|
+
lastUsedAt: Date | null;
|
|
4004
|
+
revokedAt: Date | null;
|
|
4005
|
+
revokedReason: string | null;
|
|
4006
|
+
}>;
|
|
4007
|
+
/**
|
|
4008
|
+
* 공개키 revoke (비활성화)
|
|
4009
|
+
* Write primary 사용
|
|
4010
|
+
*/
|
|
4011
|
+
revokeByKeyIdAndUserId(keyId: string, userId: number, reason: string): Promise<{
|
|
4012
|
+
id: number;
|
|
4013
|
+
userId: number;
|
|
4014
|
+
keyId: string;
|
|
3415
4015
|
publicKey: string;
|
|
3416
4016
|
algorithm: "ES256" | "RS256";
|
|
3417
4017
|
fingerprint: string;
|
|
4018
|
+
isActive: boolean;
|
|
4019
|
+
createdAt: Date;
|
|
3418
4020
|
lastUsedAt: Date | null;
|
|
3419
4021
|
expiresAt: Date | null;
|
|
3420
4022
|
revokedAt: Date | null;
|
|
3421
4023
|
revokedReason: string | null;
|
|
3422
4024
|
}>;
|
|
3423
4025
|
/**
|
|
3424
|
-
* 공개키 revoke (비활성화)
|
|
4026
|
+
* 사용자의 모든 활성 공개키 revoke (비활성화)
|
|
4027
|
+
*
|
|
4028
|
+
* 비번 변경 시 전체 세션 로그아웃에 사용. authenticate는 활성 키만 검증하므로,
|
|
4029
|
+
* revoke된 키로 서명한 기존 세션의 요청은 즉시 401이 된다.
|
|
3425
4030
|
* Write primary 사용
|
|
3426
4031
|
*/
|
|
3427
|
-
|
|
4032
|
+
revokeAllActiveByUserId(userId: number, reason: string): Promise<{
|
|
3428
4033
|
id: number;
|
|
3429
4034
|
userId: number;
|
|
3430
4035
|
keyId: string;
|
|
@@ -3437,25 +4042,33 @@ declare class KeysRepository extends BaseRepository {
|
|
|
3437
4042
|
expiresAt: Date | null;
|
|
3438
4043
|
revokedAt: Date | null;
|
|
3439
4044
|
revokedReason: string | null;
|
|
3440
|
-
}>;
|
|
4045
|
+
}[]>;
|
|
3441
4046
|
/**
|
|
3442
4047
|
* 공개키 삭제
|
|
3443
4048
|
* Write primary 사용
|
|
3444
4049
|
*/
|
|
3445
4050
|
deleteByKeyIdAndUserId(keyId: string, userId: number): Promise<{
|
|
3446
|
-
|
|
4051
|
+
publicKey: string;
|
|
3447
4052
|
keyId: string;
|
|
4053
|
+
fingerprint: string;
|
|
4054
|
+
algorithm: "ES256" | "RS256";
|
|
4055
|
+
userId: number;
|
|
3448
4056
|
id: number;
|
|
3449
4057
|
isActive: boolean;
|
|
3450
4058
|
createdAt: Date;
|
|
3451
|
-
publicKey: string;
|
|
3452
|
-
algorithm: "ES256" | "RS256";
|
|
3453
|
-
fingerprint: string;
|
|
3454
|
-
lastUsedAt: Date | null;
|
|
3455
4059
|
expiresAt: Date | null;
|
|
4060
|
+
lastUsedAt: Date | null;
|
|
3456
4061
|
revokedAt: Date | null;
|
|
3457
4062
|
revokedReason: string | null;
|
|
3458
4063
|
}>;
|
|
4064
|
+
/**
|
|
4065
|
+
* 사용자의 모든 공개키 삭제 (계정 익명화 파기용)
|
|
4066
|
+
*
|
|
4067
|
+
* hard-delete는 FK cascade로 자동 처리되지만, anonymize 모드는 users row를
|
|
4068
|
+
* 남기므로 자식 row를 직접 지워야 한다.
|
|
4069
|
+
* Write primary 사용
|
|
4070
|
+
*/
|
|
4071
|
+
deleteAllByUserId(userId: number): Promise<number>;
|
|
3459
4072
|
/**
|
|
3460
4073
|
* 마지막 사용 시간 업데이트
|
|
3461
4074
|
* Write primary 사용
|
|
@@ -3494,22 +4107,14 @@ declare class KeysRepository extends BaseRepository {
|
|
|
3494
4107
|
}>;
|
|
3495
4108
|
/**
|
|
3496
4109
|
* Primary key로 마지막 사용 시간 업데이트 (authenticate용)
|
|
3497
|
-
* Write primary
|
|
4110
|
+
* Write primary 사용.
|
|
4111
|
+
*
|
|
4112
|
+
* Throttled: only writes when lastUsedAt is stale (older than
|
|
4113
|
+
* LAST_USED_THROTTLE_MS), so a busy key isn't UPDATEd on every request. The
|
|
4114
|
+
* throttle lives in the WHERE clause — atomic, no read-then-write race. No
|
|
4115
|
+
* RETURNING (callers fire-and-forget and discard the row).
|
|
3498
4116
|
*/
|
|
3499
|
-
updateLastUsedById(id: number): Promise<
|
|
3500
|
-
id: number;
|
|
3501
|
-
userId: number;
|
|
3502
|
-
keyId: string;
|
|
3503
|
-
publicKey: string;
|
|
3504
|
-
algorithm: "ES256" | "RS256";
|
|
3505
|
-
fingerprint: string;
|
|
3506
|
-
isActive: boolean;
|
|
3507
|
-
createdAt: Date;
|
|
3508
|
-
lastUsedAt: Date | null;
|
|
3509
|
-
expiresAt: Date | null;
|
|
3510
|
-
revokedAt: Date | null;
|
|
3511
|
-
revokedReason: string | null;
|
|
3512
|
-
}>;
|
|
4117
|
+
updateLastUsedById(id: number): Promise<void>;
|
|
3513
4118
|
}
|
|
3514
4119
|
declare const keysRepository: KeysRepository;
|
|
3515
4120
|
|
|
@@ -3541,7 +4146,7 @@ declare class VerificationCodesRepository extends BaseRepository {
|
|
|
3541
4146
|
target: string;
|
|
3542
4147
|
targetType: "email" | "phone";
|
|
3543
4148
|
code: string;
|
|
3544
|
-
purpose: "registration" | "login" | "password_reset" | "email_change" | "phone_change";
|
|
4149
|
+
purpose: "registration" | "login" | "password_reset" | "email_change" | "phone_change" | "account_deletion";
|
|
3545
4150
|
expiresAt: Date;
|
|
3546
4151
|
usedAt: Date | null;
|
|
3547
4152
|
attempts: number;
|
|
@@ -3557,7 +4162,7 @@ declare class VerificationCodesRepository extends BaseRepository {
|
|
|
3557
4162
|
target: string;
|
|
3558
4163
|
targetType: "email" | "phone";
|
|
3559
4164
|
code: string;
|
|
3560
|
-
purpose: "registration" | "login" | "password_reset" | "email_change" | "phone_change";
|
|
4165
|
+
purpose: "registration" | "login" | "password_reset" | "email_change" | "phone_change" | "account_deletion";
|
|
3561
4166
|
expiresAt: Date;
|
|
3562
4167
|
usedAt: Date | null;
|
|
3563
4168
|
attempts: number;
|
|
@@ -3567,14 +4172,14 @@ declare class VerificationCodesRepository extends BaseRepository {
|
|
|
3567
4172
|
* Write primary 사용
|
|
3568
4173
|
*/
|
|
3569
4174
|
create(data: NewVerificationCode): Promise<{
|
|
4175
|
+
target: string;
|
|
4176
|
+
targetType: "email" | "phone";
|
|
4177
|
+
purpose: "registration" | "login" | "password_reset" | "email_change" | "phone_change" | "account_deletion";
|
|
4178
|
+
code: string;
|
|
3570
4179
|
id: number;
|
|
3571
4180
|
createdAt: Date;
|
|
3572
4181
|
updatedAt: Date;
|
|
3573
4182
|
expiresAt: Date;
|
|
3574
|
-
target: string;
|
|
3575
|
-
targetType: "email" | "phone";
|
|
3576
|
-
code: string;
|
|
3577
|
-
purpose: "registration" | "login" | "password_reset" | "email_change" | "phone_change";
|
|
3578
4183
|
usedAt: Date | null;
|
|
3579
4184
|
attempts: number;
|
|
3580
4185
|
}>;
|
|
@@ -3589,7 +4194,7 @@ declare class VerificationCodesRepository extends BaseRepository {
|
|
|
3589
4194
|
target: string;
|
|
3590
4195
|
targetType: "email" | "phone";
|
|
3591
4196
|
code: string;
|
|
3592
|
-
purpose: "registration" | "login" | "password_reset" | "email_change" | "phone_change";
|
|
4197
|
+
purpose: "registration" | "login" | "password_reset" | "email_change" | "phone_change" | "account_deletion";
|
|
3593
4198
|
expiresAt: Date;
|
|
3594
4199
|
usedAt: Date | null;
|
|
3595
4200
|
attempts: number;
|
|
@@ -3605,7 +4210,7 @@ declare class VerificationCodesRepository extends BaseRepository {
|
|
|
3605
4210
|
target: string;
|
|
3606
4211
|
targetType: "email" | "phone";
|
|
3607
4212
|
code: string;
|
|
3608
|
-
purpose: "registration" | "login" | "password_reset" | "email_change" | "phone_change";
|
|
4213
|
+
purpose: "registration" | "login" | "password_reset" | "email_change" | "phone_change" | "account_deletion";
|
|
3609
4214
|
expiresAt: Date;
|
|
3610
4215
|
usedAt: Date | null;
|
|
3611
4216
|
attempts: number;
|
|
@@ -3615,6 +4220,14 @@ declare class VerificationCodesRepository extends BaseRepository {
|
|
|
3615
4220
|
* Write primary 사용
|
|
3616
4221
|
*/
|
|
3617
4222
|
deleteExpired(): Promise<number>;
|
|
4223
|
+
/**
|
|
4224
|
+
* Target(email/phone)의 모든 인증 코드 삭제 (계정 파기용)
|
|
4225
|
+
*
|
|
4226
|
+
* verificationCodes는 userId FK가 없고 target 텍스트로만 연결되므로, 파기 시
|
|
4227
|
+
* 원래 email/phone을 이 메서드로 직접 정리해야 한다.
|
|
4228
|
+
* Write primary 사용
|
|
4229
|
+
*/
|
|
4230
|
+
deleteByTarget(target: string): Promise<number>;
|
|
3618
4231
|
/**
|
|
3619
4232
|
* Target의 모든 이전 코드 무효화 (새 코드 발급 시)
|
|
3620
4233
|
* Write primary 사용
|
|
@@ -3763,7 +4376,7 @@ declare class PermissionsRepository extends BaseRepository {
|
|
|
3763
4376
|
name: string;
|
|
3764
4377
|
displayName: string;
|
|
3765
4378
|
description: string | null;
|
|
3766
|
-
category: "
|
|
4379
|
+
category: "auth" | "custom" | "user" | "rbac" | "system" | null;
|
|
3767
4380
|
isBuiltin: boolean;
|
|
3768
4381
|
isSystem: boolean;
|
|
3769
4382
|
isActive: boolean;
|
|
@@ -3779,7 +4392,7 @@ declare class PermissionsRepository extends BaseRepository {
|
|
|
3779
4392
|
name: string;
|
|
3780
4393
|
displayName: string;
|
|
3781
4394
|
description: string | null;
|
|
3782
|
-
category: "
|
|
4395
|
+
category: "auth" | "custom" | "user" | "rbac" | "system" | null;
|
|
3783
4396
|
isBuiltin: boolean;
|
|
3784
4397
|
isSystem: boolean;
|
|
3785
4398
|
isActive: boolean;
|
|
@@ -3819,7 +4432,7 @@ declare class PermissionsRepository extends BaseRepository {
|
|
|
3819
4432
|
name: string;
|
|
3820
4433
|
displayName: string;
|
|
3821
4434
|
description: string | null;
|
|
3822
|
-
category: "
|
|
4435
|
+
category: "auth" | "custom" | "user" | "rbac" | "system" | null;
|
|
3823
4436
|
isBuiltin: boolean;
|
|
3824
4437
|
isSystem: boolean;
|
|
3825
4438
|
isActive: boolean;
|
|
@@ -3830,6 +4443,7 @@ declare class PermissionsRepository extends BaseRepository {
|
|
|
3830
4443
|
*/
|
|
3831
4444
|
deleteById(id: number): Promise<{
|
|
3832
4445
|
description: string | null;
|
|
4446
|
+
metadata: Record<string, any> | null;
|
|
3833
4447
|
id: number;
|
|
3834
4448
|
name: string;
|
|
3835
4449
|
displayName: string;
|
|
@@ -3838,8 +4452,7 @@ declare class PermissionsRepository extends BaseRepository {
|
|
|
3838
4452
|
isActive: boolean;
|
|
3839
4453
|
createdAt: Date;
|
|
3840
4454
|
updatedAt: Date;
|
|
3841
|
-
|
|
3842
|
-
category: "custom" | "user" | "auth" | "rbac" | "system" | null;
|
|
4455
|
+
category: "auth" | "custom" | "user" | "rbac" | "system" | null;
|
|
3843
4456
|
}>;
|
|
3844
4457
|
}
|
|
3845
4458
|
declare const permissionsRepository: PermissionsRepository;
|
|
@@ -3884,9 +4497,9 @@ declare class RolePermissionsRepository extends BaseRepository {
|
|
|
3884
4497
|
*/
|
|
3885
4498
|
createMany(data: NewRolePermission[]): Promise<{
|
|
3886
4499
|
id: number;
|
|
4500
|
+
roleId: number;
|
|
3887
4501
|
createdAt: Date;
|
|
3888
4502
|
updatedAt: Date;
|
|
3889
|
-
roleId: number;
|
|
3890
4503
|
permissionId: number;
|
|
3891
4504
|
}[]>;
|
|
3892
4505
|
/**
|
|
@@ -3902,9 +4515,9 @@ declare class RolePermissionsRepository extends BaseRepository {
|
|
|
3902
4515
|
*/
|
|
3903
4516
|
setPermissionsForRole(roleId: number, permissionIds: number[]): Promise<{
|
|
3904
4517
|
id: number;
|
|
4518
|
+
roleId: number;
|
|
3905
4519
|
createdAt: Date;
|
|
3906
4520
|
updatedAt: Date;
|
|
3907
|
-
roleId: number;
|
|
3908
4521
|
permissionId: number;
|
|
3909
4522
|
}[]>;
|
|
3910
4523
|
}
|
|
@@ -3969,10 +4582,10 @@ declare class UserPermissionsRepository extends BaseRepository {
|
|
|
3969
4582
|
id: number;
|
|
3970
4583
|
createdAt: Date;
|
|
3971
4584
|
updatedAt: Date;
|
|
3972
|
-
expiresAt: Date | null;
|
|
3973
4585
|
permissionId: number;
|
|
3974
|
-
granted: boolean;
|
|
3975
4586
|
reason: string | null;
|
|
4587
|
+
expiresAt: Date | null;
|
|
4588
|
+
granted: boolean;
|
|
3976
4589
|
}>;
|
|
3977
4590
|
/**
|
|
3978
4591
|
* 사용자 권한 오버라이드 업데이트
|
|
@@ -3995,10 +4608,10 @@ declare class UserPermissionsRepository extends BaseRepository {
|
|
|
3995
4608
|
id: number;
|
|
3996
4609
|
createdAt: Date;
|
|
3997
4610
|
updatedAt: Date;
|
|
3998
|
-
expiresAt: Date | null;
|
|
3999
4611
|
permissionId: number;
|
|
4000
|
-
granted: boolean;
|
|
4001
4612
|
reason: string | null;
|
|
4613
|
+
expiresAt: Date | null;
|
|
4614
|
+
granted: boolean;
|
|
4002
4615
|
}>;
|
|
4003
4616
|
/**
|
|
4004
4617
|
* 사용자의 모든 권한 오버라이드 삭제
|
|
@@ -4030,7 +4643,7 @@ declare class UserProfilesRepository extends BaseRepository {
|
|
|
4030
4643
|
updatedAt: Date;
|
|
4031
4644
|
id: number;
|
|
4032
4645
|
userId: number;
|
|
4033
|
-
displayName: string;
|
|
4646
|
+
displayName: string | null;
|
|
4034
4647
|
firstName: string | null;
|
|
4035
4648
|
lastName: string | null;
|
|
4036
4649
|
avatarUrl: string | null;
|
|
@@ -4045,6 +4658,10 @@ declare class UserProfilesRepository extends BaseRepository {
|
|
|
4045
4658
|
jobTitle: string | null;
|
|
4046
4659
|
metadata: Record<string, any> | null;
|
|
4047
4660
|
}>;
|
|
4661
|
+
/**
|
|
4662
|
+
* User ID로 locale만 조회 (경량)
|
|
4663
|
+
*/
|
|
4664
|
+
findLocaleByUserId(userId: number): Promise<string>;
|
|
4048
4665
|
/**
|
|
4049
4666
|
* User ID로 프로필 조회
|
|
4050
4667
|
*/
|
|
@@ -4053,7 +4670,7 @@ declare class UserProfilesRepository extends BaseRepository {
|
|
|
4053
4670
|
updatedAt: Date;
|
|
4054
4671
|
id: number;
|
|
4055
4672
|
userId: number;
|
|
4056
|
-
displayName: string;
|
|
4673
|
+
displayName: string | null;
|
|
4057
4674
|
firstName: string | null;
|
|
4058
4675
|
lastName: string | null;
|
|
4059
4676
|
avatarUrl: string | null;
|
|
@@ -4072,9 +4689,10 @@ declare class UserProfilesRepository extends BaseRepository {
|
|
|
4072
4689
|
* 프로필 생성
|
|
4073
4690
|
*/
|
|
4074
4691
|
create(data: NewUserProfile): Promise<{
|
|
4692
|
+
metadata: Record<string, any> | null;
|
|
4075
4693
|
userId: number;
|
|
4076
4694
|
id: number;
|
|
4077
|
-
displayName: string;
|
|
4695
|
+
displayName: string | null;
|
|
4078
4696
|
createdAt: Date;
|
|
4079
4697
|
updatedAt: Date;
|
|
4080
4698
|
firstName: string | null;
|
|
@@ -4089,7 +4707,6 @@ declare class UserProfilesRepository extends BaseRepository {
|
|
|
4089
4707
|
location: string | null;
|
|
4090
4708
|
company: string | null;
|
|
4091
4709
|
jobTitle: string | null;
|
|
4092
|
-
metadata: Record<string, any> | null;
|
|
4093
4710
|
}>;
|
|
4094
4711
|
/**
|
|
4095
4712
|
* 프로필 업데이트 (by ID)
|
|
@@ -4099,7 +4716,7 @@ declare class UserProfilesRepository extends BaseRepository {
|
|
|
4099
4716
|
updatedAt: Date;
|
|
4100
4717
|
id: number;
|
|
4101
4718
|
userId: number;
|
|
4102
|
-
displayName: string;
|
|
4719
|
+
displayName: string | null;
|
|
4103
4720
|
firstName: string | null;
|
|
4104
4721
|
lastName: string | null;
|
|
4105
4722
|
avatarUrl: string | null;
|
|
@@ -4122,7 +4739,7 @@ declare class UserProfilesRepository extends BaseRepository {
|
|
|
4122
4739
|
updatedAt: Date;
|
|
4123
4740
|
id: number;
|
|
4124
4741
|
userId: number;
|
|
4125
|
-
displayName: string;
|
|
4742
|
+
displayName: string | null;
|
|
4126
4743
|
firstName: string | null;
|
|
4127
4744
|
lastName: string | null;
|
|
4128
4745
|
avatarUrl: string | null;
|
|
@@ -4141,9 +4758,10 @@ declare class UserProfilesRepository extends BaseRepository {
|
|
|
4141
4758
|
* 프로필 삭제 (by ID)
|
|
4142
4759
|
*/
|
|
4143
4760
|
deleteById(id: number): Promise<{
|
|
4761
|
+
metadata: Record<string, any> | null;
|
|
4144
4762
|
userId: number;
|
|
4145
4763
|
id: number;
|
|
4146
|
-
displayName: string;
|
|
4764
|
+
displayName: string | null;
|
|
4147
4765
|
createdAt: Date;
|
|
4148
4766
|
updatedAt: Date;
|
|
4149
4767
|
firstName: string | null;
|
|
@@ -4158,15 +4776,15 @@ declare class UserProfilesRepository extends BaseRepository {
|
|
|
4158
4776
|
location: string | null;
|
|
4159
4777
|
company: string | null;
|
|
4160
4778
|
jobTitle: string | null;
|
|
4161
|
-
metadata: Record<string, any> | null;
|
|
4162
4779
|
}>;
|
|
4163
4780
|
/**
|
|
4164
4781
|
* 프로필 삭제 (by User ID)
|
|
4165
4782
|
*/
|
|
4166
4783
|
deleteByUserId(userId: number): Promise<{
|
|
4784
|
+
metadata: Record<string, any> | null;
|
|
4167
4785
|
userId: number;
|
|
4168
4786
|
id: number;
|
|
4169
|
-
displayName: string;
|
|
4787
|
+
displayName: string | null;
|
|
4170
4788
|
createdAt: Date;
|
|
4171
4789
|
updatedAt: Date;
|
|
4172
4790
|
firstName: string | null;
|
|
@@ -4181,7 +4799,6 @@ declare class UserProfilesRepository extends BaseRepository {
|
|
|
4181
4799
|
location: string | null;
|
|
4182
4800
|
company: string | null;
|
|
4183
4801
|
jobTitle: string | null;
|
|
4184
|
-
metadata: Record<string, any> | null;
|
|
4185
4802
|
}>;
|
|
4186
4803
|
/**
|
|
4187
4804
|
* 프로필 Upsert (by User ID)
|
|
@@ -4190,9 +4807,10 @@ declare class UserProfilesRepository extends BaseRepository {
|
|
|
4190
4807
|
* 새로 생성 시 displayName은 필수 (없으면 'User'로 설정)
|
|
4191
4808
|
*/
|
|
4192
4809
|
upsertByUserId(userId: number, data: Partial<Omit<NewUserProfile, 'userId'>>): Promise<{
|
|
4810
|
+
metadata: Record<string, any> | null;
|
|
4193
4811
|
userId: number;
|
|
4194
4812
|
id: number;
|
|
4195
|
-
displayName: string;
|
|
4813
|
+
displayName: string | null;
|
|
4196
4814
|
createdAt: Date;
|
|
4197
4815
|
updatedAt: Date;
|
|
4198
4816
|
firstName: string | null;
|
|
@@ -4207,7 +4825,6 @@ declare class UserProfilesRepository extends BaseRepository {
|
|
|
4207
4825
|
location: string | null;
|
|
4208
4826
|
company: string | null;
|
|
4209
4827
|
jobTitle: string | null;
|
|
4210
|
-
metadata: Record<string, any> | null;
|
|
4211
4828
|
}>;
|
|
4212
4829
|
/**
|
|
4213
4830
|
* User ID로 프로필 데이터 조회 (formatted)
|
|
@@ -4216,7 +4833,7 @@ declare class UserProfilesRepository extends BaseRepository {
|
|
|
4216
4833
|
*/
|
|
4217
4834
|
fetchProfileData(userId: number): Promise<{
|
|
4218
4835
|
profileId: number;
|
|
4219
|
-
displayName: string;
|
|
4836
|
+
displayName: string | null;
|
|
4220
4837
|
firstName: string | null;
|
|
4221
4838
|
lastName: string | null;
|
|
4222
4839
|
avatarUrl: string | null;
|
|
@@ -4335,15 +4952,15 @@ declare class InvitationsRepository extends BaseRepository {
|
|
|
4335
4952
|
*/
|
|
4336
4953
|
create(data: NewInvitation): Promise<{
|
|
4337
4954
|
email: string;
|
|
4338
|
-
|
|
4955
|
+
metadata: Record<string, any> | null;
|
|
4339
4956
|
id: number;
|
|
4957
|
+
roleId: number;
|
|
4340
4958
|
createdAt: Date;
|
|
4341
4959
|
updatedAt: Date;
|
|
4342
|
-
|
|
4343
|
-
metadata: Record<string, any> | null;
|
|
4344
|
-
expiresAt: Date;
|
|
4960
|
+
status: "pending" | "accepted" | "expired" | "cancelled";
|
|
4345
4961
|
token: string;
|
|
4346
4962
|
invitedBy: number;
|
|
4963
|
+
expiresAt: Date;
|
|
4347
4964
|
acceptedAt: Date | null;
|
|
4348
4965
|
cancelledAt: Date | null;
|
|
4349
4966
|
}>;
|
|
@@ -4369,15 +4986,15 @@ declare class InvitationsRepository extends BaseRepository {
|
|
|
4369
4986
|
*/
|
|
4370
4987
|
deleteById(id: number): Promise<{
|
|
4371
4988
|
email: string;
|
|
4372
|
-
|
|
4989
|
+
metadata: Record<string, any> | null;
|
|
4373
4990
|
id: number;
|
|
4991
|
+
roleId: number;
|
|
4374
4992
|
createdAt: Date;
|
|
4375
4993
|
updatedAt: Date;
|
|
4376
|
-
|
|
4377
|
-
metadata: Record<string, any> | null;
|
|
4378
|
-
expiresAt: Date;
|
|
4994
|
+
status: "pending" | "accepted" | "expired" | "cancelled";
|
|
4379
4995
|
token: string;
|
|
4380
4996
|
invitedBy: number;
|
|
4997
|
+
expiresAt: Date;
|
|
4381
4998
|
acceptedAt: Date | null;
|
|
4382
4999
|
cancelledAt: Date | null;
|
|
4383
5000
|
}>;
|
|
@@ -4480,35 +5097,369 @@ declare class InvitationsRepository extends BaseRepository {
|
|
|
4480
5097
|
expiresAt: Date;
|
|
4481
5098
|
acceptedAt: Date | null;
|
|
4482
5099
|
cancelledAt: Date | null;
|
|
4483
|
-
metadata: Record<string, any> | null;
|
|
5100
|
+
metadata: Record<string, any> | null;
|
|
5101
|
+
}>;
|
|
5102
|
+
/**
|
|
5103
|
+
* 초대 취소 (status, metadata 동시 업데이트)
|
|
5104
|
+
*/
|
|
5105
|
+
cancel(id: number, cancelledBy: number, reason?: string, currentMetadata?: Record<string, any> | null): Promise<{
|
|
5106
|
+
createdAt: Date;
|
|
5107
|
+
updatedAt: Date;
|
|
5108
|
+
id: number;
|
|
5109
|
+
email: string;
|
|
5110
|
+
token: string;
|
|
5111
|
+
roleId: number;
|
|
5112
|
+
invitedBy: number;
|
|
5113
|
+
status: "pending" | "accepted" | "expired" | "cancelled";
|
|
5114
|
+
expiresAt: Date;
|
|
5115
|
+
acceptedAt: Date | null;
|
|
5116
|
+
cancelledAt: Date | null;
|
|
5117
|
+
metadata: Record<string, any> | null;
|
|
5118
|
+
}>;
|
|
5119
|
+
}
|
|
5120
|
+
declare const invitationsRepository: InvitationsRepository;
|
|
5121
|
+
|
|
5122
|
+
/**
|
|
5123
|
+
* Social Accounts Repository
|
|
5124
|
+
*
|
|
5125
|
+
* OAuth 소셜 계정 데이터 관리를 위한 Repository
|
|
5126
|
+
* BaseRepository를 상속받아 자동 트랜잭션 컨텍스트 지원 및 Read/Write 분리
|
|
5127
|
+
*/
|
|
5128
|
+
|
|
5129
|
+
/**
|
|
5130
|
+
* Social Accounts Repository 클래스
|
|
5131
|
+
*/
|
|
5132
|
+
declare class SocialAccountsRepository extends BaseRepository {
|
|
5133
|
+
/**
|
|
5134
|
+
* 저장 row 의 토큰을 평문으로 복호화해 반환한다.
|
|
5135
|
+
*
|
|
5136
|
+
* 레거시 평문(마커 없음)이 감지되면 즉시 재암호화해 저장하는
|
|
5137
|
+
* self-healing 마이그레이션을 수행한다. 호출자에게는 항상 평문이 반환되어
|
|
5138
|
+
* 외부 API 계약(평문 토큰)이 유지된다.
|
|
5139
|
+
*/
|
|
5140
|
+
private decryptAccount;
|
|
5141
|
+
/**
|
|
5142
|
+
* provider와 providerUserId로 소셜 계정 조회
|
|
5143
|
+
* Read replica 사용
|
|
5144
|
+
*/
|
|
5145
|
+
findByProviderAndProviderId(provider: SocialProvider, providerUserId: string): Promise<{
|
|
5146
|
+
accessToken: string | null;
|
|
5147
|
+
refreshToken: string | null;
|
|
5148
|
+
userId: number;
|
|
5149
|
+
id: number;
|
|
5150
|
+
createdAt: Date;
|
|
5151
|
+
updatedAt: Date;
|
|
5152
|
+
provider: "google" | "apple" | "github" | "kakao" | "naver" | "superself";
|
|
5153
|
+
providerUserId: string;
|
|
5154
|
+
providerEmail: string | null;
|
|
5155
|
+
tokenExpiresAt: Date | null;
|
|
5156
|
+
} | null>;
|
|
5157
|
+
/**
|
|
5158
|
+
* userId로 모든 소셜 계정 조회
|
|
5159
|
+
* Read replica 사용
|
|
5160
|
+
*/
|
|
5161
|
+
findByUserId(userId: number): Promise<({
|
|
5162
|
+
accessToken: string | null;
|
|
5163
|
+
refreshToken: string | null;
|
|
5164
|
+
userId: number;
|
|
5165
|
+
id: number;
|
|
5166
|
+
createdAt: Date;
|
|
5167
|
+
updatedAt: Date;
|
|
5168
|
+
provider: "google" | "apple" | "github" | "kakao" | "naver" | "superself";
|
|
5169
|
+
providerUserId: string;
|
|
5170
|
+
providerEmail: string | null;
|
|
5171
|
+
tokenExpiresAt: Date | null;
|
|
5172
|
+
} | null)[]>;
|
|
5173
|
+
/**
|
|
5174
|
+
* userId와 provider로 소셜 계정 조회
|
|
5175
|
+
* Read replica 사용
|
|
5176
|
+
*/
|
|
5177
|
+
findByUserIdAndProvider(userId: number, provider: SocialProvider): Promise<{
|
|
5178
|
+
accessToken: string | null;
|
|
5179
|
+
refreshToken: string | null;
|
|
5180
|
+
userId: number;
|
|
5181
|
+
id: number;
|
|
5182
|
+
createdAt: Date;
|
|
5183
|
+
updatedAt: Date;
|
|
5184
|
+
provider: "google" | "apple" | "github" | "kakao" | "naver" | "superself";
|
|
5185
|
+
providerUserId: string;
|
|
5186
|
+
providerEmail: string | null;
|
|
5187
|
+
tokenExpiresAt: Date | null;
|
|
5188
|
+
} | null>;
|
|
5189
|
+
/**
|
|
5190
|
+
* 소셜 계정 생성
|
|
5191
|
+
* Write primary 사용
|
|
5192
|
+
*/
|
|
5193
|
+
create(data: NewUserSocialAccount): Promise<{
|
|
5194
|
+
accessToken: string | null;
|
|
5195
|
+
refreshToken: string | null;
|
|
5196
|
+
userId: number;
|
|
5197
|
+
id: number;
|
|
5198
|
+
createdAt: Date;
|
|
5199
|
+
updatedAt: Date;
|
|
5200
|
+
provider: "google" | "apple" | "github" | "kakao" | "naver" | "superself";
|
|
5201
|
+
providerUserId: string;
|
|
5202
|
+
providerEmail: string | null;
|
|
5203
|
+
tokenExpiresAt: Date | null;
|
|
5204
|
+
} | null>;
|
|
5205
|
+
/**
|
|
5206
|
+
* 토큰 정보 업데이트
|
|
5207
|
+
* Write primary 사용
|
|
5208
|
+
*/
|
|
5209
|
+
updateTokens(id: number, data: {
|
|
5210
|
+
accessToken?: string | null;
|
|
5211
|
+
refreshToken?: string | null;
|
|
5212
|
+
tokenExpiresAt?: Date | null;
|
|
5213
|
+
}): Promise<{
|
|
5214
|
+
accessToken: string | null;
|
|
5215
|
+
refreshToken: string | null;
|
|
5216
|
+
userId: number;
|
|
5217
|
+
id: number;
|
|
5218
|
+
createdAt: Date;
|
|
5219
|
+
updatedAt: Date;
|
|
5220
|
+
provider: "google" | "apple" | "github" | "kakao" | "naver" | "superself";
|
|
5221
|
+
providerUserId: string;
|
|
5222
|
+
providerEmail: string | null;
|
|
5223
|
+
tokenExpiresAt: Date | null;
|
|
5224
|
+
} | null>;
|
|
5225
|
+
/**
|
|
5226
|
+
* 소셜 계정 삭제
|
|
5227
|
+
* Write primary 사용
|
|
5228
|
+
*/
|
|
5229
|
+
deleteById(id: number): Promise<{
|
|
5230
|
+
userId: number;
|
|
5231
|
+
id: number;
|
|
5232
|
+
createdAt: Date;
|
|
5233
|
+
updatedAt: Date;
|
|
5234
|
+
provider: "google" | "apple" | "github" | "kakao" | "naver" | "superself";
|
|
5235
|
+
providerUserId: string;
|
|
5236
|
+
providerEmail: string | null;
|
|
5237
|
+
accessToken: string | null;
|
|
5238
|
+
refreshToken: string | null;
|
|
5239
|
+
tokenExpiresAt: Date | null;
|
|
5240
|
+
}>;
|
|
5241
|
+
/**
|
|
5242
|
+
* userId와 provider로 소셜 계정 삭제
|
|
5243
|
+
* Write primary 사용
|
|
5244
|
+
*/
|
|
5245
|
+
deleteByUserIdAndProvider(userId: number, provider: SocialProvider): Promise<{
|
|
5246
|
+
userId: number;
|
|
5247
|
+
id: number;
|
|
5248
|
+
createdAt: Date;
|
|
5249
|
+
updatedAt: Date;
|
|
5250
|
+
provider: "google" | "apple" | "github" | "kakao" | "naver" | "superself";
|
|
5251
|
+
providerUserId: string;
|
|
5252
|
+
providerEmail: string | null;
|
|
5253
|
+
accessToken: string | null;
|
|
5254
|
+
refreshToken: string | null;
|
|
5255
|
+
tokenExpiresAt: Date | null;
|
|
5256
|
+
}>;
|
|
5257
|
+
/**
|
|
5258
|
+
* 사용자의 모든 소셜 계정 삭제 (계정 익명화 파기용)
|
|
5259
|
+
*
|
|
5260
|
+
* provider unique index(provider, providerUserId)를 해제해 같은 소셜 계정으로
|
|
5261
|
+
* 재가입할 수 있게 한다.
|
|
5262
|
+
* Write primary 사용
|
|
5263
|
+
*/
|
|
5264
|
+
deleteAllByUserId(userId: number): Promise<number>;
|
|
5265
|
+
}
|
|
5266
|
+
declare const socialAccountsRepository: SocialAccountsRepository;
|
|
5267
|
+
|
|
5268
|
+
/**
|
|
5269
|
+
* Auth Metadata Repository
|
|
5270
|
+
*
|
|
5271
|
+
* Key-value 기반 시스템 메타데이터 저장소
|
|
5272
|
+
* RBAC 설정 해시 등 시스템 설정값 관리
|
|
5273
|
+
*/
|
|
5274
|
+
|
|
5275
|
+
declare class AuthMetadataRepository extends BaseRepository {
|
|
5276
|
+
/**
|
|
5277
|
+
* 키로 값 조회
|
|
5278
|
+
*/
|
|
5279
|
+
get(key: string): Promise<string | null>;
|
|
5280
|
+
/**
|
|
5281
|
+
* 키-값 저장 (upsert)
|
|
5282
|
+
*/
|
|
5283
|
+
set(key: string, value: string): Promise<void>;
|
|
5284
|
+
}
|
|
5285
|
+
declare const authMetadataRepository: AuthMetadataRepository;
|
|
5286
|
+
|
|
5287
|
+
/**
|
|
5288
|
+
* Account Deletion Requests Repository
|
|
5289
|
+
*
|
|
5290
|
+
* 계정 탈퇴 요청/복구/파기 감사 이력 관리를 위한 Repository
|
|
5291
|
+
* BaseRepository를 상속받아 자동 트랜잭션 컨텍스트 지원 및 Read/Write 분리
|
|
5292
|
+
*
|
|
5293
|
+
* Row는 절대 삭제하지 않는다 — status 전이(pending -> cancelled|completed)로만 갱신.
|
|
5294
|
+
*/
|
|
5295
|
+
|
|
5296
|
+
/**
|
|
5297
|
+
* Account Deletion Requests Repository 클래스
|
|
5298
|
+
*/
|
|
5299
|
+
declare class AccountDeletionRequestsRepository extends BaseRepository {
|
|
5300
|
+
/**
|
|
5301
|
+
* ID로 요청 조회
|
|
5302
|
+
* Read replica 사용
|
|
5303
|
+
*/
|
|
5304
|
+
findById(id: number): Promise<{
|
|
5305
|
+
createdAt: Date;
|
|
5306
|
+
updatedAt: Date;
|
|
5307
|
+
id: number;
|
|
5308
|
+
userId: number | null;
|
|
5309
|
+
userPublicId: string;
|
|
5310
|
+
requestedAt: Date;
|
|
5311
|
+
purgeScheduledAt: Date;
|
|
5312
|
+
status: "pending" | "cancelled" | "completed";
|
|
5313
|
+
requestedBy: "admin" | "self";
|
|
5314
|
+
reason: string | null;
|
|
5315
|
+
cancelledAt: Date | null;
|
|
5316
|
+
completedAt: Date | null;
|
|
5317
|
+
purgeStrategy: "anonymize" | "hard-delete" | null;
|
|
5318
|
+
}>;
|
|
5319
|
+
/**
|
|
5320
|
+
* User ID로 pending 요청 조회 (유저당 최대 1건, partial unique index로 보장)
|
|
5321
|
+
* Read replica 사용
|
|
5322
|
+
*/
|
|
5323
|
+
findPendingByUserId(userId: number): Promise<{
|
|
5324
|
+
createdAt: Date;
|
|
5325
|
+
updatedAt: Date;
|
|
5326
|
+
id: number;
|
|
5327
|
+
userId: number | null;
|
|
5328
|
+
userPublicId: string;
|
|
5329
|
+
requestedAt: Date;
|
|
5330
|
+
purgeScheduledAt: Date;
|
|
5331
|
+
status: "pending" | "cancelled" | "completed";
|
|
5332
|
+
requestedBy: "admin" | "self";
|
|
5333
|
+
reason: string | null;
|
|
5334
|
+
cancelledAt: Date | null;
|
|
5335
|
+
completedAt: Date | null;
|
|
5336
|
+
purgeStrategy: "anonymize" | "hard-delete" | null;
|
|
5337
|
+
}>;
|
|
5338
|
+
/**
|
|
5339
|
+
* User ID로 pending 요청 조회 — Write primary에서 직접 읽는다.
|
|
5340
|
+
*
|
|
5341
|
+
* 로그인/OAuth/authenticate 게이트가 pending_deletion 유저의 purgeScheduledAt을
|
|
5342
|
+
* 표시하는 데 쓴다. 이 경로들은 복제 지연이 있으면 방금 커밋된 상태 전이를 놓칠 수
|
|
5343
|
+
* 있어(예: 삭제 요청 직후 OAuth 로그인 시도), replica가 아닌 primary에서 읽는다.
|
|
5344
|
+
*/
|
|
5345
|
+
findPendingByUserIdOnPrimary(userId: number): Promise<{
|
|
5346
|
+
createdAt: Date;
|
|
5347
|
+
updatedAt: Date;
|
|
5348
|
+
id: number;
|
|
5349
|
+
userId: number | null;
|
|
5350
|
+
userPublicId: string;
|
|
5351
|
+
requestedAt: Date;
|
|
5352
|
+
purgeScheduledAt: Date;
|
|
5353
|
+
status: "pending" | "cancelled" | "completed";
|
|
5354
|
+
requestedBy: "admin" | "self";
|
|
5355
|
+
reason: string | null;
|
|
5356
|
+
cancelledAt: Date | null;
|
|
5357
|
+
completedAt: Date | null;
|
|
5358
|
+
purgeStrategy: "anonymize" | "hard-delete" | null;
|
|
5359
|
+
}>;
|
|
5360
|
+
/**
|
|
5361
|
+
* 파기 스윕 대상 조회 — status='pending' AND purgeScheduledAt <= now
|
|
5362
|
+
* Read replica 사용
|
|
5363
|
+
*/
|
|
5364
|
+
findDueForPurge(now: Date): Promise<{
|
|
5365
|
+
createdAt: Date;
|
|
5366
|
+
updatedAt: Date;
|
|
5367
|
+
id: number;
|
|
5368
|
+
userId: number | null;
|
|
5369
|
+
userPublicId: string;
|
|
5370
|
+
requestedAt: Date;
|
|
5371
|
+
purgeScheduledAt: Date;
|
|
5372
|
+
status: "pending" | "cancelled" | "completed";
|
|
5373
|
+
requestedBy: "admin" | "self";
|
|
5374
|
+
reason: string | null;
|
|
5375
|
+
cancelledAt: Date | null;
|
|
5376
|
+
completedAt: Date | null;
|
|
5377
|
+
purgeStrategy: "anonymize" | "hard-delete" | null;
|
|
5378
|
+
}[]>;
|
|
5379
|
+
/**
|
|
5380
|
+
* 요청 생성
|
|
5381
|
+
* Write primary 사용
|
|
5382
|
+
*/
|
|
5383
|
+
create(data: NewAccountDeletionRequest): Promise<{
|
|
5384
|
+
userId: number | null;
|
|
5385
|
+
id: number;
|
|
5386
|
+
createdAt: Date;
|
|
5387
|
+
updatedAt: Date;
|
|
5388
|
+
status: "pending" | "cancelled" | "completed";
|
|
5389
|
+
reason: string | null;
|
|
5390
|
+
purgeScheduledAt: Date;
|
|
5391
|
+
cancelledAt: Date | null;
|
|
5392
|
+
userPublicId: string;
|
|
5393
|
+
requestedAt: Date;
|
|
5394
|
+
requestedBy: "admin" | "self";
|
|
5395
|
+
completedAt: Date | null;
|
|
5396
|
+
purgeStrategy: "anonymize" | "hard-delete" | null;
|
|
5397
|
+
}>;
|
|
5398
|
+
/**
|
|
5399
|
+
* 요청 취소 (복구)
|
|
5400
|
+
*
|
|
5401
|
+
* `WHERE status = 'pending'` 조건부 UPDATE — 이미 completed(파기 완료)된 row를
|
|
5402
|
+
* 뒤늦게 도착한 cancel이 덮어쓰지 않도록 한다. 0 row 매치(= 이미 completed거나
|
|
5403
|
+
* cancelled) 시 null을 반환하니 호출자가 그 결과를 확인해야 한다.
|
|
5404
|
+
* Write primary 사용
|
|
5405
|
+
*/
|
|
5406
|
+
markCancelled(id: number): Promise<{
|
|
5407
|
+
createdAt: Date;
|
|
5408
|
+
updatedAt: Date;
|
|
5409
|
+
id: number;
|
|
5410
|
+
userId: number | null;
|
|
5411
|
+
userPublicId: string;
|
|
5412
|
+
requestedAt: Date;
|
|
5413
|
+
purgeScheduledAt: Date;
|
|
5414
|
+
status: "pending" | "cancelled" | "completed";
|
|
5415
|
+
requestedBy: "admin" | "self";
|
|
5416
|
+
reason: string | null;
|
|
5417
|
+
cancelledAt: Date | null;
|
|
5418
|
+
completedAt: Date | null;
|
|
5419
|
+
purgeStrategy: "anonymize" | "hard-delete" | null;
|
|
4484
5420
|
}>;
|
|
4485
5421
|
/**
|
|
4486
|
-
*
|
|
5422
|
+
* 요청 완료 처리 (파기 실행 직전에 호출하는 "claim" — 파기 실행 후가 아니다)
|
|
5423
|
+
*
|
|
5424
|
+
* `WHERE status = 'pending'` 조건부 UPDATE. Postgres UPDATE는 매치되는 row에 락을
|
|
5425
|
+
* 걸고 최신 커밋 상태로 WHERE를 재평가하므로, 이 호출 하나로 (1) 동시에 취소된
|
|
5426
|
+
* row를 안전하게 걸러내고 (2) row-level 락으로 동시 파기 시도를 직렬화한다.
|
|
5427
|
+
* 0 row 매치(= 이미 취소되었거나 다른 파기가 먼저 처리함) 시 null을 반환 — 호출자는
|
|
5428
|
+
* destructive DML을 실행하기 **전에** 반드시 이 결과를 확인해야 한다.
|
|
5429
|
+
* Write primary 사용
|
|
4487
5430
|
*/
|
|
4488
|
-
|
|
5431
|
+
markCompleted(id: number, purgeStrategy: PurgeStrategy): Promise<{
|
|
4489
5432
|
createdAt: Date;
|
|
4490
5433
|
updatedAt: Date;
|
|
4491
5434
|
id: number;
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
status: "pending" | "
|
|
4497
|
-
|
|
4498
|
-
|
|
5435
|
+
userId: number | null;
|
|
5436
|
+
userPublicId: string;
|
|
5437
|
+
requestedAt: Date;
|
|
5438
|
+
purgeScheduledAt: Date;
|
|
5439
|
+
status: "pending" | "cancelled" | "completed";
|
|
5440
|
+
requestedBy: "admin" | "self";
|
|
5441
|
+
reason: string | null;
|
|
4499
5442
|
cancelledAt: Date | null;
|
|
4500
|
-
|
|
5443
|
+
completedAt: Date | null;
|
|
5444
|
+
purgeStrategy: "anonymize" | "hard-delete" | null;
|
|
4501
5445
|
}>;
|
|
4502
5446
|
}
|
|
4503
|
-
declare const
|
|
5447
|
+
declare const accountDeletionRequestsRepository: AccountDeletionRequestsRepository;
|
|
4504
5448
|
|
|
4505
5449
|
/**
|
|
4506
5450
|
* @spfn/auth - Password Helpers
|
|
4507
5451
|
*
|
|
4508
|
-
* Password hashing and verification using bcrypt
|
|
5452
|
+
* Password hashing and verification using bcrypt (@node-rs/bcrypt).
|
|
5453
|
+
*
|
|
5454
|
+
* Uses the native (Rust/napi) implementation, which runs the CPU-bound key
|
|
5455
|
+
* derivation on the libuv threadpool instead of the main event loop — so
|
|
5456
|
+
* concurrent logins run in parallel and don't head-of-line-block other requests.
|
|
5457
|
+
* Hashes are standard bcrypt ($2*$) and verify against existing bcryptjs hashes.
|
|
5458
|
+
* For very high concurrent-login load, raise UV_THREADPOOL_SIZE toward the core
|
|
5459
|
+
* count (default pool is 4).
|
|
4509
5460
|
*
|
|
4510
5461
|
* Security:
|
|
4511
|
-
* - Adaptive hashing (configurable rounds)
|
|
5462
|
+
* - Adaptive hashing (configurable rounds, default 12)
|
|
4512
5463
|
* - Automatic salt generation (per-password)
|
|
4513
5464
|
* - Constant-time comparison (timing attack protection)
|
|
4514
5465
|
* - Rainbow table protection
|
|
@@ -4521,10 +5472,10 @@ declare const invitationsRepository: InvitationsRepository;
|
|
|
4521
5472
|
* 2. Apply bcrypt key derivation (2^rounds iterations)
|
|
4522
5473
|
* 3. Return $2b$rounds$[salt][hash] (60 chars)
|
|
4523
5474
|
*
|
|
4524
|
-
* Salt rounds are configured via SPFN_AUTH_BCRYPT_SALT_ROUNDS:
|
|
4525
|
-
* -
|
|
4526
|
-
* -
|
|
4527
|
-
* - 14 rounds: ~
|
|
5475
|
+
* Salt rounds are configured via SPFN_AUTH_BCRYPT_SALT_ROUNDS (native timings):
|
|
5476
|
+
* - 12 rounds: ~200ms (default — OWASP-aligned, off the event loop)
|
|
5477
|
+
* - 10 rounds: ~55ms (faster, lower work factor)
|
|
5478
|
+
* - 14 rounds: ~800ms (very secure, heavy)
|
|
4528
5479
|
*
|
|
4529
5480
|
* @param password - Plain text password to hash
|
|
4530
5481
|
* @returns Bcrypt hash string (includes salt)
|
|
@@ -4558,6 +5509,7 @@ declare function hashPassword(password: string): Promise<string>;
|
|
|
4558
5509
|
* ```
|
|
4559
5510
|
*/
|
|
4560
5511
|
declare function verifyPassword(password: string, hash: string): Promise<boolean>;
|
|
5512
|
+
declare function getDummyPasswordHash(): Promise<string>;
|
|
4561
5513
|
/**
|
|
4562
5514
|
* Validate password strength
|
|
4563
5515
|
*
|
|
@@ -4846,6 +5798,29 @@ interface RoleGuardOptions {
|
|
|
4846
5798
|
*/
|
|
4847
5799
|
declare const roleGuard: _spfn_core_route.NamedMiddlewareFactory<"roleGuard", [options: RoleGuardOptions]>;
|
|
4848
5800
|
|
|
5801
|
+
/**
|
|
5802
|
+
* One-Time Token Authentication Middleware
|
|
5803
|
+
*
|
|
5804
|
+
* Authenticates requests using a one-time token instead of JWT.
|
|
5805
|
+
* Extracts token from query parameter `?token=xxx` or `Authorization: OTT xxx` header.
|
|
5806
|
+
*
|
|
5807
|
+
* On success, injects AuthContext identical to the `authenticate` middleware,
|
|
5808
|
+
* making it transparent to downstream handlers using `getAuth(c)`.
|
|
5809
|
+
*
|
|
5810
|
+
* Auto-skips the global 'auth' middleware.
|
|
5811
|
+
*
|
|
5812
|
+
* @example
|
|
5813
|
+
* ```typescript
|
|
5814
|
+
* export const uploadFile = route.post('/files/upload')
|
|
5815
|
+
* .use([oneTimeTokenAuth])
|
|
5816
|
+
* .handler(async (c) => {
|
|
5817
|
+
* const { userId } = getAuth(c);
|
|
5818
|
+
* // handle file upload...
|
|
5819
|
+
* });
|
|
5820
|
+
* ```
|
|
5821
|
+
*/
|
|
5822
|
+
declare const oneTimeTokenAuth: _spfn_core_route.NamedMiddleware<"oneTimeTokenAuth">;
|
|
5823
|
+
|
|
4849
5824
|
/**
|
|
4850
5825
|
* Auth Context Helpers
|
|
4851
5826
|
*
|
|
@@ -4872,6 +5847,32 @@ declare const roleGuard: _spfn_core_route.NamedMiddlewareFactory<"roleGuard", [o
|
|
|
4872
5847
|
declare function getAuth(c: Context | {
|
|
4873
5848
|
raw: Context;
|
|
4874
5849
|
}): AuthContext;
|
|
5850
|
+
/**
|
|
5851
|
+
* Get optional auth context from route context
|
|
5852
|
+
*
|
|
5853
|
+
* Returns AuthContext if authenticated, undefined otherwise.
|
|
5854
|
+
* Use with `optionalAuth` middleware for routes that serve both
|
|
5855
|
+
* authenticated and unauthenticated users.
|
|
5856
|
+
*
|
|
5857
|
+
* @example
|
|
5858
|
+
* ```typescript
|
|
5859
|
+
* export const getProducts = route.get('/products')
|
|
5860
|
+
* .use([optionalAuth])
|
|
5861
|
+
* .handler(async (c) => {
|
|
5862
|
+
* const auth = getOptionalAuth(c);
|
|
5863
|
+
*
|
|
5864
|
+
* if (auth)
|
|
5865
|
+
* {
|
|
5866
|
+
* return getPersonalizedProducts(auth.userId);
|
|
5867
|
+
* }
|
|
5868
|
+
*
|
|
5869
|
+
* return getPublicProducts();
|
|
5870
|
+
* });
|
|
5871
|
+
* ```
|
|
5872
|
+
*/
|
|
5873
|
+
declare function getOptionalAuth(c: Context | {
|
|
5874
|
+
raw: Context;
|
|
5875
|
+
}): AuthContext | undefined;
|
|
4875
5876
|
/**
|
|
4876
5877
|
* Get authenticated user from route context
|
|
4877
5878
|
*
|
|
@@ -4888,16 +5889,20 @@ declare function getUser(c: Context | {
|
|
|
4888
5889
|
}): {
|
|
4889
5890
|
email: string | null;
|
|
4890
5891
|
phone: string | null;
|
|
4891
|
-
status: "active" | "inactive" | "suspended";
|
|
4892
5892
|
id: number;
|
|
4893
|
-
|
|
4894
|
-
|
|
5893
|
+
publicId: string;
|
|
5894
|
+
username: string | null;
|
|
4895
5895
|
passwordHash: string | null;
|
|
4896
5896
|
passwordChangeRequired: boolean;
|
|
4897
5897
|
roleId: number;
|
|
5898
|
+
createdAt: Date;
|
|
5899
|
+
updatedAt: Date;
|
|
5900
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
4898
5901
|
emailVerifiedAt: Date | null;
|
|
4899
5902
|
phoneVerifiedAt: Date | null;
|
|
4900
5903
|
lastLoginAt: Date | null;
|
|
5904
|
+
deletedAt: Date | null;
|
|
5905
|
+
deletedBy: string | null;
|
|
4901
5906
|
};
|
|
4902
5907
|
/**
|
|
4903
5908
|
* Get authenticated user ID from route context
|
|
@@ -4913,6 +5918,38 @@ declare function getUser(c: Context | {
|
|
|
4913
5918
|
declare function getUserId(c: Context | {
|
|
4914
5919
|
raw: Context;
|
|
4915
5920
|
}): string;
|
|
5921
|
+
/**
|
|
5922
|
+
* Get authenticated user's role from route context
|
|
5923
|
+
*
|
|
5924
|
+
* @returns Role name or null if user has no role
|
|
5925
|
+
*
|
|
5926
|
+
* @example
|
|
5927
|
+
* ```typescript
|
|
5928
|
+
* app.bind(adminContract, [authenticate], async (c) => {
|
|
5929
|
+
* const role = getRole(c);
|
|
5930
|
+
* // 'admin' | 'superadmin' | null
|
|
5931
|
+
* });
|
|
5932
|
+
* ```
|
|
5933
|
+
*/
|
|
5934
|
+
declare function getRole(c: Context | {
|
|
5935
|
+
raw: Context;
|
|
5936
|
+
}): string | null;
|
|
5937
|
+
/**
|
|
5938
|
+
* Get authenticated user's locale from route context
|
|
5939
|
+
*
|
|
5940
|
+
* @returns Locale string (e.g., 'en', 'ko')
|
|
5941
|
+
*
|
|
5942
|
+
* @example
|
|
5943
|
+
* ```typescript
|
|
5944
|
+
* app.bind(contract, [authenticate], async (c) => {
|
|
5945
|
+
* const locale = getLocale(c);
|
|
5946
|
+
* // 'en' | 'ko' | ...
|
|
5947
|
+
* });
|
|
5948
|
+
* ```
|
|
5949
|
+
*/
|
|
5950
|
+
declare function getLocale(c: Context | {
|
|
5951
|
+
raw: Context;
|
|
5952
|
+
}): string;
|
|
4916
5953
|
/**
|
|
4917
5954
|
* Get current key ID from route context
|
|
4918
5955
|
*
|
|
@@ -4939,7 +5976,7 @@ declare function getKeyId(c: Context | {
|
|
|
4939
5976
|
* - RS256 (RSA 2048): ~294 bytes (Base64: ~392 chars)
|
|
4940
5977
|
*/
|
|
4941
5978
|
|
|
4942
|
-
type Unit =
|
|
5979
|
+
type Unit = 'Years' | 'Year' | 'Yrs' | 'Yr' | 'Y' | 'Weeks' | 'Week' | 'W' | 'Days' | 'Day' | 'D' | 'Hours' | 'Hour' | 'Hrs' | 'Hr' | 'H' | 'Minutes' | 'Minute' | 'Mins' | 'Min' | 'M' | 'Seconds' | 'Second' | 'Secs' | 'Sec' | 's' | 'Milliseconds' | 'Millisecond' | 'Msecs' | 'Msec' | 'Ms';
|
|
4943
5980
|
type UnitAnyCase = Unit | Uppercase<Unit> | Lowercase<Unit>;
|
|
4944
5981
|
type StringValue = `${number}` | `${number}${UnitAnyCase}` | `${number} ${UnitAnyCase}`;
|
|
4945
5982
|
interface KeyPair {
|
|
@@ -4985,70 +6022,40 @@ declare function shouldRotateKey(createdAt: Date, rotationDays?: number): {
|
|
|
4985
6022
|
daysRemaining: number;
|
|
4986
6023
|
};
|
|
4987
6024
|
|
|
4988
|
-
/**
|
|
4989
|
-
* @spfn/auth - Client Session Management
|
|
4990
|
-
*
|
|
4991
|
-
* Uses Jose JWE (JSON Web Encryption) to securely store session data in cookies
|
|
4992
|
-
* More efficient than Iron Session with better Edge Runtime support
|
|
4993
|
-
*/
|
|
4994
|
-
|
|
4995
|
-
interface SessionData {
|
|
4996
|
-
userId: string;
|
|
4997
|
-
privateKey: string;
|
|
4998
|
-
keyId: string;
|
|
4999
|
-
algorithm: KeyAlgorithmType;
|
|
5000
|
-
}
|
|
5001
|
-
/**
|
|
5002
|
-
* Seal session data into encrypted JWT (JWE)
|
|
5003
|
-
*
|
|
5004
|
-
* @param data - Session data to encrypt
|
|
5005
|
-
* @param ttl - Time to live in seconds (default: 7 days)
|
|
5006
|
-
* @returns Encrypted JWT string
|
|
5007
|
-
*/
|
|
5008
|
-
declare function sealSession(data: SessionData, ttl?: number): Promise<string>;
|
|
5009
|
-
/**
|
|
5010
|
-
* Unseal encrypted JWT (JWE) to session data
|
|
5011
|
-
*
|
|
5012
|
-
* @param jwt - Encrypted JWT string
|
|
5013
|
-
* @returns Session data
|
|
5014
|
-
* @throws Error if session is invalid or expired
|
|
5015
|
-
*/
|
|
5016
|
-
declare function unsealSession(jwt: string): Promise<SessionData>;
|
|
5017
|
-
/**
|
|
5018
|
-
* Get session metadata without decrypting
|
|
5019
|
-
*
|
|
5020
|
-
* @param jwt - Encrypted JWT string
|
|
5021
|
-
* @returns Session metadata or null if invalid
|
|
5022
|
-
*/
|
|
5023
|
-
declare function getSessionInfo(jwt: string): Promise<{
|
|
5024
|
-
issuedAt: Date;
|
|
5025
|
-
expiresAt: Date;
|
|
5026
|
-
issuer: string;
|
|
5027
|
-
audience: string;
|
|
5028
|
-
} | null>;
|
|
5029
|
-
/**
|
|
5030
|
-
* Check if session is about to expire (within threshold)
|
|
5031
|
-
*
|
|
5032
|
-
* @param jwt - Encrypted JWT string
|
|
5033
|
-
* @param thresholdHours - Hours before expiry to trigger refresh (default: 24)
|
|
5034
|
-
* @returns True if session should be refreshed
|
|
5035
|
-
*/
|
|
5036
|
-
declare function shouldRefreshSession(jwt: string, thresholdHours?: number): Promise<boolean>;
|
|
5037
|
-
|
|
5038
6025
|
/**
|
|
5039
6026
|
* @spfn/auth - Global Configuration
|
|
5040
6027
|
*
|
|
5041
6028
|
* Manages global auth configuration including session TTL
|
|
5042
6029
|
*/
|
|
6030
|
+
|
|
5043
6031
|
/**
|
|
5044
6032
|
* Cookie names used by SPFN Auth
|
|
6033
|
+
*
|
|
6034
|
+
* Names include a port-based suffix so that multiple dev instances
|
|
6035
|
+
* on different ports don't overwrite each other's cookies.
|
|
5045
6036
|
*/
|
|
5046
6037
|
declare const COOKIE_NAMES: {
|
|
5047
6038
|
/** Encrypted session data (userId, privateKey, keyId, algorithm) */
|
|
5048
|
-
readonly SESSION:
|
|
6039
|
+
readonly SESSION: string;
|
|
5049
6040
|
/** Current key ID (for key rotation) */
|
|
5050
|
-
readonly SESSION_KEY_ID:
|
|
6041
|
+
readonly SESSION_KEY_ID: string;
|
|
6042
|
+
/** Pending OAuth session (privateKey, keyId, algorithm) - temporary during OAuth flow */
|
|
6043
|
+
readonly OAUTH_PENDING: string;
|
|
6044
|
+
/** OAuth CSRF nonce — double-submit against the (encrypted) state.nonce at callback */
|
|
6045
|
+
readonly OAUTH_CSRF: string;
|
|
5051
6046
|
};
|
|
6047
|
+
/**
|
|
6048
|
+
* OAuth CSRF 쿠키를 PORT 접미사와 무관하게 전부 수집한다.
|
|
6049
|
+
*
|
|
6050
|
+
* 쿠키를 심는 쪽은 Next.js 프로세스, 읽는 쪽은 API 프로세스라 분리 배포에서는
|
|
6051
|
+
* 두 프로세스의 PORT가 달라 COOKIE_NAMES.OAUTH_CSRF 정확 일치 조회가 빗나간다.
|
|
6052
|
+
* nonce 자체가 랜덤값이고 암호화된 state의 nonce와 대조되므로, 접미사가 다른
|
|
6053
|
+
* spfn_oauth_csrf* 후보를 모두 대조 대상으로 넘겨도 안전하다.
|
|
6054
|
+
*/
|
|
6055
|
+
declare function matchOAuthCsrfCookies(cookies: Record<string, string>): {
|
|
6056
|
+
name: string;
|
|
6057
|
+
value: string;
|
|
6058
|
+
}[];
|
|
5052
6059
|
/**
|
|
5053
6060
|
* Parse duration string to seconds
|
|
5054
6061
|
*
|
|
@@ -5061,6 +6068,36 @@ declare const COOKIE_NAMES: {
|
|
|
5061
6068
|
* parseDuration('3600') // 3600
|
|
5062
6069
|
*/
|
|
5063
6070
|
declare function parseDuration(duration: string | number): number;
|
|
6071
|
+
/**
|
|
6072
|
+
* Registration channel passed to the beforeRegister hook
|
|
6073
|
+
*
|
|
6074
|
+
* - credentials: email/phone + password registration
|
|
6075
|
+
* - oauth: new-user signup through a social provider (web or native flow)
|
|
6076
|
+
* - invitation: invitation acceptance
|
|
6077
|
+
*/
|
|
6078
|
+
type RegisterChannel = 'credentials' | 'oauth' | 'invitation';
|
|
6079
|
+
/**
|
|
6080
|
+
* Context passed to the beforeRegister hook
|
|
6081
|
+
*
|
|
6082
|
+
* Credentials (password, keys) are intentionally excluded — the hook is a
|
|
6083
|
+
* policy gate, not a credential handler.
|
|
6084
|
+
*/
|
|
6085
|
+
interface BeforeRegisterContext {
|
|
6086
|
+
channel: RegisterChannel;
|
|
6087
|
+
/** Social provider — only set when channel is 'oauth' */
|
|
6088
|
+
provider?: SocialProvider;
|
|
6089
|
+
email?: string;
|
|
6090
|
+
/**
|
|
6091
|
+
* Whether the email is verified — only set when channel is 'oauth'.
|
|
6092
|
+
* OAuth providers may report an unverified (spoofable) email; the created
|
|
6093
|
+
* account stores it as null in that case, so email-based policies must
|
|
6094
|
+
* check this flag. credentials/invitation emails are already verified.
|
|
6095
|
+
*/
|
|
6096
|
+
emailVerified?: boolean;
|
|
6097
|
+
phone?: string;
|
|
6098
|
+
/** App-supplied registration metadata (register params / OAuth start params / invitation) */
|
|
6099
|
+
metadata?: Record<string, unknown>;
|
|
6100
|
+
}
|
|
5064
6101
|
/**
|
|
5065
6102
|
* Auth configuration
|
|
5066
6103
|
*/
|
|
@@ -5075,6 +6112,35 @@ interface AuthConfig {
|
|
|
5075
6112
|
* @default 7d (7 days)
|
|
5076
6113
|
*/
|
|
5077
6114
|
sessionTtl?: string | number;
|
|
6115
|
+
/**
|
|
6116
|
+
* App-injected validator that runs before a new user row is created,
|
|
6117
|
+
* on every registration channel (credentials, oauth, invitation).
|
|
6118
|
+
*
|
|
6119
|
+
* Throw to reject the registration — RegistrationRejectedError (403) is
|
|
6120
|
+
* the recommended error; any HttpError subclass keeps its own status.
|
|
6121
|
+
* Runs after built-in checks (verification token, duplicate account),
|
|
6122
|
+
* so existing error precedence is unchanged. Not called for admin
|
|
6123
|
+
* seeding (initializeAuth) or when linking a social account to an
|
|
6124
|
+
* existing user.
|
|
6125
|
+
*
|
|
6126
|
+
* Runs inside the registration DB transaction on every channel — keep it
|
|
6127
|
+
* fast. A slow call (e.g. an external policy API) holds a pooled DB
|
|
6128
|
+
* connection open for its full duration on every signup.
|
|
6129
|
+
*
|
|
6130
|
+
* @example
|
|
6131
|
+
* ```typescript
|
|
6132
|
+
* configureAuth({
|
|
6133
|
+
* beforeRegister: async ({ channel, metadata }) =>
|
|
6134
|
+
* {
|
|
6135
|
+
* if (channel === 'credentials' && !isOldEnough(metadata?.birthDate))
|
|
6136
|
+
* {
|
|
6137
|
+
* throw new RegistrationRejectedError({ message: 'Age requirement not met' });
|
|
6138
|
+
* }
|
|
6139
|
+
* },
|
|
6140
|
+
* });
|
|
6141
|
+
* ```
|
|
6142
|
+
*/
|
|
6143
|
+
beforeRegister?: (context: BeforeRegisterContext) => void | Promise<void>;
|
|
5078
6144
|
}
|
|
5079
6145
|
/**
|
|
5080
6146
|
* Configure global auth settings
|
|
@@ -5093,6 +6159,14 @@ declare function configureAuth(config: AuthConfig): void;
|
|
|
5093
6159
|
* Get current auth configuration
|
|
5094
6160
|
*/
|
|
5095
6161
|
declare function getAuthConfig(): AuthConfig;
|
|
6162
|
+
/**
|
|
6163
|
+
* Run the app-injected beforeRegister hook if configured — throws to reject.
|
|
6164
|
+
*
|
|
6165
|
+
* Single entry point for every registration channel so a new channel cannot
|
|
6166
|
+
* forget the configured-check. Callers invoke this right before creating the
|
|
6167
|
+
* user row.
|
|
6168
|
+
*/
|
|
6169
|
+
declare function runBeforeRegister(context: BeforeRegisterContext): Promise<void>;
|
|
5096
6170
|
/**
|
|
5097
6171
|
* Get session TTL in seconds
|
|
5098
6172
|
*
|
|
@@ -5104,6 +6178,107 @@ declare function getAuthConfig(): AuthConfig;
|
|
|
5104
6178
|
*/
|
|
5105
6179
|
declare function getSessionTtl(override?: string | number): number;
|
|
5106
6180
|
|
|
6181
|
+
/**
|
|
6182
|
+
* One-Time Token Manager
|
|
6183
|
+
*
|
|
6184
|
+
* Singleton wrapper around SSETokenManager for one-time token authentication.
|
|
6185
|
+
* Used for direct API access (file uploads, SSE streaming, etc.) bypassing RPC proxy.
|
|
6186
|
+
*/
|
|
6187
|
+
|
|
6188
|
+
/**
|
|
6189
|
+
* Initialize the one-time token manager
|
|
6190
|
+
*
|
|
6191
|
+
* Called during auth lifecycle initialization.
|
|
6192
|
+
* Creates a singleton SSETokenManager instance.
|
|
6193
|
+
*
|
|
6194
|
+
* @param config - Optional configuration
|
|
6195
|
+
* @param config.ttl - Token time-to-live in milliseconds (default: 30000)
|
|
6196
|
+
* @param config.store - Custom token store (e.g., CacheTokenStore for Redis)
|
|
6197
|
+
*/
|
|
6198
|
+
declare function initOneTimeTokenManager(config?: {
|
|
6199
|
+
ttl?: number;
|
|
6200
|
+
store?: SSETokenStore;
|
|
6201
|
+
}): void;
|
|
6202
|
+
/**
|
|
6203
|
+
* Get the one-time token manager instance
|
|
6204
|
+
*
|
|
6205
|
+
* @throws Error if initOneTimeTokenManager() has not been called
|
|
6206
|
+
*
|
|
6207
|
+
* @example
|
|
6208
|
+
* ```typescript
|
|
6209
|
+
* import { getOneTimeTokenManager } from '@spfn/auth/server';
|
|
6210
|
+
*
|
|
6211
|
+
* // Use as SSE tokenManager
|
|
6212
|
+
* .eventsConfig({
|
|
6213
|
+
* auth: {
|
|
6214
|
+
* enabled: true,
|
|
6215
|
+
* tokenManager: getOneTimeTokenManager(),
|
|
6216
|
+
* },
|
|
6217
|
+
* })
|
|
6218
|
+
* ```
|
|
6219
|
+
*/
|
|
6220
|
+
declare function getOneTimeTokenManager(): SSETokenManager;
|
|
6221
|
+
|
|
6222
|
+
/**
|
|
6223
|
+
* Account Deletion Configuration
|
|
6224
|
+
*
|
|
6225
|
+
* Singleton config for the account deletion/recovery lifecycle, mirroring the
|
|
6226
|
+
* one-time-token manager pattern (`lib/one-time-token.ts`): a mutable module-level
|
|
6227
|
+
* value, set once during `createAuthLifecycle()` and read at request/handler time.
|
|
6228
|
+
*
|
|
6229
|
+
* `purgeCron` is the one field this module does NOT make effective on its own —
|
|
6230
|
+
* `job('auth.deletion.purge').cron(expression)` bakes the cron string in at
|
|
6231
|
+
* *module-import* time, which always runs before `createAuthLifecycle()` executes
|
|
6232
|
+
* (ESM evaluates imports before the importing module's own statements). So a
|
|
6233
|
+
* statically-exported job router can never see a `purgeCron` set here. Apps that
|
|
6234
|
+
* need a non-default cron must build the router themselves, after configuring
|
|
6235
|
+
* deletion, with `createAuthDeletionJobRouter({ purgeCron })` — see
|
|
6236
|
+
* `server/jobs/deletion-purge.ts` and the README.
|
|
6237
|
+
*/
|
|
6238
|
+
|
|
6239
|
+
interface AccountDeletionPurgeUser {
|
|
6240
|
+
id: number;
|
|
6241
|
+
publicId: string;
|
|
6242
|
+
email: string | null;
|
|
6243
|
+
phone: string | null;
|
|
6244
|
+
}
|
|
6245
|
+
interface AuthDeletionConfig {
|
|
6246
|
+
/** Days between a deletion request and the purge becoming eligible. 0 = immediate. */
|
|
6247
|
+
gracePeriodDays: number;
|
|
6248
|
+
/** How the purge job destroys the account once the grace period elapses. */
|
|
6249
|
+
purgeStrategy: PurgeStrategy;
|
|
6250
|
+
/** Whether a self-service caller may request `immediate: true` (skip the grace period). */
|
|
6251
|
+
allowSelfImmediate: boolean;
|
|
6252
|
+
/**
|
|
6253
|
+
* Cron schedule for the purge sweep. NOT read automatically by the static
|
|
6254
|
+
* `authJobRouter` export — see the module doc comment above.
|
|
6255
|
+
*/
|
|
6256
|
+
purgeCron: string;
|
|
6257
|
+
/** Whether to email users (when they have one) at request/recovery/purge time. */
|
|
6258
|
+
sendNotifications: boolean;
|
|
6259
|
+
/**
|
|
6260
|
+
* Synchronous hook invoked immediately before a user is purged. Throw to skip
|
|
6261
|
+
* that user for this sweep (they stay `pending`, retried on the next tick).
|
|
6262
|
+
*/
|
|
6263
|
+
onBeforePurge?: (user: AccountDeletionPurgeUser) => Promise<void>;
|
|
6264
|
+
}
|
|
6265
|
+
declare const DEFAULT_DELETION_GRACE_PERIOD_DAYS = 30;
|
|
6266
|
+
declare const DEFAULT_DELETION_PURGE_STRATEGY: PurgeStrategy;
|
|
6267
|
+
declare const DEFAULT_DELETION_ALLOW_SELF_IMMEDIATE = false;
|
|
6268
|
+
declare const DEFAULT_DELETION_PURGE_CRON = "0 4 * * *";
|
|
6269
|
+
declare const DEFAULT_DELETION_SEND_NOTIFICATIONS = true;
|
|
6270
|
+
/**
|
|
6271
|
+
* Set the resolved deletion config. Called synchronously from `createAuthLifecycle()`
|
|
6272
|
+
* (not from `afterInfrastructure`) so it takes effect before any route/job handler
|
|
6273
|
+
* that reads `getDeletionConfig()` can run.
|
|
6274
|
+
*/
|
|
6275
|
+
declare function configureDeletion(options?: Partial<AuthDeletionConfig>): void;
|
|
6276
|
+
/**
|
|
6277
|
+
* Read the current deletion config. Safe to call any time — defaults apply even
|
|
6278
|
+
* if `createAuthLifecycle()` was never given a `deletion` block.
|
|
6279
|
+
*/
|
|
6280
|
+
declare function getDeletionConfig(): AuthDeletionConfig;
|
|
6281
|
+
|
|
5107
6282
|
/**
|
|
5108
6283
|
* @spfn/auth - Centralized Logger
|
|
5109
6284
|
*
|
|
@@ -5116,7 +6291,9 @@ declare const authLogger: {
|
|
|
5116
6291
|
general: _spfn_core_logger.Logger;
|
|
5117
6292
|
login: _spfn_core_logger.Logger;
|
|
5118
6293
|
keyRotation: _spfn_core_logger.Logger;
|
|
6294
|
+
oauth: _spfn_core_logger.Logger;
|
|
5119
6295
|
};
|
|
6296
|
+
session: _spfn_core_logger.Logger;
|
|
5120
6297
|
service: _spfn_core_logger.Logger;
|
|
5121
6298
|
setup: _spfn_core_logger.Logger;
|
|
5122
6299
|
email: _spfn_core_logger.Logger;
|
|
@@ -5197,6 +6374,337 @@ interface AuthLifecycleConfig {
|
|
|
5197
6374
|
* .build();
|
|
5198
6375
|
* ```
|
|
5199
6376
|
*/
|
|
5200
|
-
|
|
6377
|
+
/**
|
|
6378
|
+
* Options for createAuthLifecycle
|
|
6379
|
+
*/
|
|
6380
|
+
interface AuthLifecycleOptions extends AuthInitOptions {
|
|
6381
|
+
/**
|
|
6382
|
+
* One-time token configuration
|
|
6383
|
+
*
|
|
6384
|
+
* Enables one-time token issuance for direct API access
|
|
6385
|
+
* (file uploads, SSE streaming, etc.)
|
|
6386
|
+
*
|
|
6387
|
+
* @example
|
|
6388
|
+
* ```typescript
|
|
6389
|
+
* createAuthLifecycle({
|
|
6390
|
+
* oneTimeToken: { ttl: 60000 }, // 60 seconds
|
|
6391
|
+
* })
|
|
6392
|
+
* ```
|
|
6393
|
+
*/
|
|
6394
|
+
oneTimeToken?: {
|
|
6395
|
+
/**
|
|
6396
|
+
* Token time-to-live in milliseconds
|
|
6397
|
+
* @default 30000
|
|
6398
|
+
*/
|
|
6399
|
+
ttl?: number;
|
|
6400
|
+
/**
|
|
6401
|
+
* Custom token store (e.g., CacheTokenStore for Redis/Valkey)
|
|
6402
|
+
*
|
|
6403
|
+
* When provided, tokens are stored in the external store instead of in-memory Map.
|
|
6404
|
+
* Required for multi-instance deployments where token issuance and verification
|
|
6405
|
+
* may happen on different server instances.
|
|
6406
|
+
*
|
|
6407
|
+
* @example
|
|
6408
|
+
* ```typescript
|
|
6409
|
+
* import { CacheTokenStore } from '@spfn/core/event/sse';
|
|
6410
|
+
* import { getCache } from '@spfn/core/cache';
|
|
6411
|
+
*
|
|
6412
|
+
* createAuthLifecycle({
|
|
6413
|
+
* oneTimeToken: {
|
|
6414
|
+
* store: new CacheTokenStore(getCache()),
|
|
6415
|
+
* },
|
|
6416
|
+
* })
|
|
6417
|
+
* ```
|
|
6418
|
+
*/
|
|
6419
|
+
store?: SSETokenStore;
|
|
6420
|
+
};
|
|
6421
|
+
/**
|
|
6422
|
+
* Account deletion/recovery lifecycle configuration
|
|
6423
|
+
*
|
|
6424
|
+
* Controls the grace-period request → recover → purge flow exposed by
|
|
6425
|
+
* `POST /_auth/deletion/request` and `POST /_auth/deletion/cancel`.
|
|
6426
|
+
* Registering the purge job itself is a separate step — see `authJobRouter`
|
|
6427
|
+
* (`@spfn/auth/server`) and the README, since job registration happens after
|
|
6428
|
+
* this lifecycle hook runs and can't be triggered from here.
|
|
6429
|
+
*
|
|
6430
|
+
* @example
|
|
6431
|
+
* ```typescript
|
|
6432
|
+
* createAuthLifecycle({
|
|
6433
|
+
* deletion: {
|
|
6434
|
+
* gracePeriodDays: 14,
|
|
6435
|
+
* purgeStrategy: 'anonymize',
|
|
6436
|
+
* allowSelfImmediate: false,
|
|
6437
|
+
* sendNotifications: true,
|
|
6438
|
+
* onBeforePurge: async (user) => {
|
|
6439
|
+
* await appDataCleanup(user.id); // throw to skip this user this sweep
|
|
6440
|
+
* },
|
|
6441
|
+
* },
|
|
6442
|
+
* })
|
|
6443
|
+
* ```
|
|
6444
|
+
*/
|
|
6445
|
+
deletion?: {
|
|
6446
|
+
/**
|
|
6447
|
+
* Days between a deletion request and the purge becoming eligible.
|
|
6448
|
+
* 0 = immediate (still goes through the same request/purge pipeline).
|
|
6449
|
+
* @default 30
|
|
6450
|
+
*/
|
|
6451
|
+
gracePeriodDays?: number;
|
|
6452
|
+
/**
|
|
6453
|
+
* How the purge job destroys the account once the grace period elapses.
|
|
6454
|
+
* - 'anonymize': scrub PII, keep the row (status -> 'deleted') — recommended default
|
|
6455
|
+
* - 'hard-delete': physically remove the `users` row (cascades to child rows)
|
|
6456
|
+
* @default 'anonymize'
|
|
6457
|
+
*/
|
|
6458
|
+
purgeStrategy?: PurgeStrategy;
|
|
6459
|
+
/**
|
|
6460
|
+
* Whether a self-service caller may pass `immediate: true` on
|
|
6461
|
+
* `POST /_auth/deletion/request` to skip the grace period entirely.
|
|
6462
|
+
* @default false
|
|
6463
|
+
*/
|
|
6464
|
+
allowSelfImmediate?: boolean;
|
|
6465
|
+
/**
|
|
6466
|
+
* Cron schedule for the purge sweep. NOTE: this does not reach the static
|
|
6467
|
+
* `authJobRouter` export automatically — job schedules are fixed at
|
|
6468
|
+
* module-import time, before this lifecycle hook runs. For a non-default
|
|
6469
|
+
* cron, build the router yourself after this call with
|
|
6470
|
+
* `createAuthDeletionJobRouter({ purgeCron })` (see `@spfn/auth/server`).
|
|
6471
|
+
* @default '0 4 * * *'
|
|
6472
|
+
*/
|
|
6473
|
+
purgeCron?: string;
|
|
6474
|
+
/**
|
|
6475
|
+
* Whether to email users (when they have one on file) at request,
|
|
6476
|
+
* recovery, and final-purge time.
|
|
6477
|
+
* @default true
|
|
6478
|
+
*/
|
|
6479
|
+
sendNotifications?: boolean;
|
|
6480
|
+
/**
|
|
6481
|
+
* Invoked immediately before a user is purged (both the immediate inline
|
|
6482
|
+
* path and the cron sweep). Throw to skip that user for this run — they
|
|
6483
|
+
* stay `pending` and are retried on the next sweep.
|
|
6484
|
+
*/
|
|
6485
|
+
onBeforePurge?: (user: AccountDeletionPurgeUser) => Promise<void>;
|
|
6486
|
+
};
|
|
6487
|
+
}
|
|
6488
|
+
declare function createAuthLifecycle(options?: AuthLifecycleOptions): AuthLifecycleConfig;
|
|
6489
|
+
|
|
6490
|
+
/**
|
|
6491
|
+
* @spfn/auth - Account Deletion Purge Job
|
|
6492
|
+
*
|
|
6493
|
+
* The package's first background job (`@spfn/core/job`). Sweeps
|
|
6494
|
+
* `account_deletion_requests` for rows whose grace period elapsed and purges
|
|
6495
|
+
* each one (see `sweepDuePurges` in `account-deletion.service.ts`).
|
|
6496
|
+
*
|
|
6497
|
+
* Registration is NOT automatic. `createAuthLifecycle()`'s `afterInfrastructure`
|
|
6498
|
+
* hook runs *before* `initBoss`/`registerJobs` in `@spfn/core`'s server startup
|
|
6499
|
+
* (`initializeInfrastructure()`), so the lifecycle has no opportunity to register
|
|
6500
|
+
* jobs itself. Apps must register explicitly:
|
|
6501
|
+
*
|
|
6502
|
+
* ```typescript
|
|
6503
|
+
* import { authJobRouter } from '@spfn/auth/server';
|
|
6504
|
+
*
|
|
6505
|
+
* export default defineServerConfig()
|
|
6506
|
+
* .lifecycle(createAuthLifecycle())
|
|
6507
|
+
* .jobs(authJobRouter)
|
|
6508
|
+
* .routes(appRouter)
|
|
6509
|
+
* .build();
|
|
6510
|
+
* ```
|
|
6511
|
+
*
|
|
6512
|
+
* Cron schedule caveat: `job(...).cron(expression)` bakes the cron string in at
|
|
6513
|
+
* *module-import* time, which always happens before `createAuthLifecycle()` runs
|
|
6514
|
+
* in the app's own module (ESM evaluates imports before the importing module's
|
|
6515
|
+
* statements). So the static `authJobRouter` export below always uses the default
|
|
6516
|
+
* cron — `deletion.purgeCron` passed to `createAuthLifecycle()` cannot reach it.
|
|
6517
|
+
* To use a custom cron, build the router yourself, after calling
|
|
6518
|
+
* `createAuthLifecycle()`, with `createAuthDeletionJobRouter({ purgeCron })` and
|
|
6519
|
+
* register that instead of the static export.
|
|
6520
|
+
*/
|
|
6521
|
+
/**
|
|
6522
|
+
* Build the `auth.deletion.purge` job with a given cron schedule.
|
|
6523
|
+
*
|
|
6524
|
+
* @param cronExpression - Defaults to `deletion-config`'s default (daily at 04:00).
|
|
6525
|
+
*/
|
|
6526
|
+
declare function createAuthDeletionPurgeJob(cronExpression?: string): _spfn_core_job.JobDef<void, void>;
|
|
6527
|
+
/**
|
|
6528
|
+
* Build a job router containing only the deletion purge job, with a given cron.
|
|
6529
|
+
* Use this (after calling `createAuthLifecycle({ deletion: { purgeCron } })`) when
|
|
6530
|
+
* you need a non-default schedule — see the module doc comment above for why the
|
|
6531
|
+
* static `authJobRouter` export can't pick up `deletion.purgeCron` automatically.
|
|
6532
|
+
*/
|
|
6533
|
+
declare function createAuthDeletionJobRouter(options?: {
|
|
6534
|
+
purgeCron?: string;
|
|
6535
|
+
}): _spfn_core_job.JobRouter<{
|
|
6536
|
+
deletionPurge: _spfn_core_job.JobDef<void, void>;
|
|
6537
|
+
}>;
|
|
6538
|
+
/**
|
|
6539
|
+
* Default job router — the default cron (`0 4 * * *`). Register with
|
|
6540
|
+
* `.jobs(authJobRouter)` in `server.config.ts`.
|
|
6541
|
+
*/
|
|
6542
|
+
declare const authJobRouter: _spfn_core_job.JobRouter<{
|
|
6543
|
+
deletionPurge: _spfn_core_job.JobDef<void, void>;
|
|
6544
|
+
}>;
|
|
6545
|
+
|
|
6546
|
+
/**
|
|
6547
|
+
* @spfn/auth - Auth Events
|
|
6548
|
+
*
|
|
6549
|
+
* 인증 관련 이벤트 정의
|
|
6550
|
+
* - auth.login: 로그인 성공 시 (기존 사용자만)
|
|
6551
|
+
* - auth.register: 회원가입 성공 시 (OAuth 신규 가입 포함)
|
|
6552
|
+
*/
|
|
6553
|
+
/**
|
|
6554
|
+
* Auth provider type
|
|
6555
|
+
*
|
|
6556
|
+
* 직접 인증(email/phone) + 등록 가능한 모든 소셜 provider(SOCIAL_PROVIDERS).
|
|
6557
|
+
*/
|
|
6558
|
+
declare const AuthProviderSchema: _sinclair_typebox.TUnion<[_sinclair_typebox.TLiteral<"email">, _sinclair_typebox.TLiteral<"phone">, ..._sinclair_typebox.TLiteral<"google" | "apple" | "github" | "kakao" | "naver" | "superself">[]]>;
|
|
6559
|
+
/**
|
|
6560
|
+
* auth.login - 로그인 성공 이벤트
|
|
6561
|
+
*
|
|
6562
|
+
* 발행 시점:
|
|
6563
|
+
* - 이메일/전화 로그인 성공 시
|
|
6564
|
+
* - OAuth 기존 사용자 로그인 시
|
|
6565
|
+
*
|
|
6566
|
+
* @example
|
|
6567
|
+
* ```typescript
|
|
6568
|
+
* authLoginEvent.subscribe(async (payload) => {
|
|
6569
|
+
* await analytics.trackLogin(payload.userId, payload.provider);
|
|
6570
|
+
* });
|
|
6571
|
+
* ```
|
|
6572
|
+
*/
|
|
6573
|
+
declare const authLoginEvent: _spfn_core_event.EventDef<{
|
|
6574
|
+
email?: string | undefined;
|
|
6575
|
+
phone?: string | undefined;
|
|
6576
|
+
userId: string;
|
|
6577
|
+
provider: "email" | "phone" | "google" | "apple" | "github" | "kakao" | "naver" | "superself";
|
|
6578
|
+
}>;
|
|
6579
|
+
/**
|
|
6580
|
+
* auth.register - 회원가입 성공 이벤트
|
|
6581
|
+
*
|
|
6582
|
+
* 발행 시점:
|
|
6583
|
+
* - 이메일/전화 회원가입 성공 시
|
|
6584
|
+
* - OAuth 신규 사용자 가입 시
|
|
6585
|
+
*
|
|
6586
|
+
* @example
|
|
6587
|
+
* ```typescript
|
|
6588
|
+
* authRegisterEvent.subscribe(async (payload) => {
|
|
6589
|
+
* await emailService.sendWelcome(payload.email);
|
|
6590
|
+
* });
|
|
6591
|
+
* ```
|
|
6592
|
+
*/
|
|
6593
|
+
declare const authRegisterEvent: _spfn_core_event.EventDef<{
|
|
6594
|
+
email?: string | undefined;
|
|
6595
|
+
phone?: string | undefined;
|
|
6596
|
+
metadata?: {
|
|
6597
|
+
[x: string]: unknown;
|
|
6598
|
+
} | undefined;
|
|
6599
|
+
userId: string;
|
|
6600
|
+
provider: "email" | "phone" | "google" | "apple" | "github" | "kakao" | "naver" | "superself";
|
|
6601
|
+
}>;
|
|
6602
|
+
/**
|
|
6603
|
+
* auth.invitation.created - 초대 생성 이벤트
|
|
6604
|
+
*
|
|
6605
|
+
* 발행 시점:
|
|
6606
|
+
* - createInvitation() 성공 시
|
|
6607
|
+
* - resendInvitation() 성공 시
|
|
6608
|
+
*
|
|
6609
|
+
* @example
|
|
6610
|
+
* ```typescript
|
|
6611
|
+
* invitationCreatedEvent.subscribe(async (payload) => {
|
|
6612
|
+
* const inviteUrl = `${APP_URL}/invite/${payload.token}`;
|
|
6613
|
+
* await notificationService.send({
|
|
6614
|
+
* channel: 'email',
|
|
6615
|
+
* to: payload.email,
|
|
6616
|
+
* subject: 'You are invited!',
|
|
6617
|
+
* html: renderInviteEmail({ inviteUrl, ...payload.metadata }),
|
|
6618
|
+
* });
|
|
6619
|
+
* });
|
|
6620
|
+
* ```
|
|
6621
|
+
*/
|
|
6622
|
+
declare const invitationCreatedEvent: _spfn_core_event.EventDef<{
|
|
6623
|
+
metadata?: {
|
|
6624
|
+
[x: string]: unknown;
|
|
6625
|
+
} | undefined;
|
|
6626
|
+
email: string;
|
|
6627
|
+
roleId: number;
|
|
6628
|
+
token: string;
|
|
6629
|
+
invitedBy: string;
|
|
6630
|
+
expiresAt: string;
|
|
6631
|
+
invitationId: string;
|
|
6632
|
+
isResend: boolean;
|
|
6633
|
+
}>;
|
|
6634
|
+
/**
|
|
6635
|
+
* auth.invitation.accepted - 초대 수락 이벤트
|
|
6636
|
+
*
|
|
6637
|
+
* 발행 시점:
|
|
6638
|
+
* - acceptInvitation() 성공 시
|
|
6639
|
+
*
|
|
6640
|
+
* @example
|
|
6641
|
+
* ```typescript
|
|
6642
|
+
* invitationAcceptedEvent.subscribe(async (payload) => {
|
|
6643
|
+
* await onboardingService.start(payload.userId);
|
|
6644
|
+
* });
|
|
6645
|
+
* ```
|
|
6646
|
+
*/
|
|
6647
|
+
declare const invitationAcceptedEvent: _spfn_core_event.EventDef<{
|
|
6648
|
+
metadata?: {
|
|
6649
|
+
[x: string]: unknown;
|
|
6650
|
+
} | undefined;
|
|
6651
|
+
email: string;
|
|
6652
|
+
userId: string;
|
|
6653
|
+
roleId: number;
|
|
6654
|
+
invitedBy: string;
|
|
6655
|
+
invitationId: string;
|
|
6656
|
+
}>;
|
|
6657
|
+
/**
|
|
6658
|
+
* auth.deletion.requested - 계정 탈퇴 요청 이벤트
|
|
6659
|
+
*
|
|
6660
|
+
* 발행 시점:
|
|
6661
|
+
* - requestAccountDeletionService() 성공 시 (self/admin 공통)
|
|
6662
|
+
*
|
|
6663
|
+
* @example
|
|
6664
|
+
* ```typescript
|
|
6665
|
+
* authDeletionRequestedEvent.subscribe(async (payload) => {
|
|
6666
|
+
* await analytics.trackChurnRisk(payload.userId);
|
|
6667
|
+
* });
|
|
6668
|
+
* ```
|
|
6669
|
+
*/
|
|
6670
|
+
declare const authDeletionRequestedEvent: _spfn_core_event.EventDef<{
|
|
6671
|
+
userId: string;
|
|
6672
|
+
purgeScheduledAt: string;
|
|
6673
|
+
userPublicId: string;
|
|
6674
|
+
requestedBy: "admin" | "self";
|
|
6675
|
+
}>;
|
|
6676
|
+
/**
|
|
6677
|
+
* auth.deletion.cancelled - 계정 탈퇴 복구 이벤트
|
|
6678
|
+
*
|
|
6679
|
+
* 발행 시점:
|
|
6680
|
+
* - cancelAccountDeletionService() 성공 시 (유예 기간 내 복구)
|
|
6681
|
+
*/
|
|
6682
|
+
declare const authDeletionCancelledEvent: _spfn_core_event.EventDef<{
|
|
6683
|
+
userId: string;
|
|
6684
|
+
userPublicId: string;
|
|
6685
|
+
}>;
|
|
6686
|
+
/**
|
|
6687
|
+
* auth.deletion.completed - 계정 파기 완료 이벤트
|
|
6688
|
+
*
|
|
6689
|
+
* 발행 시점:
|
|
6690
|
+
* - purge job(또는 즉시 파기 경로)이 유저를 파기한 직후
|
|
6691
|
+
*
|
|
6692
|
+
* PII를 담지 않는다 — userId(내부 순번)/email/phone 없이 userPublicId만 실어
|
|
6693
|
+
* 파기 완료 이후에도 구독자가 식별 정보를 다시 축적하지 않도록 한다.
|
|
6694
|
+
*/
|
|
6695
|
+
declare const authDeletionCompletedEvent: _spfn_core_event.EventDef<{
|
|
6696
|
+
userPublicId: string;
|
|
6697
|
+
purgeStrategy: "anonymize" | "hard-delete";
|
|
6698
|
+
}>;
|
|
6699
|
+
/**
|
|
6700
|
+
* Auth event payload types
|
|
6701
|
+
*/
|
|
6702
|
+
type AuthLoginPayload = typeof authLoginEvent._payload;
|
|
6703
|
+
type AuthRegisterPayload = typeof authRegisterEvent._payload;
|
|
6704
|
+
type InvitationCreatedPayload = typeof invitationCreatedEvent._payload;
|
|
6705
|
+
type InvitationAcceptedPayload = typeof invitationAcceptedEvent._payload;
|
|
6706
|
+
type AuthDeletionRequestedPayload = typeof authDeletionRequestedEvent._payload;
|
|
6707
|
+
type AuthDeletionCancelledPayload = typeof authDeletionCancelledEvent._payload;
|
|
6708
|
+
type AuthDeletionCompletedPayload = typeof authDeletionCompletedEvent._payload;
|
|
5201
6709
|
|
|
5202
|
-
export { type AuthConfig, AuthContext, COOKIE_NAMES, type
|
|
6710
|
+
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, EnvironmentKeyringTokenCipher, type GoogleTokenResponse, type GoogleUserInfo, type Invitation, type InvitationAcceptedPayload, type InvitationCreatedPayload, InvitationStatus, InvitationsRepository, KeyAlgorithmType, type KeyPair, KeysRepository, type NewAccountDeletionRequest, type NewAuthMetadataEntity, type NewInvitation, type NewPermission, type NewPermissionEntity, type NewRole, type NewRoleEntity, type NewRolePermission, type NewUser, type NewUserPermission, type NewUserProfile, type NewUserPublicKey, type NewUserSocialAccount, type NewVerificationCode, OAuthProvider, type OAuthState, type OAuthTokenCipher, type OAuthTokenContext, type OAuthTokenType, 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, 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 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, generateClientToken, generateKeyPair, generateKeyPairES256, generateKeyPairRS256, generateOAuthNonce, generateToken, getAllRoles, getAuth, getAuthConfig, getAuthSessionService, getDeletionConfig, getDummyPasswordHash, getGoogleAuthUrl, getGoogleOAuthConfig, getGoogleUserInfo, getInvitationByToken, getInvitationWithDetails, getKeyId, getKeySize, getLocale, getOneTimeTokenManager, 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, kakaoProvider, keysRepository, listInvitations, matchOAuthCsrfCookies, naverProvider, oneTimeTokenAuth, parseDuration, permissions, permissionsRepository, purgeUserService, refreshAccessToken, removePermissionFromRole, requestAccountDeletionService, requireAnyPermission, requirePermissions, requireRole, resendInvitation, roleGuard, rolePermissions, rolePermissionsRepository, roles, rolesRepository, runBeforeRegister, setRolePermissions, shouldRotateKey, 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, verifyPassword, verifyToken };
|