@spfn/auth 0.2.0-beta.8 → 0.2.0-beta.80
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 +549 -1819
- package/dist/authenticate-ofdEmk6x.d.ts +1109 -0
- package/dist/config.d.ts +359 -41
- package/dist/config.js +186 -30
- 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 +2015 -513
- package/dist/server.js +4198 -1086
- 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,576 @@ 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
|
|
1665
|
-
*
|
|
1666
|
-
* @example
|
|
1667
|
-
* ```typescript
|
|
1668
|
-
* import { registerSMSProvider } from '@spfn/auth/server/services/sms';
|
|
1669
|
-
*
|
|
1670
|
-
* const twilioProvider = {
|
|
1671
|
-
* name: 'twilio',
|
|
1672
|
-
* sendSMS: async (params) => {
|
|
1673
|
-
* // Twilio implementation
|
|
1674
|
-
* return { success: true, messageId: '...' };
|
|
1675
|
-
* }
|
|
1676
|
-
* };
|
|
1678
|
+
* Apple OAuthProvider 구현 (native id_token 검증 전용)
|
|
1677
1679
|
*
|
|
1678
|
-
*
|
|
1679
|
-
*
|
|
1680
|
-
|
|
1681
|
-
declare function registerSMSProvider(provider: SMSProvider): void;
|
|
1682
|
-
/**
|
|
1683
|
-
* Send SMS using the registered provider
|
|
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 없음).
|
|
1684
1683
|
*
|
|
1685
|
-
*
|
|
1684
|
+
* 따라서 web 메서드(getAuthUrl/exchangeCodeForTokens/getUserInfo)는 지원하지 않는다.
|
|
1686
1685
|
*
|
|
1687
|
-
*
|
|
1688
|
-
* @returns Send result
|
|
1686
|
+
* 이 모듈을 import 하는 것만으로 apple provider가 registry에 자기 등록된다.
|
|
1689
1687
|
*/
|
|
1690
|
-
|
|
1688
|
+
|
|
1689
|
+
declare const appleProvider: OAuthProvider;
|
|
1691
1690
|
|
|
1692
1691
|
/**
|
|
1693
|
-
*
|
|
1694
|
-
*
|
|
1695
|
-
* Defines the 'spfn_auth' PostgreSQL schema for all auth-related tables
|
|
1692
|
+
* Kakao Login OAuthProvider (web authorization-code flow).
|
|
1696
1693
|
*/
|
|
1694
|
+
|
|
1695
|
+
declare const kakaoProvider: OAuthProvider;
|
|
1696
|
+
|
|
1697
1697
|
/**
|
|
1698
|
-
*
|
|
1699
|
-
* Tables: users, roles, permissions, user_invitations, etc.
|
|
1698
|
+
* Naver Login OAuthProvider (web authorization-code flow).
|
|
1700
1699
|
*/
|
|
1701
|
-
|
|
1700
|
+
|
|
1701
|
+
declare const naverProvider: OAuthProvider;
|
|
1702
1702
|
|
|
1703
1703
|
/**
|
|
1704
|
-
* @spfn/auth -
|
|
1705
|
-
*
|
|
1706
|
-
* User profile information (separate from authentication data)
|
|
1704
|
+
* @spfn/auth - Account Deletion Requests Entity
|
|
1707
1705
|
*
|
|
1708
|
-
*
|
|
1706
|
+
* Audit trail for account deletion/recovery. Rows are never deleted — even after
|
|
1707
|
+
* a hard-delete purge — so "who requested/recovered/purged what, when" survives
|
|
1708
|
+
* the user row itself (개인정보보호법 제21조 3항 분리 보존 원칙).
|
|
1709
|
+
*/
|
|
1710
|
+
declare const accountDeletionRequests: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
1711
|
+
name: "account_deletion_requests";
|
|
1712
|
+
schema: string;
|
|
1713
|
+
columns: {
|
|
1714
|
+
createdAt: drizzle_orm_pg_core.PgColumn<{
|
|
1715
|
+
name: "created_at";
|
|
1716
|
+
tableName: "account_deletion_requests";
|
|
1717
|
+
dataType: "date";
|
|
1718
|
+
columnType: "PgTimestamp";
|
|
1719
|
+
data: Date;
|
|
1720
|
+
driverParam: string;
|
|
1721
|
+
notNull: true;
|
|
1722
|
+
hasDefault: true;
|
|
1723
|
+
isPrimaryKey: false;
|
|
1724
|
+
isAutoincrement: false;
|
|
1725
|
+
hasRuntimeDefault: false;
|
|
1726
|
+
enumValues: undefined;
|
|
1727
|
+
baseColumn: never;
|
|
1728
|
+
identity: undefined;
|
|
1729
|
+
generated: undefined;
|
|
1730
|
+
}, {}, {}>;
|
|
1731
|
+
updatedAt: drizzle_orm_pg_core.PgColumn<{
|
|
1732
|
+
name: "updated_at";
|
|
1733
|
+
tableName: "account_deletion_requests";
|
|
1734
|
+
dataType: "date";
|
|
1735
|
+
columnType: "PgTimestamp";
|
|
1736
|
+
data: Date;
|
|
1737
|
+
driverParam: string;
|
|
1738
|
+
notNull: true;
|
|
1739
|
+
hasDefault: true;
|
|
1740
|
+
isPrimaryKey: false;
|
|
1741
|
+
isAutoincrement: false;
|
|
1742
|
+
hasRuntimeDefault: false;
|
|
1743
|
+
enumValues: undefined;
|
|
1744
|
+
baseColumn: never;
|
|
1745
|
+
identity: undefined;
|
|
1746
|
+
generated: undefined;
|
|
1747
|
+
}, {}, {}>;
|
|
1748
|
+
id: drizzle_orm_pg_core.PgColumn<{
|
|
1749
|
+
name: "id";
|
|
1750
|
+
tableName: "account_deletion_requests";
|
|
1751
|
+
dataType: "number";
|
|
1752
|
+
columnType: "PgBigSerial53";
|
|
1753
|
+
data: number;
|
|
1754
|
+
driverParam: number;
|
|
1755
|
+
notNull: true;
|
|
1756
|
+
hasDefault: true;
|
|
1757
|
+
isPrimaryKey: true;
|
|
1758
|
+
isAutoincrement: false;
|
|
1759
|
+
hasRuntimeDefault: false;
|
|
1760
|
+
enumValues: undefined;
|
|
1761
|
+
baseColumn: never;
|
|
1762
|
+
identity: undefined;
|
|
1763
|
+
generated: undefined;
|
|
1764
|
+
}, {}, {}>;
|
|
1765
|
+
userId: drizzle_orm_pg_core.PgColumn<{
|
|
1766
|
+
name: `${string}_id`;
|
|
1767
|
+
tableName: "account_deletion_requests";
|
|
1768
|
+
dataType: "number";
|
|
1769
|
+
columnType: "PgBigInt53";
|
|
1770
|
+
data: number;
|
|
1771
|
+
driverParam: string | number;
|
|
1772
|
+
notNull: false;
|
|
1773
|
+
hasDefault: false;
|
|
1774
|
+
isPrimaryKey: false;
|
|
1775
|
+
isAutoincrement: false;
|
|
1776
|
+
hasRuntimeDefault: false;
|
|
1777
|
+
enumValues: undefined;
|
|
1778
|
+
baseColumn: never;
|
|
1779
|
+
identity: undefined;
|
|
1780
|
+
generated: undefined;
|
|
1781
|
+
}, {}, {}>;
|
|
1782
|
+
userPublicId: drizzle_orm_pg_core.PgColumn<{
|
|
1783
|
+
name: "user_public_id";
|
|
1784
|
+
tableName: "account_deletion_requests";
|
|
1785
|
+
dataType: "string";
|
|
1786
|
+
columnType: "PgText";
|
|
1787
|
+
data: string;
|
|
1788
|
+
driverParam: string;
|
|
1789
|
+
notNull: true;
|
|
1790
|
+
hasDefault: false;
|
|
1791
|
+
isPrimaryKey: false;
|
|
1792
|
+
isAutoincrement: false;
|
|
1793
|
+
hasRuntimeDefault: false;
|
|
1794
|
+
enumValues: [string, ...string[]];
|
|
1795
|
+
baseColumn: never;
|
|
1796
|
+
identity: undefined;
|
|
1797
|
+
generated: undefined;
|
|
1798
|
+
}, {}, {}>;
|
|
1799
|
+
requestedAt: drizzle_orm_pg_core.PgColumn<{
|
|
1800
|
+
name: string;
|
|
1801
|
+
tableName: "account_deletion_requests";
|
|
1802
|
+
dataType: "date";
|
|
1803
|
+
columnType: "PgTimestamp";
|
|
1804
|
+
data: Date;
|
|
1805
|
+
driverParam: string;
|
|
1806
|
+
notNull: true;
|
|
1807
|
+
hasDefault: true;
|
|
1808
|
+
isPrimaryKey: false;
|
|
1809
|
+
isAutoincrement: false;
|
|
1810
|
+
hasRuntimeDefault: false;
|
|
1811
|
+
enumValues: undefined;
|
|
1812
|
+
baseColumn: never;
|
|
1813
|
+
identity: undefined;
|
|
1814
|
+
generated: undefined;
|
|
1815
|
+
}, {}, {}>;
|
|
1816
|
+
purgeScheduledAt: drizzle_orm_pg_core.PgColumn<{
|
|
1817
|
+
name: string;
|
|
1818
|
+
tableName: "account_deletion_requests";
|
|
1819
|
+
dataType: "date";
|
|
1820
|
+
columnType: "PgTimestamp";
|
|
1821
|
+
data: Date;
|
|
1822
|
+
driverParam: string;
|
|
1823
|
+
notNull: true;
|
|
1824
|
+
hasDefault: false;
|
|
1825
|
+
isPrimaryKey: false;
|
|
1826
|
+
isAutoincrement: false;
|
|
1827
|
+
hasRuntimeDefault: false;
|
|
1828
|
+
enumValues: undefined;
|
|
1829
|
+
baseColumn: never;
|
|
1830
|
+
identity: undefined;
|
|
1831
|
+
generated: undefined;
|
|
1832
|
+
}, {}, {}>;
|
|
1833
|
+
status: drizzle_orm_pg_core.PgColumn<{
|
|
1834
|
+
name: string;
|
|
1835
|
+
tableName: "account_deletion_requests";
|
|
1836
|
+
dataType: "string";
|
|
1837
|
+
columnType: "PgText";
|
|
1838
|
+
data: "pending" | "cancelled" | "completed";
|
|
1839
|
+
driverParam: string;
|
|
1840
|
+
notNull: true;
|
|
1841
|
+
hasDefault: true;
|
|
1842
|
+
isPrimaryKey: false;
|
|
1843
|
+
isAutoincrement: false;
|
|
1844
|
+
hasRuntimeDefault: false;
|
|
1845
|
+
enumValues: ["pending", "cancelled", "completed"] & [string, ...string[]];
|
|
1846
|
+
baseColumn: never;
|
|
1847
|
+
identity: undefined;
|
|
1848
|
+
generated: undefined;
|
|
1849
|
+
}, {}, {}>;
|
|
1850
|
+
requestedBy: drizzle_orm_pg_core.PgColumn<{
|
|
1851
|
+
name: string;
|
|
1852
|
+
tableName: "account_deletion_requests";
|
|
1853
|
+
dataType: "string";
|
|
1854
|
+
columnType: "PgText";
|
|
1855
|
+
data: "admin" | "self";
|
|
1856
|
+
driverParam: string;
|
|
1857
|
+
notNull: true;
|
|
1858
|
+
hasDefault: true;
|
|
1859
|
+
isPrimaryKey: false;
|
|
1860
|
+
isAutoincrement: false;
|
|
1861
|
+
hasRuntimeDefault: false;
|
|
1862
|
+
enumValues: ["self", "admin"] & [string, ...string[]];
|
|
1863
|
+
baseColumn: never;
|
|
1864
|
+
identity: undefined;
|
|
1865
|
+
generated: undefined;
|
|
1866
|
+
}, {}, {}>;
|
|
1867
|
+
reason: drizzle_orm_pg_core.PgColumn<{
|
|
1868
|
+
name: "reason";
|
|
1869
|
+
tableName: "account_deletion_requests";
|
|
1870
|
+
dataType: "string";
|
|
1871
|
+
columnType: "PgText";
|
|
1872
|
+
data: string;
|
|
1873
|
+
driverParam: string;
|
|
1874
|
+
notNull: false;
|
|
1875
|
+
hasDefault: false;
|
|
1876
|
+
isPrimaryKey: false;
|
|
1877
|
+
isAutoincrement: false;
|
|
1878
|
+
hasRuntimeDefault: false;
|
|
1879
|
+
enumValues: [string, ...string[]];
|
|
1880
|
+
baseColumn: never;
|
|
1881
|
+
identity: undefined;
|
|
1882
|
+
generated: undefined;
|
|
1883
|
+
}, {}, {}>;
|
|
1884
|
+
cancelledAt: drizzle_orm_pg_core.PgColumn<{
|
|
1885
|
+
name: string;
|
|
1886
|
+
tableName: "account_deletion_requests";
|
|
1887
|
+
dataType: "date";
|
|
1888
|
+
columnType: "PgTimestamp";
|
|
1889
|
+
data: Date;
|
|
1890
|
+
driverParam: string;
|
|
1891
|
+
notNull: false;
|
|
1892
|
+
hasDefault: false;
|
|
1893
|
+
isPrimaryKey: false;
|
|
1894
|
+
isAutoincrement: false;
|
|
1895
|
+
hasRuntimeDefault: false;
|
|
1896
|
+
enumValues: undefined;
|
|
1897
|
+
baseColumn: never;
|
|
1898
|
+
identity: undefined;
|
|
1899
|
+
generated: undefined;
|
|
1900
|
+
}, {}, {}>;
|
|
1901
|
+
completedAt: drizzle_orm_pg_core.PgColumn<{
|
|
1902
|
+
name: string;
|
|
1903
|
+
tableName: "account_deletion_requests";
|
|
1904
|
+
dataType: "date";
|
|
1905
|
+
columnType: "PgTimestamp";
|
|
1906
|
+
data: Date;
|
|
1907
|
+
driverParam: string;
|
|
1908
|
+
notNull: false;
|
|
1909
|
+
hasDefault: false;
|
|
1910
|
+
isPrimaryKey: false;
|
|
1911
|
+
isAutoincrement: false;
|
|
1912
|
+
hasRuntimeDefault: false;
|
|
1913
|
+
enumValues: undefined;
|
|
1914
|
+
baseColumn: never;
|
|
1915
|
+
identity: undefined;
|
|
1916
|
+
generated: undefined;
|
|
1917
|
+
}, {}, {}>;
|
|
1918
|
+
purgeStrategy: drizzle_orm_pg_core.PgColumn<{
|
|
1919
|
+
name: string;
|
|
1920
|
+
tableName: "account_deletion_requests";
|
|
1921
|
+
dataType: "string";
|
|
1922
|
+
columnType: "PgText";
|
|
1923
|
+
data: "anonymize" | "hard-delete";
|
|
1924
|
+
driverParam: string;
|
|
1925
|
+
notNull: false;
|
|
1926
|
+
hasDefault: false;
|
|
1927
|
+
isPrimaryKey: false;
|
|
1928
|
+
isAutoincrement: false;
|
|
1929
|
+
hasRuntimeDefault: false;
|
|
1930
|
+
enumValues: ["anonymize", "hard-delete"] & [string, ...string[]];
|
|
1931
|
+
baseColumn: never;
|
|
1932
|
+
identity: undefined;
|
|
1933
|
+
generated: undefined;
|
|
1934
|
+
}, {}, {}>;
|
|
1935
|
+
};
|
|
1936
|
+
dialect: "pg";
|
|
1937
|
+
}>;
|
|
1938
|
+
type AccountDeletionRequest = typeof accountDeletionRequests.$inferSelect;
|
|
1939
|
+
type NewAccountDeletionRequest = typeof accountDeletionRequests.$inferInsert;
|
|
1940
|
+
|
|
1941
|
+
/**
|
|
1942
|
+
* @spfn/auth - Account Deletion Service
|
|
1943
|
+
*
|
|
1944
|
+
* Request / cancel (recovery) / purge for the account deletion lifecycle.
|
|
1945
|
+
*
|
|
1946
|
+
* Lifecycle:
|
|
1947
|
+
* active --request(reauth)--> pending_deletion --grace expires(cron)--> deleted (anonymize) | row removed (hard-delete)
|
|
1948
|
+
* ^ |
|
|
1949
|
+
* +-------cancel(reauth)---------+ immediate = grace period of 0, same pipeline
|
|
1950
|
+
*/
|
|
1951
|
+
|
|
1952
|
+
interface PendingDeletionInfo {
|
|
1953
|
+
purgeScheduledAt: Date;
|
|
1954
|
+
}
|
|
1955
|
+
/**
|
|
1956
|
+
* Look up the pending deletion request's `purgeScheduledAt` for a `pending_deletion`
|
|
1957
|
+
* user, so login/OAuth/authenticate can surface it on `AccountPendingDeletionError`.
|
|
1958
|
+
* Returns null if there is no pending row (shouldn't normally happen for a user in
|
|
1959
|
+
* that status, but the caller falls back to an error without the date).
|
|
1960
|
+
*
|
|
1961
|
+
* Reads from the write primary, not a replica — this backs the same status gates
|
|
1962
|
+
* that decide whether to grant a session, so a replica-lag window must not let a
|
|
1963
|
+
* just-requested deletion go unnoticed.
|
|
1964
|
+
*/
|
|
1965
|
+
declare function getPendingDeletionInfo(userId: number): Promise<PendingDeletionInfo | null>;
|
|
1966
|
+
interface RequestAccountDeletionParams {
|
|
1967
|
+
/** Who initiated this — 'self' requires re-auth via password/verificationToken; 'admin' does not. */
|
|
1968
|
+
requestedBy: AccountDeletionRequestedBy;
|
|
1969
|
+
password?: string;
|
|
1970
|
+
verificationToken?: string;
|
|
1971
|
+
reason?: string;
|
|
1972
|
+
/** Skip the grace period and purge inline. Self-service requires `deletion.allowSelfImmediate`. */
|
|
1973
|
+
immediate?: boolean;
|
|
1974
|
+
}
|
|
1975
|
+
interface RequestAccountDeletionResult {
|
|
1976
|
+
requestId: number;
|
|
1977
|
+
purgeScheduledAt: Date;
|
|
1978
|
+
}
|
|
1979
|
+
/**
|
|
1980
|
+
* Request account deletion (self-service re-auth gate, or trusted admin/DSR call).
|
|
1981
|
+
*
|
|
1982
|
+
* Sets status -> 'pending_deletion', records the request, revokes every active
|
|
1983
|
+
* session key, and (after commit) emits `auth.deletion.requested` and sends the
|
|
1984
|
+
* request-received notice. With a zero grace period (explicit `immediate: true`,
|
|
1985
|
+
* gated for self-service by `allowSelfImmediate`), purges inline instead of
|
|
1986
|
+
* waiting for the cron sweep.
|
|
1987
|
+
*/
|
|
1988
|
+
declare function requestAccountDeletionService(userId: number, params: RequestAccountDeletionParams): Promise<RequestAccountDeletionResult>;
|
|
1989
|
+
interface CancelAccountDeletionParams {
|
|
1990
|
+
email?: string;
|
|
1991
|
+
phone?: string;
|
|
1992
|
+
password?: string;
|
|
1993
|
+
verificationToken?: string;
|
|
1994
|
+
}
|
|
1995
|
+
interface CancelAccountDeletionResult {
|
|
1996
|
+
userId: string;
|
|
1997
|
+
}
|
|
1998
|
+
/**
|
|
1999
|
+
* Cancel a pending deletion (recovery). All sessions were revoked at request
|
|
2000
|
+
* time, so this is credential-based (no Bearer token) rather than auth-context-based.
|
|
2001
|
+
*
|
|
2002
|
+
* Credential verification always runs before the caller learns whether the
|
|
2003
|
+
* account exists / is pending deletion — otherwise this endpoint could be used
|
|
2004
|
+
* to enumerate accounts or probe deletion status without a valid credential,
|
|
2005
|
+
* the same posture `loginService` takes for missing accounts.
|
|
2006
|
+
*
|
|
2007
|
+
* Claims the `account_deletion_requests` row (conditional `WHERE status =
|
|
2008
|
+
* 'pending'`) before touching `users` — see the comment at that claim below for
|
|
2009
|
+
* why the `users` row must not move until the claim succeeds.
|
|
2010
|
+
*/
|
|
2011
|
+
declare function cancelAccountDeletionService(params: CancelAccountDeletionParams): Promise<CancelAccountDeletionResult>;
|
|
2012
|
+
interface PurgeUserResult {
|
|
2013
|
+
outcome: 'purged' | 'skipped' | 'not-found';
|
|
2014
|
+
}
|
|
2015
|
+
/**
|
|
2016
|
+
* Purge a user's pending deletion request immediately, ignoring `purgeScheduledAt`.
|
|
2017
|
+
* Admin/GDPR-response entry point — the route (if any) belongs to the app.
|
|
2018
|
+
*
|
|
2019
|
+
* @throws DeletionNotRequestedError if the user has no pending request
|
|
2020
|
+
*/
|
|
2021
|
+
declare function purgeUserService(userId: number): Promise<PurgeUserResult>;
|
|
2022
|
+
interface SweepDuePurgesResult {
|
|
2023
|
+
processed: number;
|
|
2024
|
+
purged: number;
|
|
2025
|
+
skipped: number;
|
|
2026
|
+
}
|
|
2027
|
+
/**
|
|
2028
|
+
* Sweep every request whose grace period has elapsed and purge it. Called by the
|
|
2029
|
+
* `auth.deletion.purge` cron job. One user's failure (thrown error, not just an
|
|
2030
|
+
* `onBeforePurge` skip) is logged and does not stop the rest of the sweep — it
|
|
2031
|
+
* stays `pending` and is retried on the next tick.
|
|
2032
|
+
*/
|
|
2033
|
+
declare function sweepDuePurges(now?: Date): Promise<SweepDuePurgesResult>;
|
|
2034
|
+
|
|
2035
|
+
/**
|
|
2036
|
+
* @spfn/auth - Database Schema Definition
|
|
2037
|
+
*
|
|
2038
|
+
* Defines the 'spfn_auth' PostgreSQL schema for all auth-related tables
|
|
2039
|
+
*/
|
|
2040
|
+
/**
|
|
2041
|
+
* Auth schema for all authentication and authorization tables
|
|
2042
|
+
* Tables: users, roles, permissions, user_invitations, etc.
|
|
2043
|
+
*/
|
|
2044
|
+
declare const authSchema: drizzle_orm_pg_core.PgSchema<string>;
|
|
2045
|
+
|
|
2046
|
+
/**
|
|
2047
|
+
* @spfn/auth - User Profiles Entity
|
|
2048
|
+
*
|
|
2049
|
+
* User profile information (separate from authentication data)
|
|
2050
|
+
*
|
|
2051
|
+
* Features:
|
|
1709
2052
|
* - Display name and personal information
|
|
1710
2053
|
* - Avatar and bio
|
|
1711
2054
|
* - Locale and timezone preferences
|
|
@@ -1771,11 +2114,11 @@ declare const userProfiles: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
1771
2114
|
name: `${string}_id`;
|
|
1772
2115
|
tableName: "user_profiles";
|
|
1773
2116
|
dataType: "number";
|
|
1774
|
-
columnType: "
|
|
2117
|
+
columnType: "PgBigInt53";
|
|
1775
2118
|
data: number;
|
|
1776
|
-
driverParam: number;
|
|
2119
|
+
driverParam: string | number;
|
|
1777
2120
|
notNull: true;
|
|
1778
|
-
hasDefault:
|
|
2121
|
+
hasDefault: false;
|
|
1779
2122
|
isPrimaryKey: false;
|
|
1780
2123
|
isAutoincrement: false;
|
|
1781
2124
|
hasRuntimeDefault: false;
|
|
@@ -1791,7 +2134,7 @@ declare const userProfiles: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
1791
2134
|
columnType: "PgText";
|
|
1792
2135
|
data: string;
|
|
1793
2136
|
driverParam: string;
|
|
1794
|
-
notNull:
|
|
2137
|
+
notNull: false;
|
|
1795
2138
|
hasDefault: false;
|
|
1796
2139
|
isPrimaryKey: false;
|
|
1797
2140
|
isAutoincrement: false;
|
|
@@ -2065,11 +2408,11 @@ declare const userPublicKeys: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
2065
2408
|
name: `${string}_id`;
|
|
2066
2409
|
tableName: "user_public_keys";
|
|
2067
2410
|
dataType: "number";
|
|
2068
|
-
columnType: "
|
|
2411
|
+
columnType: "PgBigInt53";
|
|
2069
2412
|
data: number;
|
|
2070
|
-
driverParam: number;
|
|
2413
|
+
driverParam: string | number;
|
|
2071
2414
|
notNull: true;
|
|
2072
|
-
hasDefault:
|
|
2415
|
+
hasDefault: false;
|
|
2073
2416
|
isPrimaryKey: false;
|
|
2074
2417
|
isAutoincrement: false;
|
|
2075
2418
|
hasRuntimeDefault: false;
|
|
@@ -2318,11 +2661,11 @@ declare const userSocialAccounts: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
2318
2661
|
name: `${string}_id`;
|
|
2319
2662
|
tableName: "user_social_accounts";
|
|
2320
2663
|
dataType: "number";
|
|
2321
|
-
columnType: "
|
|
2664
|
+
columnType: "PgBigInt53";
|
|
2322
2665
|
data: number;
|
|
2323
|
-
driverParam: number;
|
|
2666
|
+
driverParam: string | number;
|
|
2324
2667
|
notNull: true;
|
|
2325
|
-
hasDefault:
|
|
2668
|
+
hasDefault: false;
|
|
2326
2669
|
isPrimaryKey: false;
|
|
2327
2670
|
isAutoincrement: false;
|
|
2328
2671
|
hasRuntimeDefault: false;
|
|
@@ -2336,14 +2679,14 @@ declare const userSocialAccounts: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
2336
2679
|
tableName: "user_social_accounts";
|
|
2337
2680
|
dataType: "string";
|
|
2338
2681
|
columnType: "PgText";
|
|
2339
|
-
data: "google" | "github" | "kakao" | "naver";
|
|
2682
|
+
data: "google" | "apple" | "github" | "kakao" | "naver" | "superself";
|
|
2340
2683
|
driverParam: string;
|
|
2341
2684
|
notNull: true;
|
|
2342
2685
|
hasDefault: false;
|
|
2343
2686
|
isPrimaryKey: false;
|
|
2344
2687
|
isAutoincrement: false;
|
|
2345
2688
|
hasRuntimeDefault: false;
|
|
2346
|
-
enumValues: ["google", "github", "kakao", "naver"] & [string, ...string[]];
|
|
2689
|
+
enumValues: ["google", "apple", "github", "kakao", "naver", "superself"] & [string, ...string[]];
|
|
2347
2690
|
baseColumn: never;
|
|
2348
2691
|
identity: undefined;
|
|
2349
2692
|
generated: undefined;
|
|
@@ -2556,14 +2899,14 @@ declare const verificationCodes: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
2556
2899
|
tableName: "verification_codes";
|
|
2557
2900
|
dataType: "string";
|
|
2558
2901
|
columnType: "PgText";
|
|
2559
|
-
data: "registration" | "login" | "password_reset" | "email_change" | "phone_change";
|
|
2902
|
+
data: "registration" | "login" | "password_reset" | "email_change" | "phone_change" | "account_deletion";
|
|
2560
2903
|
driverParam: string;
|
|
2561
2904
|
notNull: true;
|
|
2562
2905
|
hasDefault: false;
|
|
2563
2906
|
isPrimaryKey: false;
|
|
2564
2907
|
isAutoincrement: false;
|
|
2565
2908
|
hasRuntimeDefault: false;
|
|
2566
|
-
enumValues: ["registration", "login", "password_reset", "email_change", "phone_change"] & [string, ...string[]];
|
|
2909
|
+
enumValues: ["registration", "login", "password_reset", "email_change", "phone_change", "account_deletion"] & [string, ...string[]];
|
|
2567
2910
|
baseColumn: never;
|
|
2568
2911
|
identity: undefined;
|
|
2569
2912
|
generated: undefined;
|
|
@@ -2747,7 +3090,7 @@ declare const permissions: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
2747
3090
|
tableName: "permissions";
|
|
2748
3091
|
dataType: "string";
|
|
2749
3092
|
columnType: "PgText";
|
|
2750
|
-
data: "
|
|
3093
|
+
data: "auth" | "custom" | "user" | "rbac" | "system";
|
|
2751
3094
|
driverParam: string;
|
|
2752
3095
|
notNull: false;
|
|
2753
3096
|
hasDefault: false;
|
|
@@ -2905,11 +3248,11 @@ declare const rolePermissions: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
2905
3248
|
name: `${string}_id`;
|
|
2906
3249
|
tableName: "role_permissions";
|
|
2907
3250
|
dataType: "number";
|
|
2908
|
-
columnType: "
|
|
3251
|
+
columnType: "PgBigInt53";
|
|
2909
3252
|
data: number;
|
|
2910
|
-
driverParam: number;
|
|
3253
|
+
driverParam: string | number;
|
|
2911
3254
|
notNull: true;
|
|
2912
|
-
hasDefault:
|
|
3255
|
+
hasDefault: false;
|
|
2913
3256
|
isPrimaryKey: false;
|
|
2914
3257
|
isAutoincrement: false;
|
|
2915
3258
|
hasRuntimeDefault: false;
|
|
@@ -2922,11 +3265,11 @@ declare const rolePermissions: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
2922
3265
|
name: `${string}_id`;
|
|
2923
3266
|
tableName: "role_permissions";
|
|
2924
3267
|
dataType: "number";
|
|
2925
|
-
columnType: "
|
|
3268
|
+
columnType: "PgBigInt53";
|
|
2926
3269
|
data: number;
|
|
2927
|
-
driverParam: number;
|
|
3270
|
+
driverParam: string | number;
|
|
2928
3271
|
notNull: true;
|
|
2929
|
-
hasDefault:
|
|
3272
|
+
hasDefault: false;
|
|
2930
3273
|
isPrimaryKey: false;
|
|
2931
3274
|
isAutoincrement: false;
|
|
2932
3275
|
hasRuntimeDefault: false;
|
|
@@ -3014,11 +3357,11 @@ declare const userPermissions: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
3014
3357
|
name: `${string}_id`;
|
|
3015
3358
|
tableName: "user_permissions";
|
|
3016
3359
|
dataType: "number";
|
|
3017
|
-
columnType: "
|
|
3360
|
+
columnType: "PgBigInt53";
|
|
3018
3361
|
data: number;
|
|
3019
|
-
driverParam: number;
|
|
3362
|
+
driverParam: string | number;
|
|
3020
3363
|
notNull: true;
|
|
3021
|
-
hasDefault:
|
|
3364
|
+
hasDefault: false;
|
|
3022
3365
|
isPrimaryKey: false;
|
|
3023
3366
|
isAutoincrement: false;
|
|
3024
3367
|
hasRuntimeDefault: false;
|
|
@@ -3031,11 +3374,11 @@ declare const userPermissions: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
3031
3374
|
name: `${string}_id`;
|
|
3032
3375
|
tableName: "user_permissions";
|
|
3033
3376
|
dataType: "number";
|
|
3034
|
-
columnType: "
|
|
3377
|
+
columnType: "PgBigInt53";
|
|
3035
3378
|
data: number;
|
|
3036
|
-
driverParam: number;
|
|
3379
|
+
driverParam: string | number;
|
|
3037
3380
|
notNull: true;
|
|
3038
|
-
hasDefault:
|
|
3381
|
+
hasDefault: false;
|
|
3039
3382
|
isPrimaryKey: false;
|
|
3040
3383
|
isAutoincrement: false;
|
|
3041
3384
|
hasRuntimeDefault: false;
|
|
@@ -3101,6 +3444,73 @@ declare const userPermissions: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
3101
3444
|
type UserPermission = typeof userPermissions.$inferSelect;
|
|
3102
3445
|
type NewUserPermission = typeof userPermissions.$inferInsert;
|
|
3103
3446
|
|
|
3447
|
+
/**
|
|
3448
|
+
* @spfn/auth - Auth Metadata Entity
|
|
3449
|
+
*
|
|
3450
|
+
* Key-value store for auth system metadata
|
|
3451
|
+
* Used for storing RBAC config hash and other system-level settings
|
|
3452
|
+
*/
|
|
3453
|
+
declare const authMetadata: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
3454
|
+
name: "auth_metadata";
|
|
3455
|
+
schema: string;
|
|
3456
|
+
columns: {
|
|
3457
|
+
key: drizzle_orm_pg_core.PgColumn<{
|
|
3458
|
+
name: "key";
|
|
3459
|
+
tableName: "auth_metadata";
|
|
3460
|
+
dataType: "string";
|
|
3461
|
+
columnType: "PgText";
|
|
3462
|
+
data: string;
|
|
3463
|
+
driverParam: string;
|
|
3464
|
+
notNull: true;
|
|
3465
|
+
hasDefault: false;
|
|
3466
|
+
isPrimaryKey: true;
|
|
3467
|
+
isAutoincrement: false;
|
|
3468
|
+
hasRuntimeDefault: false;
|
|
3469
|
+
enumValues: [string, ...string[]];
|
|
3470
|
+
baseColumn: never;
|
|
3471
|
+
identity: undefined;
|
|
3472
|
+
generated: undefined;
|
|
3473
|
+
}, {}, {}>;
|
|
3474
|
+
value: drizzle_orm_pg_core.PgColumn<{
|
|
3475
|
+
name: "value";
|
|
3476
|
+
tableName: "auth_metadata";
|
|
3477
|
+
dataType: "string";
|
|
3478
|
+
columnType: "PgText";
|
|
3479
|
+
data: string;
|
|
3480
|
+
driverParam: string;
|
|
3481
|
+
notNull: true;
|
|
3482
|
+
hasDefault: false;
|
|
3483
|
+
isPrimaryKey: false;
|
|
3484
|
+
isAutoincrement: false;
|
|
3485
|
+
hasRuntimeDefault: false;
|
|
3486
|
+
enumValues: [string, ...string[]];
|
|
3487
|
+
baseColumn: never;
|
|
3488
|
+
identity: undefined;
|
|
3489
|
+
generated: undefined;
|
|
3490
|
+
}, {}, {}>;
|
|
3491
|
+
updatedAt: drizzle_orm_pg_core.PgColumn<{
|
|
3492
|
+
name: "updated_at";
|
|
3493
|
+
tableName: "auth_metadata";
|
|
3494
|
+
dataType: "date";
|
|
3495
|
+
columnType: "PgTimestamp";
|
|
3496
|
+
data: Date;
|
|
3497
|
+
driverParam: string;
|
|
3498
|
+
notNull: true;
|
|
3499
|
+
hasDefault: true;
|
|
3500
|
+
isPrimaryKey: false;
|
|
3501
|
+
isAutoincrement: false;
|
|
3502
|
+
hasRuntimeDefault: false;
|
|
3503
|
+
enumValues: undefined;
|
|
3504
|
+
baseColumn: never;
|
|
3505
|
+
identity: undefined;
|
|
3506
|
+
generated: undefined;
|
|
3507
|
+
}, {}, {}>;
|
|
3508
|
+
};
|
|
3509
|
+
dialect: "pg";
|
|
3510
|
+
}>;
|
|
3511
|
+
type AuthMetadataEntity = typeof authMetadata.$inferSelect;
|
|
3512
|
+
type NewAuthMetadataEntity = typeof authMetadata.$inferInsert;
|
|
3513
|
+
|
|
3104
3514
|
/**
|
|
3105
3515
|
* Users Repository
|
|
3106
3516
|
*
|
|
@@ -3122,15 +3532,44 @@ declare class UsersRepository extends BaseRepository {
|
|
|
3122
3532
|
* Read replica 사용
|
|
3123
3533
|
*/
|
|
3124
3534
|
findById(id: number): Promise<{
|
|
3535
|
+
deletedAt: Date | null;
|
|
3536
|
+
deletedBy: string | null;
|
|
3537
|
+
createdAt: Date;
|
|
3538
|
+
updatedAt: Date;
|
|
3539
|
+
id: number;
|
|
3540
|
+
publicId: string;
|
|
3541
|
+
email: string | null;
|
|
3542
|
+
phone: string | null;
|
|
3543
|
+
username: string | null;
|
|
3544
|
+
passwordHash: string | null;
|
|
3545
|
+
passwordChangeRequired: boolean;
|
|
3546
|
+
roleId: number;
|
|
3547
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
3548
|
+
emailVerifiedAt: Date | null;
|
|
3549
|
+
phoneVerifiedAt: Date | null;
|
|
3550
|
+
lastLoginAt: Date | null;
|
|
3551
|
+
}>;
|
|
3552
|
+
/**
|
|
3553
|
+
* ID로 사용자 조회 — Write primary에서 직접 읽는다.
|
|
3554
|
+
*
|
|
3555
|
+
* 복제 지연 창에서 status 전이(예: 삭제 요청으로 pending_deletion 전환)를 놓치면
|
|
3556
|
+
* 안 되는 게이트(OAuth 세션 발급 등)가 사용한다. 일반 조회는 `findById`(replica)를
|
|
3557
|
+
* 계속 사용할 것.
|
|
3558
|
+
*/
|
|
3559
|
+
findByIdOnPrimary(id: number): Promise<{
|
|
3560
|
+
deletedAt: Date | null;
|
|
3561
|
+
deletedBy: string | null;
|
|
3125
3562
|
createdAt: Date;
|
|
3126
3563
|
updatedAt: Date;
|
|
3127
3564
|
id: number;
|
|
3565
|
+
publicId: string;
|
|
3128
3566
|
email: string | null;
|
|
3129
3567
|
phone: string | null;
|
|
3568
|
+
username: string | null;
|
|
3130
3569
|
passwordHash: string | null;
|
|
3131
3570
|
passwordChangeRequired: boolean;
|
|
3132
3571
|
roleId: number;
|
|
3133
|
-
status: "active" | "inactive" | "suspended";
|
|
3572
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
3134
3573
|
emailVerifiedAt: Date | null;
|
|
3135
3574
|
phoneVerifiedAt: Date | null;
|
|
3136
3575
|
lastLoginAt: Date | null;
|
|
@@ -3140,15 +3579,19 @@ declare class UsersRepository extends BaseRepository {
|
|
|
3140
3579
|
* Read replica 사용
|
|
3141
3580
|
*/
|
|
3142
3581
|
findByEmail(email: string): Promise<{
|
|
3582
|
+
deletedAt: Date | null;
|
|
3583
|
+
deletedBy: string | null;
|
|
3143
3584
|
createdAt: Date;
|
|
3144
3585
|
updatedAt: Date;
|
|
3145
3586
|
id: number;
|
|
3587
|
+
publicId: string;
|
|
3146
3588
|
email: string | null;
|
|
3147
3589
|
phone: string | null;
|
|
3590
|
+
username: string | null;
|
|
3148
3591
|
passwordHash: string | null;
|
|
3149
3592
|
passwordChangeRequired: boolean;
|
|
3150
3593
|
roleId: number;
|
|
3151
|
-
status: "active" | "inactive" | "suspended";
|
|
3594
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
3152
3595
|
emailVerifiedAt: Date | null;
|
|
3153
3596
|
phoneVerifiedAt: Date | null;
|
|
3154
3597
|
lastLoginAt: Date | null;
|
|
@@ -3158,15 +3601,63 @@ declare class UsersRepository extends BaseRepository {
|
|
|
3158
3601
|
* Read replica 사용
|
|
3159
3602
|
*/
|
|
3160
3603
|
findByPhone(phone: string): Promise<{
|
|
3604
|
+
deletedAt: Date | null;
|
|
3605
|
+
deletedBy: string | null;
|
|
3161
3606
|
createdAt: Date;
|
|
3162
3607
|
updatedAt: Date;
|
|
3163
3608
|
id: number;
|
|
3609
|
+
publicId: string;
|
|
3164
3610
|
email: string | null;
|
|
3165
3611
|
phone: string | null;
|
|
3612
|
+
username: string | null;
|
|
3166
3613
|
passwordHash: string | null;
|
|
3167
3614
|
passwordChangeRequired: boolean;
|
|
3168
3615
|
roleId: number;
|
|
3169
|
-
status: "active" | "inactive" | "suspended";
|
|
3616
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
3617
|
+
emailVerifiedAt: Date | null;
|
|
3618
|
+
phoneVerifiedAt: Date | null;
|
|
3619
|
+
lastLoginAt: Date | null;
|
|
3620
|
+
}>;
|
|
3621
|
+
/**
|
|
3622
|
+
* 사용자명으로 사용자 조회
|
|
3623
|
+
* Read replica 사용
|
|
3624
|
+
*/
|
|
3625
|
+
findByUsername(username: string): Promise<{
|
|
3626
|
+
deletedAt: Date | null;
|
|
3627
|
+
deletedBy: string | null;
|
|
3628
|
+
createdAt: Date;
|
|
3629
|
+
updatedAt: Date;
|
|
3630
|
+
id: number;
|
|
3631
|
+
publicId: string;
|
|
3632
|
+
email: string | null;
|
|
3633
|
+
phone: string | null;
|
|
3634
|
+
username: string | null;
|
|
3635
|
+
passwordHash: string | null;
|
|
3636
|
+
passwordChangeRequired: boolean;
|
|
3637
|
+
roleId: number;
|
|
3638
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
3639
|
+
emailVerifiedAt: Date | null;
|
|
3640
|
+
phoneVerifiedAt: Date | null;
|
|
3641
|
+
lastLoginAt: Date | null;
|
|
3642
|
+
}>;
|
|
3643
|
+
/**
|
|
3644
|
+
* Public ID(UUID)로 사용자 조회
|
|
3645
|
+
* Read replica 사용
|
|
3646
|
+
*/
|
|
3647
|
+
findByPublicId(publicId: string): Promise<{
|
|
3648
|
+
deletedAt: Date | null;
|
|
3649
|
+
deletedBy: string | null;
|
|
3650
|
+
createdAt: Date;
|
|
3651
|
+
updatedAt: Date;
|
|
3652
|
+
id: number;
|
|
3653
|
+
publicId: string;
|
|
3654
|
+
email: string | null;
|
|
3655
|
+
phone: string | null;
|
|
3656
|
+
username: string | null;
|
|
3657
|
+
passwordHash: string | null;
|
|
3658
|
+
passwordChangeRequired: boolean;
|
|
3659
|
+
roleId: number;
|
|
3660
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
3170
3661
|
emailVerifiedAt: Date | null;
|
|
3171
3662
|
phoneVerifiedAt: Date | null;
|
|
3172
3663
|
lastLoginAt: Date | null;
|
|
@@ -3176,19 +3667,54 @@ declare class UsersRepository extends BaseRepository {
|
|
|
3176
3667
|
* Read replica 사용
|
|
3177
3668
|
*/
|
|
3178
3669
|
findByEmailOrPhone(email?: string, phone?: string): Promise<{
|
|
3670
|
+
deletedAt: Date | null;
|
|
3671
|
+
deletedBy: string | null;
|
|
3179
3672
|
createdAt: Date;
|
|
3180
3673
|
updatedAt: Date;
|
|
3181
3674
|
id: number;
|
|
3675
|
+
publicId: string;
|
|
3182
3676
|
email: string | null;
|
|
3183
3677
|
phone: string | null;
|
|
3678
|
+
username: string | null;
|
|
3184
3679
|
passwordHash: string | null;
|
|
3185
3680
|
passwordChangeRequired: boolean;
|
|
3186
3681
|
roleId: number;
|
|
3187
|
-
status: "active" | "inactive" | "suspended";
|
|
3682
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
3188
3683
|
emailVerifiedAt: Date | null;
|
|
3189
3684
|
phoneVerifiedAt: Date | null;
|
|
3190
3685
|
lastLoginAt: Date | null;
|
|
3191
3686
|
} | null>;
|
|
3687
|
+
/**
|
|
3688
|
+
* ID로 사용자 + Role 조회 (leftJoin)
|
|
3689
|
+
* Read replica 사용
|
|
3690
|
+
*
|
|
3691
|
+
* roleId가 null인 유저는 role: null 반환
|
|
3692
|
+
*/
|
|
3693
|
+
findByIdWithRole(id: number): Promise<{
|
|
3694
|
+
user: {
|
|
3695
|
+
deletedAt: Date | null;
|
|
3696
|
+
deletedBy: string | null;
|
|
3697
|
+
createdAt: Date;
|
|
3698
|
+
updatedAt: Date;
|
|
3699
|
+
id: number;
|
|
3700
|
+
publicId: string;
|
|
3701
|
+
email: string | null;
|
|
3702
|
+
phone: string | null;
|
|
3703
|
+
username: string | null;
|
|
3704
|
+
passwordHash: string | null;
|
|
3705
|
+
passwordChangeRequired: boolean;
|
|
3706
|
+
roleId: number;
|
|
3707
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
3708
|
+
emailVerifiedAt: Date | null;
|
|
3709
|
+
phoneVerifiedAt: Date | null;
|
|
3710
|
+
lastLoginAt: Date | null;
|
|
3711
|
+
};
|
|
3712
|
+
role: {
|
|
3713
|
+
name: string;
|
|
3714
|
+
displayName: string;
|
|
3715
|
+
priority: number;
|
|
3716
|
+
} | null;
|
|
3717
|
+
} | null>;
|
|
3192
3718
|
/**
|
|
3193
3719
|
* 사용자 생성
|
|
3194
3720
|
* Write primary 사용
|
|
@@ -3196,31 +3722,66 @@ declare class UsersRepository extends BaseRepository {
|
|
|
3196
3722
|
create(data: NewUser): Promise<{
|
|
3197
3723
|
email: string | null;
|
|
3198
3724
|
phone: string | null;
|
|
3199
|
-
status: "active" | "inactive" | "suspended";
|
|
3200
3725
|
id: number;
|
|
3201
|
-
|
|
3202
|
-
|
|
3726
|
+
publicId: string;
|
|
3727
|
+
username: string | null;
|
|
3203
3728
|
passwordHash: string | null;
|
|
3204
3729
|
passwordChangeRequired: boolean;
|
|
3205
3730
|
roleId: number;
|
|
3731
|
+
createdAt: Date;
|
|
3732
|
+
updatedAt: Date;
|
|
3733
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
3206
3734
|
emailVerifiedAt: Date | null;
|
|
3207
3735
|
phoneVerifiedAt: Date | null;
|
|
3208
3736
|
lastLoginAt: Date | null;
|
|
3737
|
+
deletedAt: Date | null;
|
|
3738
|
+
deletedBy: string | null;
|
|
3209
3739
|
}>;
|
|
3210
3740
|
/**
|
|
3211
3741
|
* 사용자 정보 업데이트
|
|
3212
3742
|
* Write primary 사용
|
|
3213
3743
|
*/
|
|
3214
3744
|
updateById(id: number, data: Partial<NewUser>): Promise<{
|
|
3745
|
+
deletedAt: Date | null;
|
|
3746
|
+
deletedBy: string | null;
|
|
3215
3747
|
createdAt: Date;
|
|
3216
3748
|
updatedAt: Date;
|
|
3217
3749
|
id: number;
|
|
3750
|
+
publicId: string;
|
|
3218
3751
|
email: string | null;
|
|
3219
3752
|
phone: string | null;
|
|
3753
|
+
username: string | null;
|
|
3220
3754
|
passwordHash: string | null;
|
|
3221
3755
|
passwordChangeRequired: boolean;
|
|
3222
3756
|
roleId: number;
|
|
3223
|
-
status: "active" | "inactive" | "suspended";
|
|
3757
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
3758
|
+
emailVerifiedAt: Date | null;
|
|
3759
|
+
phoneVerifiedAt: Date | null;
|
|
3760
|
+
lastLoginAt: Date | null;
|
|
3761
|
+
}>;
|
|
3762
|
+
/**
|
|
3763
|
+
* 계정 삭제 취소(복구) — `WHERE status = 'pending_deletion'` 조건부 UPDATE.
|
|
3764
|
+
*
|
|
3765
|
+
* cancelAccountDeletionService가 account_deletion_requests claim(조건부
|
|
3766
|
+
* markCancelled)에 성공한 **이후에만** 호출한다. 그 claim이 이미 purge와의
|
|
3767
|
+
* 경합을 해결하므로 이 조건은 방어적 이중 안전장치 — 0 row 매치(= 이미
|
|
3768
|
+
* status가 바뀐 상태) 시 null을 반환하며 예외를 던지지 않는다.
|
|
3769
|
+
* Write primary 사용
|
|
3770
|
+
*/
|
|
3771
|
+
reactivateFromPendingDeletion(id: number): Promise<{
|
|
3772
|
+
deletedAt: Date | null;
|
|
3773
|
+
deletedBy: string | null;
|
|
3774
|
+
createdAt: Date;
|
|
3775
|
+
updatedAt: Date;
|
|
3776
|
+
id: number;
|
|
3777
|
+
publicId: string;
|
|
3778
|
+
email: string | null;
|
|
3779
|
+
phone: string | null;
|
|
3780
|
+
username: string | null;
|
|
3781
|
+
passwordHash: string | null;
|
|
3782
|
+
passwordChangeRequired: boolean;
|
|
3783
|
+
roleId: number;
|
|
3784
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
3224
3785
|
emailVerifiedAt: Date | null;
|
|
3225
3786
|
phoneVerifiedAt: Date | null;
|
|
3226
3787
|
lastLoginAt: Date | null;
|
|
@@ -3230,15 +3791,19 @@ declare class UsersRepository extends BaseRepository {
|
|
|
3230
3791
|
* Write primary 사용
|
|
3231
3792
|
*/
|
|
3232
3793
|
updatePassword(id: number, passwordHash: string, clearPasswordChangeRequired?: boolean): Promise<{
|
|
3794
|
+
deletedAt: Date | null;
|
|
3795
|
+
deletedBy: string | null;
|
|
3233
3796
|
createdAt: Date;
|
|
3234
3797
|
updatedAt: Date;
|
|
3235
3798
|
id: number;
|
|
3799
|
+
publicId: string;
|
|
3236
3800
|
email: string | null;
|
|
3237
3801
|
phone: string | null;
|
|
3802
|
+
username: string | null;
|
|
3238
3803
|
passwordHash: string | null;
|
|
3239
3804
|
passwordChangeRequired: boolean;
|
|
3240
3805
|
roleId: number;
|
|
3241
|
-
status: "active" | "inactive" | "suspended";
|
|
3806
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
3242
3807
|
emailVerifiedAt: Date | null;
|
|
3243
3808
|
phoneVerifiedAt: Date | null;
|
|
3244
3809
|
lastLoginAt: Date | null;
|
|
@@ -3248,15 +3813,19 @@ declare class UsersRepository extends BaseRepository {
|
|
|
3248
3813
|
* Write primary 사용
|
|
3249
3814
|
*/
|
|
3250
3815
|
updateLastLogin(id: number): Promise<{
|
|
3816
|
+
deletedAt: Date | null;
|
|
3817
|
+
deletedBy: string | null;
|
|
3251
3818
|
createdAt: Date;
|
|
3252
3819
|
updatedAt: Date;
|
|
3253
3820
|
id: number;
|
|
3821
|
+
publicId: string;
|
|
3254
3822
|
email: string | null;
|
|
3255
3823
|
phone: string | null;
|
|
3824
|
+
username: string | null;
|
|
3256
3825
|
passwordHash: string | null;
|
|
3257
3826
|
passwordChangeRequired: boolean;
|
|
3258
3827
|
roleId: number;
|
|
3259
|
-
status: "active" | "inactive" | "suspended";
|
|
3828
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
3260
3829
|
emailVerifiedAt: Date | null;
|
|
3261
3830
|
phoneVerifiedAt: Date | null;
|
|
3262
3831
|
lastLoginAt: Date | null;
|
|
@@ -3268,16 +3837,20 @@ declare class UsersRepository extends BaseRepository {
|
|
|
3268
3837
|
deleteById(id: number): Promise<{
|
|
3269
3838
|
email: string | null;
|
|
3270
3839
|
phone: string | null;
|
|
3271
|
-
status: "active" | "inactive" | "suspended";
|
|
3272
3840
|
id: number;
|
|
3273
|
-
|
|
3274
|
-
|
|
3841
|
+
publicId: string;
|
|
3842
|
+
username: string | null;
|
|
3275
3843
|
passwordHash: string | null;
|
|
3276
3844
|
passwordChangeRequired: boolean;
|
|
3277
3845
|
roleId: number;
|
|
3846
|
+
createdAt: Date;
|
|
3847
|
+
updatedAt: Date;
|
|
3848
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
3278
3849
|
emailVerifiedAt: Date | null;
|
|
3279
3850
|
phoneVerifiedAt: Date | null;
|
|
3280
3851
|
lastLoginAt: Date | null;
|
|
3852
|
+
deletedAt: Date | null;
|
|
3853
|
+
deletedBy: string | null;
|
|
3281
3854
|
}>;
|
|
3282
3855
|
/**
|
|
3283
3856
|
* User의 Role과 Permissions 조회 (JOIN)
|
|
@@ -3297,7 +3870,7 @@ declare class UsersRepository extends BaseRepository {
|
|
|
3297
3870
|
id: number;
|
|
3298
3871
|
name: string;
|
|
3299
3872
|
displayName: string;
|
|
3300
|
-
category: "
|
|
3873
|
+
category: "auth" | "custom" | "user" | "rbac" | "system" | undefined;
|
|
3301
3874
|
}[];
|
|
3302
3875
|
}>;
|
|
3303
3876
|
/**
|
|
@@ -3309,9 +3882,12 @@ declare class UsersRepository extends BaseRepository {
|
|
|
3309
3882
|
*/
|
|
3310
3883
|
fetchMinimalUserData(userId: number): Promise<{
|
|
3311
3884
|
userId: number;
|
|
3885
|
+
publicId: string;
|
|
3312
3886
|
email: string | null;
|
|
3887
|
+
username: string | null;
|
|
3313
3888
|
isEmailVerified: boolean;
|
|
3314
3889
|
isPhoneVerified: boolean;
|
|
3890
|
+
hasPassword: boolean;
|
|
3315
3891
|
}>;
|
|
3316
3892
|
/**
|
|
3317
3893
|
* Full user data 조회 (user profile용)
|
|
@@ -3322,7 +3898,9 @@ declare class UsersRepository extends BaseRepository {
|
|
|
3322
3898
|
*/
|
|
3323
3899
|
fetchFullUserData(userId: number): Promise<{
|
|
3324
3900
|
userId: number;
|
|
3901
|
+
publicId: string;
|
|
3325
3902
|
email: string | null;
|
|
3903
|
+
username: string | null;
|
|
3326
3904
|
isEmailVerified: boolean;
|
|
3327
3905
|
isPhoneVerified: boolean;
|
|
3328
3906
|
lastLoginAt: Date | null;
|
|
@@ -3407,24 +3985,45 @@ declare class KeysRepository extends BaseRepository {
|
|
|
3407
3985
|
* Write primary 사용
|
|
3408
3986
|
*/
|
|
3409
3987
|
create(data: NewUserPublicKey): Promise<{
|
|
3410
|
-
|
|
3988
|
+
publicKey: string;
|
|
3411
3989
|
keyId: string;
|
|
3990
|
+
fingerprint: string;
|
|
3991
|
+
algorithm: "ES256" | "RS256";
|
|
3992
|
+
userId: number;
|
|
3412
3993
|
id: number;
|
|
3413
3994
|
isActive: boolean;
|
|
3414
3995
|
createdAt: Date;
|
|
3996
|
+
expiresAt: Date | null;
|
|
3997
|
+
lastUsedAt: Date | null;
|
|
3998
|
+
revokedAt: Date | null;
|
|
3999
|
+
revokedReason: string | null;
|
|
4000
|
+
}>;
|
|
4001
|
+
/**
|
|
4002
|
+
* 공개키 revoke (비활성화)
|
|
4003
|
+
* Write primary 사용
|
|
4004
|
+
*/
|
|
4005
|
+
revokeByKeyIdAndUserId(keyId: string, userId: number, reason: string): Promise<{
|
|
4006
|
+
id: number;
|
|
4007
|
+
userId: number;
|
|
4008
|
+
keyId: string;
|
|
3415
4009
|
publicKey: string;
|
|
3416
4010
|
algorithm: "ES256" | "RS256";
|
|
3417
4011
|
fingerprint: string;
|
|
4012
|
+
isActive: boolean;
|
|
4013
|
+
createdAt: Date;
|
|
3418
4014
|
lastUsedAt: Date | null;
|
|
3419
4015
|
expiresAt: Date | null;
|
|
3420
4016
|
revokedAt: Date | null;
|
|
3421
4017
|
revokedReason: string | null;
|
|
3422
4018
|
}>;
|
|
3423
4019
|
/**
|
|
3424
|
-
* 공개키 revoke (비활성화)
|
|
4020
|
+
* 사용자의 모든 활성 공개키 revoke (비활성화)
|
|
4021
|
+
*
|
|
4022
|
+
* 비번 변경 시 전체 세션 로그아웃에 사용. authenticate는 활성 키만 검증하므로,
|
|
4023
|
+
* revoke된 키로 서명한 기존 세션의 요청은 즉시 401이 된다.
|
|
3425
4024
|
* Write primary 사용
|
|
3426
4025
|
*/
|
|
3427
|
-
|
|
4026
|
+
revokeAllActiveByUserId(userId: number, reason: string): Promise<{
|
|
3428
4027
|
id: number;
|
|
3429
4028
|
userId: number;
|
|
3430
4029
|
keyId: string;
|
|
@@ -3437,25 +4036,33 @@ declare class KeysRepository extends BaseRepository {
|
|
|
3437
4036
|
expiresAt: Date | null;
|
|
3438
4037
|
revokedAt: Date | null;
|
|
3439
4038
|
revokedReason: string | null;
|
|
3440
|
-
}>;
|
|
4039
|
+
}[]>;
|
|
3441
4040
|
/**
|
|
3442
4041
|
* 공개키 삭제
|
|
3443
4042
|
* Write primary 사용
|
|
3444
4043
|
*/
|
|
3445
4044
|
deleteByKeyIdAndUserId(keyId: string, userId: number): Promise<{
|
|
3446
|
-
|
|
4045
|
+
publicKey: string;
|
|
3447
4046
|
keyId: string;
|
|
4047
|
+
fingerprint: string;
|
|
4048
|
+
algorithm: "ES256" | "RS256";
|
|
4049
|
+
userId: number;
|
|
3448
4050
|
id: number;
|
|
3449
4051
|
isActive: boolean;
|
|
3450
4052
|
createdAt: Date;
|
|
3451
|
-
publicKey: string;
|
|
3452
|
-
algorithm: "ES256" | "RS256";
|
|
3453
|
-
fingerprint: string;
|
|
3454
|
-
lastUsedAt: Date | null;
|
|
3455
4053
|
expiresAt: Date | null;
|
|
4054
|
+
lastUsedAt: Date | null;
|
|
3456
4055
|
revokedAt: Date | null;
|
|
3457
4056
|
revokedReason: string | null;
|
|
3458
4057
|
}>;
|
|
4058
|
+
/**
|
|
4059
|
+
* 사용자의 모든 공개키 삭제 (계정 익명화 파기용)
|
|
4060
|
+
*
|
|
4061
|
+
* hard-delete는 FK cascade로 자동 처리되지만, anonymize 모드는 users row를
|
|
4062
|
+
* 남기므로 자식 row를 직접 지워야 한다.
|
|
4063
|
+
* Write primary 사용
|
|
4064
|
+
*/
|
|
4065
|
+
deleteAllByUserId(userId: number): Promise<number>;
|
|
3459
4066
|
/**
|
|
3460
4067
|
* 마지막 사용 시간 업데이트
|
|
3461
4068
|
* Write primary 사용
|
|
@@ -3494,22 +4101,14 @@ declare class KeysRepository extends BaseRepository {
|
|
|
3494
4101
|
}>;
|
|
3495
4102
|
/**
|
|
3496
4103
|
* Primary key로 마지막 사용 시간 업데이트 (authenticate용)
|
|
3497
|
-
* Write primary
|
|
4104
|
+
* Write primary 사용.
|
|
4105
|
+
*
|
|
4106
|
+
* Throttled: only writes when lastUsedAt is stale (older than
|
|
4107
|
+
* LAST_USED_THROTTLE_MS), so a busy key isn't UPDATEd on every request. The
|
|
4108
|
+
* throttle lives in the WHERE clause — atomic, no read-then-write race. No
|
|
4109
|
+
* RETURNING (callers fire-and-forget and discard the row).
|
|
3498
4110
|
*/
|
|
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
|
-
}>;
|
|
4111
|
+
updateLastUsedById(id: number): Promise<void>;
|
|
3513
4112
|
}
|
|
3514
4113
|
declare const keysRepository: KeysRepository;
|
|
3515
4114
|
|
|
@@ -3541,7 +4140,7 @@ declare class VerificationCodesRepository extends BaseRepository {
|
|
|
3541
4140
|
target: string;
|
|
3542
4141
|
targetType: "email" | "phone";
|
|
3543
4142
|
code: string;
|
|
3544
|
-
purpose: "registration" | "login" | "password_reset" | "email_change" | "phone_change";
|
|
4143
|
+
purpose: "registration" | "login" | "password_reset" | "email_change" | "phone_change" | "account_deletion";
|
|
3545
4144
|
expiresAt: Date;
|
|
3546
4145
|
usedAt: Date | null;
|
|
3547
4146
|
attempts: number;
|
|
@@ -3557,7 +4156,7 @@ declare class VerificationCodesRepository extends BaseRepository {
|
|
|
3557
4156
|
target: string;
|
|
3558
4157
|
targetType: "email" | "phone";
|
|
3559
4158
|
code: string;
|
|
3560
|
-
purpose: "registration" | "login" | "password_reset" | "email_change" | "phone_change";
|
|
4159
|
+
purpose: "registration" | "login" | "password_reset" | "email_change" | "phone_change" | "account_deletion";
|
|
3561
4160
|
expiresAt: Date;
|
|
3562
4161
|
usedAt: Date | null;
|
|
3563
4162
|
attempts: number;
|
|
@@ -3567,14 +4166,14 @@ declare class VerificationCodesRepository extends BaseRepository {
|
|
|
3567
4166
|
* Write primary 사용
|
|
3568
4167
|
*/
|
|
3569
4168
|
create(data: NewVerificationCode): Promise<{
|
|
4169
|
+
target: string;
|
|
4170
|
+
targetType: "email" | "phone";
|
|
4171
|
+
purpose: "registration" | "login" | "password_reset" | "email_change" | "phone_change" | "account_deletion";
|
|
4172
|
+
code: string;
|
|
3570
4173
|
id: number;
|
|
3571
4174
|
createdAt: Date;
|
|
3572
4175
|
updatedAt: Date;
|
|
3573
4176
|
expiresAt: Date;
|
|
3574
|
-
target: string;
|
|
3575
|
-
targetType: "email" | "phone";
|
|
3576
|
-
code: string;
|
|
3577
|
-
purpose: "registration" | "login" | "password_reset" | "email_change" | "phone_change";
|
|
3578
4177
|
usedAt: Date | null;
|
|
3579
4178
|
attempts: number;
|
|
3580
4179
|
}>;
|
|
@@ -3589,7 +4188,7 @@ declare class VerificationCodesRepository extends BaseRepository {
|
|
|
3589
4188
|
target: string;
|
|
3590
4189
|
targetType: "email" | "phone";
|
|
3591
4190
|
code: string;
|
|
3592
|
-
purpose: "registration" | "login" | "password_reset" | "email_change" | "phone_change";
|
|
4191
|
+
purpose: "registration" | "login" | "password_reset" | "email_change" | "phone_change" | "account_deletion";
|
|
3593
4192
|
expiresAt: Date;
|
|
3594
4193
|
usedAt: Date | null;
|
|
3595
4194
|
attempts: number;
|
|
@@ -3605,7 +4204,7 @@ declare class VerificationCodesRepository extends BaseRepository {
|
|
|
3605
4204
|
target: string;
|
|
3606
4205
|
targetType: "email" | "phone";
|
|
3607
4206
|
code: string;
|
|
3608
|
-
purpose: "registration" | "login" | "password_reset" | "email_change" | "phone_change";
|
|
4207
|
+
purpose: "registration" | "login" | "password_reset" | "email_change" | "phone_change" | "account_deletion";
|
|
3609
4208
|
expiresAt: Date;
|
|
3610
4209
|
usedAt: Date | null;
|
|
3611
4210
|
attempts: number;
|
|
@@ -3615,6 +4214,14 @@ declare class VerificationCodesRepository extends BaseRepository {
|
|
|
3615
4214
|
* Write primary 사용
|
|
3616
4215
|
*/
|
|
3617
4216
|
deleteExpired(): Promise<number>;
|
|
4217
|
+
/**
|
|
4218
|
+
* Target(email/phone)의 모든 인증 코드 삭제 (계정 파기용)
|
|
4219
|
+
*
|
|
4220
|
+
* verificationCodes는 userId FK가 없고 target 텍스트로만 연결되므로, 파기 시
|
|
4221
|
+
* 원래 email/phone을 이 메서드로 직접 정리해야 한다.
|
|
4222
|
+
* Write primary 사용
|
|
4223
|
+
*/
|
|
4224
|
+
deleteByTarget(target: string): Promise<number>;
|
|
3618
4225
|
/**
|
|
3619
4226
|
* Target의 모든 이전 코드 무효화 (새 코드 발급 시)
|
|
3620
4227
|
* Write primary 사용
|
|
@@ -3763,7 +4370,7 @@ declare class PermissionsRepository extends BaseRepository {
|
|
|
3763
4370
|
name: string;
|
|
3764
4371
|
displayName: string;
|
|
3765
4372
|
description: string | null;
|
|
3766
|
-
category: "
|
|
4373
|
+
category: "auth" | "custom" | "user" | "rbac" | "system" | null;
|
|
3767
4374
|
isBuiltin: boolean;
|
|
3768
4375
|
isSystem: boolean;
|
|
3769
4376
|
isActive: boolean;
|
|
@@ -3779,7 +4386,7 @@ declare class PermissionsRepository extends BaseRepository {
|
|
|
3779
4386
|
name: string;
|
|
3780
4387
|
displayName: string;
|
|
3781
4388
|
description: string | null;
|
|
3782
|
-
category: "
|
|
4389
|
+
category: "auth" | "custom" | "user" | "rbac" | "system" | null;
|
|
3783
4390
|
isBuiltin: boolean;
|
|
3784
4391
|
isSystem: boolean;
|
|
3785
4392
|
isActive: boolean;
|
|
@@ -3819,7 +4426,7 @@ declare class PermissionsRepository extends BaseRepository {
|
|
|
3819
4426
|
name: string;
|
|
3820
4427
|
displayName: string;
|
|
3821
4428
|
description: string | null;
|
|
3822
|
-
category: "
|
|
4429
|
+
category: "auth" | "custom" | "user" | "rbac" | "system" | null;
|
|
3823
4430
|
isBuiltin: boolean;
|
|
3824
4431
|
isSystem: boolean;
|
|
3825
4432
|
isActive: boolean;
|
|
@@ -3830,6 +4437,7 @@ declare class PermissionsRepository extends BaseRepository {
|
|
|
3830
4437
|
*/
|
|
3831
4438
|
deleteById(id: number): Promise<{
|
|
3832
4439
|
description: string | null;
|
|
4440
|
+
metadata: Record<string, any> | null;
|
|
3833
4441
|
id: number;
|
|
3834
4442
|
name: string;
|
|
3835
4443
|
displayName: string;
|
|
@@ -3838,8 +4446,7 @@ declare class PermissionsRepository extends BaseRepository {
|
|
|
3838
4446
|
isActive: boolean;
|
|
3839
4447
|
createdAt: Date;
|
|
3840
4448
|
updatedAt: Date;
|
|
3841
|
-
|
|
3842
|
-
category: "custom" | "user" | "auth" | "rbac" | "system" | null;
|
|
4449
|
+
category: "auth" | "custom" | "user" | "rbac" | "system" | null;
|
|
3843
4450
|
}>;
|
|
3844
4451
|
}
|
|
3845
4452
|
declare const permissionsRepository: PermissionsRepository;
|
|
@@ -3884,9 +4491,9 @@ declare class RolePermissionsRepository extends BaseRepository {
|
|
|
3884
4491
|
*/
|
|
3885
4492
|
createMany(data: NewRolePermission[]): Promise<{
|
|
3886
4493
|
id: number;
|
|
4494
|
+
roleId: number;
|
|
3887
4495
|
createdAt: Date;
|
|
3888
4496
|
updatedAt: Date;
|
|
3889
|
-
roleId: number;
|
|
3890
4497
|
permissionId: number;
|
|
3891
4498
|
}[]>;
|
|
3892
4499
|
/**
|
|
@@ -3902,9 +4509,9 @@ declare class RolePermissionsRepository extends BaseRepository {
|
|
|
3902
4509
|
*/
|
|
3903
4510
|
setPermissionsForRole(roleId: number, permissionIds: number[]): Promise<{
|
|
3904
4511
|
id: number;
|
|
4512
|
+
roleId: number;
|
|
3905
4513
|
createdAt: Date;
|
|
3906
4514
|
updatedAt: Date;
|
|
3907
|
-
roleId: number;
|
|
3908
4515
|
permissionId: number;
|
|
3909
4516
|
}[]>;
|
|
3910
4517
|
}
|
|
@@ -3969,10 +4576,10 @@ declare class UserPermissionsRepository extends BaseRepository {
|
|
|
3969
4576
|
id: number;
|
|
3970
4577
|
createdAt: Date;
|
|
3971
4578
|
updatedAt: Date;
|
|
3972
|
-
expiresAt: Date | null;
|
|
3973
4579
|
permissionId: number;
|
|
3974
|
-
granted: boolean;
|
|
3975
4580
|
reason: string | null;
|
|
4581
|
+
expiresAt: Date | null;
|
|
4582
|
+
granted: boolean;
|
|
3976
4583
|
}>;
|
|
3977
4584
|
/**
|
|
3978
4585
|
* 사용자 권한 오버라이드 업데이트
|
|
@@ -3995,10 +4602,10 @@ declare class UserPermissionsRepository extends BaseRepository {
|
|
|
3995
4602
|
id: number;
|
|
3996
4603
|
createdAt: Date;
|
|
3997
4604
|
updatedAt: Date;
|
|
3998
|
-
expiresAt: Date | null;
|
|
3999
4605
|
permissionId: number;
|
|
4000
|
-
granted: boolean;
|
|
4001
4606
|
reason: string | null;
|
|
4607
|
+
expiresAt: Date | null;
|
|
4608
|
+
granted: boolean;
|
|
4002
4609
|
}>;
|
|
4003
4610
|
/**
|
|
4004
4611
|
* 사용자의 모든 권한 오버라이드 삭제
|
|
@@ -4030,7 +4637,7 @@ declare class UserProfilesRepository extends BaseRepository {
|
|
|
4030
4637
|
updatedAt: Date;
|
|
4031
4638
|
id: number;
|
|
4032
4639
|
userId: number;
|
|
4033
|
-
displayName: string;
|
|
4640
|
+
displayName: string | null;
|
|
4034
4641
|
firstName: string | null;
|
|
4035
4642
|
lastName: string | null;
|
|
4036
4643
|
avatarUrl: string | null;
|
|
@@ -4045,6 +4652,10 @@ declare class UserProfilesRepository extends BaseRepository {
|
|
|
4045
4652
|
jobTitle: string | null;
|
|
4046
4653
|
metadata: Record<string, any> | null;
|
|
4047
4654
|
}>;
|
|
4655
|
+
/**
|
|
4656
|
+
* User ID로 locale만 조회 (경량)
|
|
4657
|
+
*/
|
|
4658
|
+
findLocaleByUserId(userId: number): Promise<string>;
|
|
4048
4659
|
/**
|
|
4049
4660
|
* User ID로 프로필 조회
|
|
4050
4661
|
*/
|
|
@@ -4053,7 +4664,7 @@ declare class UserProfilesRepository extends BaseRepository {
|
|
|
4053
4664
|
updatedAt: Date;
|
|
4054
4665
|
id: number;
|
|
4055
4666
|
userId: number;
|
|
4056
|
-
displayName: string;
|
|
4667
|
+
displayName: string | null;
|
|
4057
4668
|
firstName: string | null;
|
|
4058
4669
|
lastName: string | null;
|
|
4059
4670
|
avatarUrl: string | null;
|
|
@@ -4072,9 +4683,10 @@ declare class UserProfilesRepository extends BaseRepository {
|
|
|
4072
4683
|
* 프로필 생성
|
|
4073
4684
|
*/
|
|
4074
4685
|
create(data: NewUserProfile): Promise<{
|
|
4686
|
+
metadata: Record<string, any> | null;
|
|
4075
4687
|
userId: number;
|
|
4076
4688
|
id: number;
|
|
4077
|
-
displayName: string;
|
|
4689
|
+
displayName: string | null;
|
|
4078
4690
|
createdAt: Date;
|
|
4079
4691
|
updatedAt: Date;
|
|
4080
4692
|
firstName: string | null;
|
|
@@ -4089,7 +4701,6 @@ declare class UserProfilesRepository extends BaseRepository {
|
|
|
4089
4701
|
location: string | null;
|
|
4090
4702
|
company: string | null;
|
|
4091
4703
|
jobTitle: string | null;
|
|
4092
|
-
metadata: Record<string, any> | null;
|
|
4093
4704
|
}>;
|
|
4094
4705
|
/**
|
|
4095
4706
|
* 프로필 업데이트 (by ID)
|
|
@@ -4099,7 +4710,7 @@ declare class UserProfilesRepository extends BaseRepository {
|
|
|
4099
4710
|
updatedAt: Date;
|
|
4100
4711
|
id: number;
|
|
4101
4712
|
userId: number;
|
|
4102
|
-
displayName: string;
|
|
4713
|
+
displayName: string | null;
|
|
4103
4714
|
firstName: string | null;
|
|
4104
4715
|
lastName: string | null;
|
|
4105
4716
|
avatarUrl: string | null;
|
|
@@ -4122,7 +4733,7 @@ declare class UserProfilesRepository extends BaseRepository {
|
|
|
4122
4733
|
updatedAt: Date;
|
|
4123
4734
|
id: number;
|
|
4124
4735
|
userId: number;
|
|
4125
|
-
displayName: string;
|
|
4736
|
+
displayName: string | null;
|
|
4126
4737
|
firstName: string | null;
|
|
4127
4738
|
lastName: string | null;
|
|
4128
4739
|
avatarUrl: string | null;
|
|
@@ -4141,9 +4752,10 @@ declare class UserProfilesRepository extends BaseRepository {
|
|
|
4141
4752
|
* 프로필 삭제 (by ID)
|
|
4142
4753
|
*/
|
|
4143
4754
|
deleteById(id: number): Promise<{
|
|
4755
|
+
metadata: Record<string, any> | null;
|
|
4144
4756
|
userId: number;
|
|
4145
4757
|
id: number;
|
|
4146
|
-
displayName: string;
|
|
4758
|
+
displayName: string | null;
|
|
4147
4759
|
createdAt: Date;
|
|
4148
4760
|
updatedAt: Date;
|
|
4149
4761
|
firstName: string | null;
|
|
@@ -4158,15 +4770,15 @@ declare class UserProfilesRepository extends BaseRepository {
|
|
|
4158
4770
|
location: string | null;
|
|
4159
4771
|
company: string | null;
|
|
4160
4772
|
jobTitle: string | null;
|
|
4161
|
-
metadata: Record<string, any> | null;
|
|
4162
4773
|
}>;
|
|
4163
4774
|
/**
|
|
4164
4775
|
* 프로필 삭제 (by User ID)
|
|
4165
4776
|
*/
|
|
4166
4777
|
deleteByUserId(userId: number): Promise<{
|
|
4778
|
+
metadata: Record<string, any> | null;
|
|
4167
4779
|
userId: number;
|
|
4168
4780
|
id: number;
|
|
4169
|
-
displayName: string;
|
|
4781
|
+
displayName: string | null;
|
|
4170
4782
|
createdAt: Date;
|
|
4171
4783
|
updatedAt: Date;
|
|
4172
4784
|
firstName: string | null;
|
|
@@ -4181,7 +4793,6 @@ declare class UserProfilesRepository extends BaseRepository {
|
|
|
4181
4793
|
location: string | null;
|
|
4182
4794
|
company: string | null;
|
|
4183
4795
|
jobTitle: string | null;
|
|
4184
|
-
metadata: Record<string, any> | null;
|
|
4185
4796
|
}>;
|
|
4186
4797
|
/**
|
|
4187
4798
|
* 프로필 Upsert (by User ID)
|
|
@@ -4190,9 +4801,10 @@ declare class UserProfilesRepository extends BaseRepository {
|
|
|
4190
4801
|
* 새로 생성 시 displayName은 필수 (없으면 'User'로 설정)
|
|
4191
4802
|
*/
|
|
4192
4803
|
upsertByUserId(userId: number, data: Partial<Omit<NewUserProfile, 'userId'>>): Promise<{
|
|
4804
|
+
metadata: Record<string, any> | null;
|
|
4193
4805
|
userId: number;
|
|
4194
4806
|
id: number;
|
|
4195
|
-
displayName: string;
|
|
4807
|
+
displayName: string | null;
|
|
4196
4808
|
createdAt: Date;
|
|
4197
4809
|
updatedAt: Date;
|
|
4198
4810
|
firstName: string | null;
|
|
@@ -4207,7 +4819,6 @@ declare class UserProfilesRepository extends BaseRepository {
|
|
|
4207
4819
|
location: string | null;
|
|
4208
4820
|
company: string | null;
|
|
4209
4821
|
jobTitle: string | null;
|
|
4210
|
-
metadata: Record<string, any> | null;
|
|
4211
4822
|
}>;
|
|
4212
4823
|
/**
|
|
4213
4824
|
* User ID로 프로필 데이터 조회 (formatted)
|
|
@@ -4216,7 +4827,7 @@ declare class UserProfilesRepository extends BaseRepository {
|
|
|
4216
4827
|
*/
|
|
4217
4828
|
fetchProfileData(userId: number): Promise<{
|
|
4218
4829
|
profileId: number;
|
|
4219
|
-
displayName: string;
|
|
4830
|
+
displayName: string | null;
|
|
4220
4831
|
firstName: string | null;
|
|
4221
4832
|
lastName: string | null;
|
|
4222
4833
|
avatarUrl: string | null;
|
|
@@ -4335,15 +4946,15 @@ declare class InvitationsRepository extends BaseRepository {
|
|
|
4335
4946
|
*/
|
|
4336
4947
|
create(data: NewInvitation): Promise<{
|
|
4337
4948
|
email: string;
|
|
4338
|
-
|
|
4949
|
+
metadata: Record<string, any> | null;
|
|
4339
4950
|
id: number;
|
|
4951
|
+
roleId: number;
|
|
4340
4952
|
createdAt: Date;
|
|
4341
4953
|
updatedAt: Date;
|
|
4342
|
-
|
|
4343
|
-
metadata: Record<string, any> | null;
|
|
4344
|
-
expiresAt: Date;
|
|
4954
|
+
status: "pending" | "accepted" | "expired" | "cancelled";
|
|
4345
4955
|
token: string;
|
|
4346
4956
|
invitedBy: number;
|
|
4957
|
+
expiresAt: Date;
|
|
4347
4958
|
acceptedAt: Date | null;
|
|
4348
4959
|
cancelledAt: Date | null;
|
|
4349
4960
|
}>;
|
|
@@ -4369,15 +4980,15 @@ declare class InvitationsRepository extends BaseRepository {
|
|
|
4369
4980
|
*/
|
|
4370
4981
|
deleteById(id: number): Promise<{
|
|
4371
4982
|
email: string;
|
|
4372
|
-
|
|
4983
|
+
metadata: Record<string, any> | null;
|
|
4373
4984
|
id: number;
|
|
4985
|
+
roleId: number;
|
|
4374
4986
|
createdAt: Date;
|
|
4375
4987
|
updatedAt: Date;
|
|
4376
|
-
|
|
4377
|
-
metadata: Record<string, any> | null;
|
|
4378
|
-
expiresAt: Date;
|
|
4988
|
+
status: "pending" | "accepted" | "expired" | "cancelled";
|
|
4379
4989
|
token: string;
|
|
4380
4990
|
invitedBy: number;
|
|
4991
|
+
expiresAt: Date;
|
|
4381
4992
|
acceptedAt: Date | null;
|
|
4382
4993
|
cancelledAt: Date | null;
|
|
4383
4994
|
}>;
|
|
@@ -4480,35 +5091,369 @@ declare class InvitationsRepository extends BaseRepository {
|
|
|
4480
5091
|
expiresAt: Date;
|
|
4481
5092
|
acceptedAt: Date | null;
|
|
4482
5093
|
cancelledAt: Date | null;
|
|
4483
|
-
metadata: Record<string, any> | null;
|
|
5094
|
+
metadata: Record<string, any> | null;
|
|
5095
|
+
}>;
|
|
5096
|
+
/**
|
|
5097
|
+
* 초대 취소 (status, metadata 동시 업데이트)
|
|
5098
|
+
*/
|
|
5099
|
+
cancel(id: number, cancelledBy: number, reason?: string, currentMetadata?: Record<string, any> | null): Promise<{
|
|
5100
|
+
createdAt: Date;
|
|
5101
|
+
updatedAt: Date;
|
|
5102
|
+
id: number;
|
|
5103
|
+
email: string;
|
|
5104
|
+
token: string;
|
|
5105
|
+
roleId: number;
|
|
5106
|
+
invitedBy: number;
|
|
5107
|
+
status: "pending" | "accepted" | "expired" | "cancelled";
|
|
5108
|
+
expiresAt: Date;
|
|
5109
|
+
acceptedAt: Date | null;
|
|
5110
|
+
cancelledAt: Date | null;
|
|
5111
|
+
metadata: Record<string, any> | null;
|
|
5112
|
+
}>;
|
|
5113
|
+
}
|
|
5114
|
+
declare const invitationsRepository: InvitationsRepository;
|
|
5115
|
+
|
|
5116
|
+
/**
|
|
5117
|
+
* Social Accounts Repository
|
|
5118
|
+
*
|
|
5119
|
+
* OAuth 소셜 계정 데이터 관리를 위한 Repository
|
|
5120
|
+
* BaseRepository를 상속받아 자동 트랜잭션 컨텍스트 지원 및 Read/Write 분리
|
|
5121
|
+
*/
|
|
5122
|
+
|
|
5123
|
+
/**
|
|
5124
|
+
* Social Accounts Repository 클래스
|
|
5125
|
+
*/
|
|
5126
|
+
declare class SocialAccountsRepository extends BaseRepository {
|
|
5127
|
+
/**
|
|
5128
|
+
* 저장 row 의 토큰을 평문으로 복호화해 반환한다.
|
|
5129
|
+
*
|
|
5130
|
+
* 레거시 평문(마커 없음)이 감지되면 즉시 재암호화해 저장하는
|
|
5131
|
+
* self-healing 마이그레이션을 수행한다. 호출자에게는 항상 평문이 반환되어
|
|
5132
|
+
* 외부 API 계약(평문 토큰)이 유지된다.
|
|
5133
|
+
*/
|
|
5134
|
+
private decryptAccount;
|
|
5135
|
+
/**
|
|
5136
|
+
* provider와 providerUserId로 소셜 계정 조회
|
|
5137
|
+
* Read replica 사용
|
|
5138
|
+
*/
|
|
5139
|
+
findByProviderAndProviderId(provider: SocialProvider, providerUserId: string): Promise<{
|
|
5140
|
+
accessToken: string | null;
|
|
5141
|
+
refreshToken: string | null;
|
|
5142
|
+
userId: number;
|
|
5143
|
+
id: number;
|
|
5144
|
+
createdAt: Date;
|
|
5145
|
+
updatedAt: Date;
|
|
5146
|
+
provider: "google" | "apple" | "github" | "kakao" | "naver" | "superself";
|
|
5147
|
+
providerUserId: string;
|
|
5148
|
+
providerEmail: string | null;
|
|
5149
|
+
tokenExpiresAt: Date | null;
|
|
5150
|
+
} | null>;
|
|
5151
|
+
/**
|
|
5152
|
+
* userId로 모든 소셜 계정 조회
|
|
5153
|
+
* Read replica 사용
|
|
5154
|
+
*/
|
|
5155
|
+
findByUserId(userId: number): Promise<({
|
|
5156
|
+
accessToken: string | null;
|
|
5157
|
+
refreshToken: string | null;
|
|
5158
|
+
userId: number;
|
|
5159
|
+
id: number;
|
|
5160
|
+
createdAt: Date;
|
|
5161
|
+
updatedAt: Date;
|
|
5162
|
+
provider: "google" | "apple" | "github" | "kakao" | "naver" | "superself";
|
|
5163
|
+
providerUserId: string;
|
|
5164
|
+
providerEmail: string | null;
|
|
5165
|
+
tokenExpiresAt: Date | null;
|
|
5166
|
+
} | null)[]>;
|
|
5167
|
+
/**
|
|
5168
|
+
* userId와 provider로 소셜 계정 조회
|
|
5169
|
+
* Read replica 사용
|
|
5170
|
+
*/
|
|
5171
|
+
findByUserIdAndProvider(userId: number, provider: SocialProvider): Promise<{
|
|
5172
|
+
accessToken: string | null;
|
|
5173
|
+
refreshToken: string | null;
|
|
5174
|
+
userId: number;
|
|
5175
|
+
id: number;
|
|
5176
|
+
createdAt: Date;
|
|
5177
|
+
updatedAt: Date;
|
|
5178
|
+
provider: "google" | "apple" | "github" | "kakao" | "naver" | "superself";
|
|
5179
|
+
providerUserId: string;
|
|
5180
|
+
providerEmail: string | null;
|
|
5181
|
+
tokenExpiresAt: Date | null;
|
|
5182
|
+
} | null>;
|
|
5183
|
+
/**
|
|
5184
|
+
* 소셜 계정 생성
|
|
5185
|
+
* Write primary 사용
|
|
5186
|
+
*/
|
|
5187
|
+
create(data: NewUserSocialAccount): Promise<{
|
|
5188
|
+
accessToken: string | null;
|
|
5189
|
+
refreshToken: string | null;
|
|
5190
|
+
userId: number;
|
|
5191
|
+
id: number;
|
|
5192
|
+
createdAt: Date;
|
|
5193
|
+
updatedAt: Date;
|
|
5194
|
+
provider: "google" | "apple" | "github" | "kakao" | "naver" | "superself";
|
|
5195
|
+
providerUserId: string;
|
|
5196
|
+
providerEmail: string | null;
|
|
5197
|
+
tokenExpiresAt: Date | null;
|
|
5198
|
+
} | null>;
|
|
5199
|
+
/**
|
|
5200
|
+
* 토큰 정보 업데이트
|
|
5201
|
+
* Write primary 사용
|
|
5202
|
+
*/
|
|
5203
|
+
updateTokens(id: number, data: {
|
|
5204
|
+
accessToken?: string | null;
|
|
5205
|
+
refreshToken?: string | null;
|
|
5206
|
+
tokenExpiresAt?: Date | null;
|
|
5207
|
+
}): Promise<{
|
|
5208
|
+
accessToken: string | null;
|
|
5209
|
+
refreshToken: string | null;
|
|
5210
|
+
userId: number;
|
|
5211
|
+
id: number;
|
|
5212
|
+
createdAt: Date;
|
|
5213
|
+
updatedAt: Date;
|
|
5214
|
+
provider: "google" | "apple" | "github" | "kakao" | "naver" | "superself";
|
|
5215
|
+
providerUserId: string;
|
|
5216
|
+
providerEmail: string | null;
|
|
5217
|
+
tokenExpiresAt: Date | null;
|
|
5218
|
+
} | null>;
|
|
5219
|
+
/**
|
|
5220
|
+
* 소셜 계정 삭제
|
|
5221
|
+
* Write primary 사용
|
|
5222
|
+
*/
|
|
5223
|
+
deleteById(id: number): Promise<{
|
|
5224
|
+
userId: number;
|
|
5225
|
+
id: number;
|
|
5226
|
+
createdAt: Date;
|
|
5227
|
+
updatedAt: Date;
|
|
5228
|
+
provider: "google" | "apple" | "github" | "kakao" | "naver" | "superself";
|
|
5229
|
+
providerUserId: string;
|
|
5230
|
+
providerEmail: string | null;
|
|
5231
|
+
accessToken: string | null;
|
|
5232
|
+
refreshToken: string | null;
|
|
5233
|
+
tokenExpiresAt: Date | null;
|
|
5234
|
+
}>;
|
|
5235
|
+
/**
|
|
5236
|
+
* userId와 provider로 소셜 계정 삭제
|
|
5237
|
+
* Write primary 사용
|
|
5238
|
+
*/
|
|
5239
|
+
deleteByUserIdAndProvider(userId: number, provider: SocialProvider): Promise<{
|
|
5240
|
+
userId: number;
|
|
5241
|
+
id: number;
|
|
5242
|
+
createdAt: Date;
|
|
5243
|
+
updatedAt: Date;
|
|
5244
|
+
provider: "google" | "apple" | "github" | "kakao" | "naver" | "superself";
|
|
5245
|
+
providerUserId: string;
|
|
5246
|
+
providerEmail: string | null;
|
|
5247
|
+
accessToken: string | null;
|
|
5248
|
+
refreshToken: string | null;
|
|
5249
|
+
tokenExpiresAt: Date | null;
|
|
5250
|
+
}>;
|
|
5251
|
+
/**
|
|
5252
|
+
* 사용자의 모든 소셜 계정 삭제 (계정 익명화 파기용)
|
|
5253
|
+
*
|
|
5254
|
+
* provider unique index(provider, providerUserId)를 해제해 같은 소셜 계정으로
|
|
5255
|
+
* 재가입할 수 있게 한다.
|
|
5256
|
+
* Write primary 사용
|
|
5257
|
+
*/
|
|
5258
|
+
deleteAllByUserId(userId: number): Promise<number>;
|
|
5259
|
+
}
|
|
5260
|
+
declare const socialAccountsRepository: SocialAccountsRepository;
|
|
5261
|
+
|
|
5262
|
+
/**
|
|
5263
|
+
* Auth Metadata Repository
|
|
5264
|
+
*
|
|
5265
|
+
* Key-value 기반 시스템 메타데이터 저장소
|
|
5266
|
+
* RBAC 설정 해시 등 시스템 설정값 관리
|
|
5267
|
+
*/
|
|
5268
|
+
|
|
5269
|
+
declare class AuthMetadataRepository extends BaseRepository {
|
|
5270
|
+
/**
|
|
5271
|
+
* 키로 값 조회
|
|
5272
|
+
*/
|
|
5273
|
+
get(key: string): Promise<string | null>;
|
|
5274
|
+
/**
|
|
5275
|
+
* 키-값 저장 (upsert)
|
|
5276
|
+
*/
|
|
5277
|
+
set(key: string, value: string): Promise<void>;
|
|
5278
|
+
}
|
|
5279
|
+
declare const authMetadataRepository: AuthMetadataRepository;
|
|
5280
|
+
|
|
5281
|
+
/**
|
|
5282
|
+
* Account Deletion Requests Repository
|
|
5283
|
+
*
|
|
5284
|
+
* 계정 탈퇴 요청/복구/파기 감사 이력 관리를 위한 Repository
|
|
5285
|
+
* BaseRepository를 상속받아 자동 트랜잭션 컨텍스트 지원 및 Read/Write 분리
|
|
5286
|
+
*
|
|
5287
|
+
* Row는 절대 삭제하지 않는다 — status 전이(pending -> cancelled|completed)로만 갱신.
|
|
5288
|
+
*/
|
|
5289
|
+
|
|
5290
|
+
/**
|
|
5291
|
+
* Account Deletion Requests Repository 클래스
|
|
5292
|
+
*/
|
|
5293
|
+
declare class AccountDeletionRequestsRepository extends BaseRepository {
|
|
5294
|
+
/**
|
|
5295
|
+
* ID로 요청 조회
|
|
5296
|
+
* Read replica 사용
|
|
5297
|
+
*/
|
|
5298
|
+
findById(id: number): Promise<{
|
|
5299
|
+
createdAt: Date;
|
|
5300
|
+
updatedAt: Date;
|
|
5301
|
+
id: number;
|
|
5302
|
+
userId: number | null;
|
|
5303
|
+
userPublicId: string;
|
|
5304
|
+
requestedAt: Date;
|
|
5305
|
+
purgeScheduledAt: Date;
|
|
5306
|
+
status: "pending" | "cancelled" | "completed";
|
|
5307
|
+
requestedBy: "admin" | "self";
|
|
5308
|
+
reason: string | null;
|
|
5309
|
+
cancelledAt: Date | null;
|
|
5310
|
+
completedAt: Date | null;
|
|
5311
|
+
purgeStrategy: "anonymize" | "hard-delete" | null;
|
|
5312
|
+
}>;
|
|
5313
|
+
/**
|
|
5314
|
+
* User ID로 pending 요청 조회 (유저당 최대 1건, partial unique index로 보장)
|
|
5315
|
+
* Read replica 사용
|
|
5316
|
+
*/
|
|
5317
|
+
findPendingByUserId(userId: number): Promise<{
|
|
5318
|
+
createdAt: Date;
|
|
5319
|
+
updatedAt: Date;
|
|
5320
|
+
id: number;
|
|
5321
|
+
userId: number | null;
|
|
5322
|
+
userPublicId: string;
|
|
5323
|
+
requestedAt: Date;
|
|
5324
|
+
purgeScheduledAt: Date;
|
|
5325
|
+
status: "pending" | "cancelled" | "completed";
|
|
5326
|
+
requestedBy: "admin" | "self";
|
|
5327
|
+
reason: string | null;
|
|
5328
|
+
cancelledAt: Date | null;
|
|
5329
|
+
completedAt: Date | null;
|
|
5330
|
+
purgeStrategy: "anonymize" | "hard-delete" | null;
|
|
5331
|
+
}>;
|
|
5332
|
+
/**
|
|
5333
|
+
* User ID로 pending 요청 조회 — Write primary에서 직접 읽는다.
|
|
5334
|
+
*
|
|
5335
|
+
* 로그인/OAuth/authenticate 게이트가 pending_deletion 유저의 purgeScheduledAt을
|
|
5336
|
+
* 표시하는 데 쓴다. 이 경로들은 복제 지연이 있으면 방금 커밋된 상태 전이를 놓칠 수
|
|
5337
|
+
* 있어(예: 삭제 요청 직후 OAuth 로그인 시도), replica가 아닌 primary에서 읽는다.
|
|
5338
|
+
*/
|
|
5339
|
+
findPendingByUserIdOnPrimary(userId: number): Promise<{
|
|
5340
|
+
createdAt: Date;
|
|
5341
|
+
updatedAt: Date;
|
|
5342
|
+
id: number;
|
|
5343
|
+
userId: number | null;
|
|
5344
|
+
userPublicId: string;
|
|
5345
|
+
requestedAt: Date;
|
|
5346
|
+
purgeScheduledAt: Date;
|
|
5347
|
+
status: "pending" | "cancelled" | "completed";
|
|
5348
|
+
requestedBy: "admin" | "self";
|
|
5349
|
+
reason: string | null;
|
|
5350
|
+
cancelledAt: Date | null;
|
|
5351
|
+
completedAt: Date | null;
|
|
5352
|
+
purgeStrategy: "anonymize" | "hard-delete" | null;
|
|
5353
|
+
}>;
|
|
5354
|
+
/**
|
|
5355
|
+
* 파기 스윕 대상 조회 — status='pending' AND purgeScheduledAt <= now
|
|
5356
|
+
* Read replica 사용
|
|
5357
|
+
*/
|
|
5358
|
+
findDueForPurge(now: Date): Promise<{
|
|
5359
|
+
createdAt: Date;
|
|
5360
|
+
updatedAt: Date;
|
|
5361
|
+
id: number;
|
|
5362
|
+
userId: number | null;
|
|
5363
|
+
userPublicId: string;
|
|
5364
|
+
requestedAt: Date;
|
|
5365
|
+
purgeScheduledAt: Date;
|
|
5366
|
+
status: "pending" | "cancelled" | "completed";
|
|
5367
|
+
requestedBy: "admin" | "self";
|
|
5368
|
+
reason: string | null;
|
|
5369
|
+
cancelledAt: Date | null;
|
|
5370
|
+
completedAt: Date | null;
|
|
5371
|
+
purgeStrategy: "anonymize" | "hard-delete" | null;
|
|
5372
|
+
}[]>;
|
|
5373
|
+
/**
|
|
5374
|
+
* 요청 생성
|
|
5375
|
+
* Write primary 사용
|
|
5376
|
+
*/
|
|
5377
|
+
create(data: NewAccountDeletionRequest): Promise<{
|
|
5378
|
+
userId: number | null;
|
|
5379
|
+
id: number;
|
|
5380
|
+
createdAt: Date;
|
|
5381
|
+
updatedAt: Date;
|
|
5382
|
+
status: "pending" | "cancelled" | "completed";
|
|
5383
|
+
reason: string | null;
|
|
5384
|
+
purgeScheduledAt: Date;
|
|
5385
|
+
cancelledAt: Date | null;
|
|
5386
|
+
userPublicId: string;
|
|
5387
|
+
requestedAt: Date;
|
|
5388
|
+
requestedBy: "admin" | "self";
|
|
5389
|
+
completedAt: Date | null;
|
|
5390
|
+
purgeStrategy: "anonymize" | "hard-delete" | null;
|
|
5391
|
+
}>;
|
|
5392
|
+
/**
|
|
5393
|
+
* 요청 취소 (복구)
|
|
5394
|
+
*
|
|
5395
|
+
* `WHERE status = 'pending'` 조건부 UPDATE — 이미 completed(파기 완료)된 row를
|
|
5396
|
+
* 뒤늦게 도착한 cancel이 덮어쓰지 않도록 한다. 0 row 매치(= 이미 completed거나
|
|
5397
|
+
* cancelled) 시 null을 반환하니 호출자가 그 결과를 확인해야 한다.
|
|
5398
|
+
* Write primary 사용
|
|
5399
|
+
*/
|
|
5400
|
+
markCancelled(id: number): Promise<{
|
|
5401
|
+
createdAt: Date;
|
|
5402
|
+
updatedAt: Date;
|
|
5403
|
+
id: number;
|
|
5404
|
+
userId: number | null;
|
|
5405
|
+
userPublicId: string;
|
|
5406
|
+
requestedAt: Date;
|
|
5407
|
+
purgeScheduledAt: Date;
|
|
5408
|
+
status: "pending" | "cancelled" | "completed";
|
|
5409
|
+
requestedBy: "admin" | "self";
|
|
5410
|
+
reason: string | null;
|
|
5411
|
+
cancelledAt: Date | null;
|
|
5412
|
+
completedAt: Date | null;
|
|
5413
|
+
purgeStrategy: "anonymize" | "hard-delete" | null;
|
|
4484
5414
|
}>;
|
|
4485
5415
|
/**
|
|
4486
|
-
*
|
|
5416
|
+
* 요청 완료 처리 (파기 실행 직전에 호출하는 "claim" — 파기 실행 후가 아니다)
|
|
5417
|
+
*
|
|
5418
|
+
* `WHERE status = 'pending'` 조건부 UPDATE. Postgres UPDATE는 매치되는 row에 락을
|
|
5419
|
+
* 걸고 최신 커밋 상태로 WHERE를 재평가하므로, 이 호출 하나로 (1) 동시에 취소된
|
|
5420
|
+
* row를 안전하게 걸러내고 (2) row-level 락으로 동시 파기 시도를 직렬화한다.
|
|
5421
|
+
* 0 row 매치(= 이미 취소되었거나 다른 파기가 먼저 처리함) 시 null을 반환 — 호출자는
|
|
5422
|
+
* destructive DML을 실행하기 **전에** 반드시 이 결과를 확인해야 한다.
|
|
5423
|
+
* Write primary 사용
|
|
4487
5424
|
*/
|
|
4488
|
-
|
|
5425
|
+
markCompleted(id: number, purgeStrategy: PurgeStrategy): Promise<{
|
|
4489
5426
|
createdAt: Date;
|
|
4490
5427
|
updatedAt: Date;
|
|
4491
5428
|
id: number;
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
status: "pending" | "
|
|
4497
|
-
|
|
4498
|
-
|
|
5429
|
+
userId: number | null;
|
|
5430
|
+
userPublicId: string;
|
|
5431
|
+
requestedAt: Date;
|
|
5432
|
+
purgeScheduledAt: Date;
|
|
5433
|
+
status: "pending" | "cancelled" | "completed";
|
|
5434
|
+
requestedBy: "admin" | "self";
|
|
5435
|
+
reason: string | null;
|
|
4499
5436
|
cancelledAt: Date | null;
|
|
4500
|
-
|
|
5437
|
+
completedAt: Date | null;
|
|
5438
|
+
purgeStrategy: "anonymize" | "hard-delete" | null;
|
|
4501
5439
|
}>;
|
|
4502
5440
|
}
|
|
4503
|
-
declare const
|
|
5441
|
+
declare const accountDeletionRequestsRepository: AccountDeletionRequestsRepository;
|
|
4504
5442
|
|
|
4505
5443
|
/**
|
|
4506
5444
|
* @spfn/auth - Password Helpers
|
|
4507
5445
|
*
|
|
4508
|
-
* Password hashing and verification using bcrypt
|
|
5446
|
+
* Password hashing and verification using bcrypt (@node-rs/bcrypt).
|
|
5447
|
+
*
|
|
5448
|
+
* Uses the native (Rust/napi) implementation, which runs the CPU-bound key
|
|
5449
|
+
* derivation on the libuv threadpool instead of the main event loop — so
|
|
5450
|
+
* concurrent logins run in parallel and don't head-of-line-block other requests.
|
|
5451
|
+
* Hashes are standard bcrypt ($2*$) and verify against existing bcryptjs hashes.
|
|
5452
|
+
* For very high concurrent-login load, raise UV_THREADPOOL_SIZE toward the core
|
|
5453
|
+
* count (default pool is 4).
|
|
4509
5454
|
*
|
|
4510
5455
|
* Security:
|
|
4511
|
-
* - Adaptive hashing (configurable rounds)
|
|
5456
|
+
* - Adaptive hashing (configurable rounds, default 12)
|
|
4512
5457
|
* - Automatic salt generation (per-password)
|
|
4513
5458
|
* - Constant-time comparison (timing attack protection)
|
|
4514
5459
|
* - Rainbow table protection
|
|
@@ -4521,10 +5466,10 @@ declare const invitationsRepository: InvitationsRepository;
|
|
|
4521
5466
|
* 2. Apply bcrypt key derivation (2^rounds iterations)
|
|
4522
5467
|
* 3. Return $2b$rounds$[salt][hash] (60 chars)
|
|
4523
5468
|
*
|
|
4524
|
-
* Salt rounds are configured via SPFN_AUTH_BCRYPT_SALT_ROUNDS:
|
|
4525
|
-
* -
|
|
4526
|
-
* -
|
|
4527
|
-
* - 14 rounds: ~
|
|
5469
|
+
* Salt rounds are configured via SPFN_AUTH_BCRYPT_SALT_ROUNDS (native timings):
|
|
5470
|
+
* - 12 rounds: ~200ms (default — OWASP-aligned, off the event loop)
|
|
5471
|
+
* - 10 rounds: ~55ms (faster, lower work factor)
|
|
5472
|
+
* - 14 rounds: ~800ms (very secure, heavy)
|
|
4528
5473
|
*
|
|
4529
5474
|
* @param password - Plain text password to hash
|
|
4530
5475
|
* @returns Bcrypt hash string (includes salt)
|
|
@@ -4558,6 +5503,7 @@ declare function hashPassword(password: string): Promise<string>;
|
|
|
4558
5503
|
* ```
|
|
4559
5504
|
*/
|
|
4560
5505
|
declare function verifyPassword(password: string, hash: string): Promise<boolean>;
|
|
5506
|
+
declare function getDummyPasswordHash(): Promise<string>;
|
|
4561
5507
|
/**
|
|
4562
5508
|
* Validate password strength
|
|
4563
5509
|
*
|
|
@@ -4846,6 +5792,29 @@ interface RoleGuardOptions {
|
|
|
4846
5792
|
*/
|
|
4847
5793
|
declare const roleGuard: _spfn_core_route.NamedMiddlewareFactory<"roleGuard", [options: RoleGuardOptions]>;
|
|
4848
5794
|
|
|
5795
|
+
/**
|
|
5796
|
+
* One-Time Token Authentication Middleware
|
|
5797
|
+
*
|
|
5798
|
+
* Authenticates requests using a one-time token instead of JWT.
|
|
5799
|
+
* Extracts token from query parameter `?token=xxx` or `Authorization: OTT xxx` header.
|
|
5800
|
+
*
|
|
5801
|
+
* On success, injects AuthContext identical to the `authenticate` middleware,
|
|
5802
|
+
* making it transparent to downstream handlers using `getAuth(c)`.
|
|
5803
|
+
*
|
|
5804
|
+
* Auto-skips the global 'auth' middleware.
|
|
5805
|
+
*
|
|
5806
|
+
* @example
|
|
5807
|
+
* ```typescript
|
|
5808
|
+
* export const uploadFile = route.post('/files/upload')
|
|
5809
|
+
* .use([oneTimeTokenAuth])
|
|
5810
|
+
* .handler(async (c) => {
|
|
5811
|
+
* const { userId } = getAuth(c);
|
|
5812
|
+
* // handle file upload...
|
|
5813
|
+
* });
|
|
5814
|
+
* ```
|
|
5815
|
+
*/
|
|
5816
|
+
declare const oneTimeTokenAuth: _spfn_core_route.NamedMiddleware<"oneTimeTokenAuth">;
|
|
5817
|
+
|
|
4849
5818
|
/**
|
|
4850
5819
|
* Auth Context Helpers
|
|
4851
5820
|
*
|
|
@@ -4872,6 +5841,32 @@ declare const roleGuard: _spfn_core_route.NamedMiddlewareFactory<"roleGuard", [o
|
|
|
4872
5841
|
declare function getAuth(c: Context | {
|
|
4873
5842
|
raw: Context;
|
|
4874
5843
|
}): AuthContext;
|
|
5844
|
+
/**
|
|
5845
|
+
* Get optional auth context from route context
|
|
5846
|
+
*
|
|
5847
|
+
* Returns AuthContext if authenticated, undefined otherwise.
|
|
5848
|
+
* Use with `optionalAuth` middleware for routes that serve both
|
|
5849
|
+
* authenticated and unauthenticated users.
|
|
5850
|
+
*
|
|
5851
|
+
* @example
|
|
5852
|
+
* ```typescript
|
|
5853
|
+
* export const getProducts = route.get('/products')
|
|
5854
|
+
* .use([optionalAuth])
|
|
5855
|
+
* .handler(async (c) => {
|
|
5856
|
+
* const auth = getOptionalAuth(c);
|
|
5857
|
+
*
|
|
5858
|
+
* if (auth)
|
|
5859
|
+
* {
|
|
5860
|
+
* return getPersonalizedProducts(auth.userId);
|
|
5861
|
+
* }
|
|
5862
|
+
*
|
|
5863
|
+
* return getPublicProducts();
|
|
5864
|
+
* });
|
|
5865
|
+
* ```
|
|
5866
|
+
*/
|
|
5867
|
+
declare function getOptionalAuth(c: Context | {
|
|
5868
|
+
raw: Context;
|
|
5869
|
+
}): AuthContext | undefined;
|
|
4875
5870
|
/**
|
|
4876
5871
|
* Get authenticated user from route context
|
|
4877
5872
|
*
|
|
@@ -4888,16 +5883,20 @@ declare function getUser(c: Context | {
|
|
|
4888
5883
|
}): {
|
|
4889
5884
|
email: string | null;
|
|
4890
5885
|
phone: string | null;
|
|
4891
|
-
status: "active" | "inactive" | "suspended";
|
|
4892
5886
|
id: number;
|
|
4893
|
-
|
|
4894
|
-
|
|
5887
|
+
publicId: string;
|
|
5888
|
+
username: string | null;
|
|
4895
5889
|
passwordHash: string | null;
|
|
4896
5890
|
passwordChangeRequired: boolean;
|
|
4897
5891
|
roleId: number;
|
|
5892
|
+
createdAt: Date;
|
|
5893
|
+
updatedAt: Date;
|
|
5894
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
4898
5895
|
emailVerifiedAt: Date | null;
|
|
4899
5896
|
phoneVerifiedAt: Date | null;
|
|
4900
5897
|
lastLoginAt: Date | null;
|
|
5898
|
+
deletedAt: Date | null;
|
|
5899
|
+
deletedBy: string | null;
|
|
4901
5900
|
};
|
|
4902
5901
|
/**
|
|
4903
5902
|
* Get authenticated user ID from route context
|
|
@@ -4913,6 +5912,38 @@ declare function getUser(c: Context | {
|
|
|
4913
5912
|
declare function getUserId(c: Context | {
|
|
4914
5913
|
raw: Context;
|
|
4915
5914
|
}): string;
|
|
5915
|
+
/**
|
|
5916
|
+
* Get authenticated user's role from route context
|
|
5917
|
+
*
|
|
5918
|
+
* @returns Role name or null if user has no role
|
|
5919
|
+
*
|
|
5920
|
+
* @example
|
|
5921
|
+
* ```typescript
|
|
5922
|
+
* app.bind(adminContract, [authenticate], async (c) => {
|
|
5923
|
+
* const role = getRole(c);
|
|
5924
|
+
* // 'admin' | 'superadmin' | null
|
|
5925
|
+
* });
|
|
5926
|
+
* ```
|
|
5927
|
+
*/
|
|
5928
|
+
declare function getRole(c: Context | {
|
|
5929
|
+
raw: Context;
|
|
5930
|
+
}): string | null;
|
|
5931
|
+
/**
|
|
5932
|
+
* Get authenticated user's locale from route context
|
|
5933
|
+
*
|
|
5934
|
+
* @returns Locale string (e.g., 'en', 'ko')
|
|
5935
|
+
*
|
|
5936
|
+
* @example
|
|
5937
|
+
* ```typescript
|
|
5938
|
+
* app.bind(contract, [authenticate], async (c) => {
|
|
5939
|
+
* const locale = getLocale(c);
|
|
5940
|
+
* // 'en' | 'ko' | ...
|
|
5941
|
+
* });
|
|
5942
|
+
* ```
|
|
5943
|
+
*/
|
|
5944
|
+
declare function getLocale(c: Context | {
|
|
5945
|
+
raw: Context;
|
|
5946
|
+
}): string;
|
|
4916
5947
|
/**
|
|
4917
5948
|
* Get current key ID from route context
|
|
4918
5949
|
*
|
|
@@ -4939,7 +5970,7 @@ declare function getKeyId(c: Context | {
|
|
|
4939
5970
|
* - RS256 (RSA 2048): ~294 bytes (Base64: ~392 chars)
|
|
4940
5971
|
*/
|
|
4941
5972
|
|
|
4942
|
-
type Unit =
|
|
5973
|
+
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
5974
|
type UnitAnyCase = Unit | Uppercase<Unit> | Lowercase<Unit>;
|
|
4944
5975
|
type StringValue = `${number}` | `${number}${UnitAnyCase}` | `${number} ${UnitAnyCase}`;
|
|
4945
5976
|
interface KeyPair {
|
|
@@ -4985,70 +6016,40 @@ declare function shouldRotateKey(createdAt: Date, rotationDays?: number): {
|
|
|
4985
6016
|
daysRemaining: number;
|
|
4986
6017
|
};
|
|
4987
6018
|
|
|
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
6019
|
/**
|
|
5039
6020
|
* @spfn/auth - Global Configuration
|
|
5040
6021
|
*
|
|
5041
6022
|
* Manages global auth configuration including session TTL
|
|
5042
6023
|
*/
|
|
6024
|
+
|
|
5043
6025
|
/**
|
|
5044
6026
|
* Cookie names used by SPFN Auth
|
|
6027
|
+
*
|
|
6028
|
+
* Names include a port-based suffix so that multiple dev instances
|
|
6029
|
+
* on different ports don't overwrite each other's cookies.
|
|
5045
6030
|
*/
|
|
5046
6031
|
declare const COOKIE_NAMES: {
|
|
5047
6032
|
/** Encrypted session data (userId, privateKey, keyId, algorithm) */
|
|
5048
|
-
readonly SESSION:
|
|
6033
|
+
readonly SESSION: string;
|
|
5049
6034
|
/** Current key ID (for key rotation) */
|
|
5050
|
-
readonly SESSION_KEY_ID:
|
|
6035
|
+
readonly SESSION_KEY_ID: string;
|
|
6036
|
+
/** Pending OAuth session (privateKey, keyId, algorithm) - temporary during OAuth flow */
|
|
6037
|
+
readonly OAUTH_PENDING: string;
|
|
6038
|
+
/** OAuth CSRF nonce — double-submit against the (encrypted) state.nonce at callback */
|
|
6039
|
+
readonly OAUTH_CSRF: string;
|
|
5051
6040
|
};
|
|
6041
|
+
/**
|
|
6042
|
+
* OAuth CSRF 쿠키를 PORT 접미사와 무관하게 전부 수집한다.
|
|
6043
|
+
*
|
|
6044
|
+
* 쿠키를 심는 쪽은 Next.js 프로세스, 읽는 쪽은 API 프로세스라 분리 배포에서는
|
|
6045
|
+
* 두 프로세스의 PORT가 달라 COOKIE_NAMES.OAUTH_CSRF 정확 일치 조회가 빗나간다.
|
|
6046
|
+
* nonce 자체가 랜덤값이고 암호화된 state의 nonce와 대조되므로, 접미사가 다른
|
|
6047
|
+
* spfn_oauth_csrf* 후보를 모두 대조 대상으로 넘겨도 안전하다.
|
|
6048
|
+
*/
|
|
6049
|
+
declare function matchOAuthCsrfCookies(cookies: Record<string, string>): {
|
|
6050
|
+
name: string;
|
|
6051
|
+
value: string;
|
|
6052
|
+
}[];
|
|
5052
6053
|
/**
|
|
5053
6054
|
* Parse duration string to seconds
|
|
5054
6055
|
*
|
|
@@ -5061,6 +6062,36 @@ declare const COOKIE_NAMES: {
|
|
|
5061
6062
|
* parseDuration('3600') // 3600
|
|
5062
6063
|
*/
|
|
5063
6064
|
declare function parseDuration(duration: string | number): number;
|
|
6065
|
+
/**
|
|
6066
|
+
* Registration channel passed to the beforeRegister hook
|
|
6067
|
+
*
|
|
6068
|
+
* - credentials: email/phone + password registration
|
|
6069
|
+
* - oauth: new-user signup through a social provider (web or native flow)
|
|
6070
|
+
* - invitation: invitation acceptance
|
|
6071
|
+
*/
|
|
6072
|
+
type RegisterChannel = 'credentials' | 'oauth' | 'invitation';
|
|
6073
|
+
/**
|
|
6074
|
+
* Context passed to the beforeRegister hook
|
|
6075
|
+
*
|
|
6076
|
+
* Credentials (password, keys) are intentionally excluded — the hook is a
|
|
6077
|
+
* policy gate, not a credential handler.
|
|
6078
|
+
*/
|
|
6079
|
+
interface BeforeRegisterContext {
|
|
6080
|
+
channel: RegisterChannel;
|
|
6081
|
+
/** Social provider — only set when channel is 'oauth' */
|
|
6082
|
+
provider?: SocialProvider;
|
|
6083
|
+
email?: string;
|
|
6084
|
+
/**
|
|
6085
|
+
* Whether the email is verified — only set when channel is 'oauth'.
|
|
6086
|
+
* OAuth providers may report an unverified (spoofable) email; the created
|
|
6087
|
+
* account stores it as null in that case, so email-based policies must
|
|
6088
|
+
* check this flag. credentials/invitation emails are already verified.
|
|
6089
|
+
*/
|
|
6090
|
+
emailVerified?: boolean;
|
|
6091
|
+
phone?: string;
|
|
6092
|
+
/** App-supplied registration metadata (register params / OAuth start params / invitation) */
|
|
6093
|
+
metadata?: Record<string, unknown>;
|
|
6094
|
+
}
|
|
5064
6095
|
/**
|
|
5065
6096
|
* Auth configuration
|
|
5066
6097
|
*/
|
|
@@ -5075,6 +6106,35 @@ interface AuthConfig {
|
|
|
5075
6106
|
* @default 7d (7 days)
|
|
5076
6107
|
*/
|
|
5077
6108
|
sessionTtl?: string | number;
|
|
6109
|
+
/**
|
|
6110
|
+
* App-injected validator that runs before a new user row is created,
|
|
6111
|
+
* on every registration channel (credentials, oauth, invitation).
|
|
6112
|
+
*
|
|
6113
|
+
* Throw to reject the registration — RegistrationRejectedError (403) is
|
|
6114
|
+
* the recommended error; any HttpError subclass keeps its own status.
|
|
6115
|
+
* Runs after built-in checks (verification token, duplicate account),
|
|
6116
|
+
* so existing error precedence is unchanged. Not called for admin
|
|
6117
|
+
* seeding (initializeAuth) or when linking a social account to an
|
|
6118
|
+
* existing user.
|
|
6119
|
+
*
|
|
6120
|
+
* Runs inside the registration DB transaction on every channel — keep it
|
|
6121
|
+
* fast. A slow call (e.g. an external policy API) holds a pooled DB
|
|
6122
|
+
* connection open for its full duration on every signup.
|
|
6123
|
+
*
|
|
6124
|
+
* @example
|
|
6125
|
+
* ```typescript
|
|
6126
|
+
* configureAuth({
|
|
6127
|
+
* beforeRegister: async ({ channel, metadata }) =>
|
|
6128
|
+
* {
|
|
6129
|
+
* if (channel === 'credentials' && !isOldEnough(metadata?.birthDate))
|
|
6130
|
+
* {
|
|
6131
|
+
* throw new RegistrationRejectedError({ message: 'Age requirement not met' });
|
|
6132
|
+
* }
|
|
6133
|
+
* },
|
|
6134
|
+
* });
|
|
6135
|
+
* ```
|
|
6136
|
+
*/
|
|
6137
|
+
beforeRegister?: (context: BeforeRegisterContext) => void | Promise<void>;
|
|
5078
6138
|
}
|
|
5079
6139
|
/**
|
|
5080
6140
|
* Configure global auth settings
|
|
@@ -5093,6 +6153,14 @@ declare function configureAuth(config: AuthConfig): void;
|
|
|
5093
6153
|
* Get current auth configuration
|
|
5094
6154
|
*/
|
|
5095
6155
|
declare function getAuthConfig(): AuthConfig;
|
|
6156
|
+
/**
|
|
6157
|
+
* Run the app-injected beforeRegister hook if configured — throws to reject.
|
|
6158
|
+
*
|
|
6159
|
+
* Single entry point for every registration channel so a new channel cannot
|
|
6160
|
+
* forget the configured-check. Callers invoke this right before creating the
|
|
6161
|
+
* user row.
|
|
6162
|
+
*/
|
|
6163
|
+
declare function runBeforeRegister(context: BeforeRegisterContext): Promise<void>;
|
|
5096
6164
|
/**
|
|
5097
6165
|
* Get session TTL in seconds
|
|
5098
6166
|
*
|
|
@@ -5104,6 +6172,107 @@ declare function getAuthConfig(): AuthConfig;
|
|
|
5104
6172
|
*/
|
|
5105
6173
|
declare function getSessionTtl(override?: string | number): number;
|
|
5106
6174
|
|
|
6175
|
+
/**
|
|
6176
|
+
* One-Time Token Manager
|
|
6177
|
+
*
|
|
6178
|
+
* Singleton wrapper around SSETokenManager for one-time token authentication.
|
|
6179
|
+
* Used for direct API access (file uploads, SSE streaming, etc.) bypassing RPC proxy.
|
|
6180
|
+
*/
|
|
6181
|
+
|
|
6182
|
+
/**
|
|
6183
|
+
* Initialize the one-time token manager
|
|
6184
|
+
*
|
|
6185
|
+
* Called during auth lifecycle initialization.
|
|
6186
|
+
* Creates a singleton SSETokenManager instance.
|
|
6187
|
+
*
|
|
6188
|
+
* @param config - Optional configuration
|
|
6189
|
+
* @param config.ttl - Token time-to-live in milliseconds (default: 30000)
|
|
6190
|
+
* @param config.store - Custom token store (e.g., CacheTokenStore for Redis)
|
|
6191
|
+
*/
|
|
6192
|
+
declare function initOneTimeTokenManager(config?: {
|
|
6193
|
+
ttl?: number;
|
|
6194
|
+
store?: SSETokenStore;
|
|
6195
|
+
}): void;
|
|
6196
|
+
/**
|
|
6197
|
+
* Get the one-time token manager instance
|
|
6198
|
+
*
|
|
6199
|
+
* @throws Error if initOneTimeTokenManager() has not been called
|
|
6200
|
+
*
|
|
6201
|
+
* @example
|
|
6202
|
+
* ```typescript
|
|
6203
|
+
* import { getOneTimeTokenManager } from '@spfn/auth/server';
|
|
6204
|
+
*
|
|
6205
|
+
* // Use as SSE tokenManager
|
|
6206
|
+
* .eventsConfig({
|
|
6207
|
+
* auth: {
|
|
6208
|
+
* enabled: true,
|
|
6209
|
+
* tokenManager: getOneTimeTokenManager(),
|
|
6210
|
+
* },
|
|
6211
|
+
* })
|
|
6212
|
+
* ```
|
|
6213
|
+
*/
|
|
6214
|
+
declare function getOneTimeTokenManager(): SSETokenManager;
|
|
6215
|
+
|
|
6216
|
+
/**
|
|
6217
|
+
* Account Deletion Configuration
|
|
6218
|
+
*
|
|
6219
|
+
* Singleton config for the account deletion/recovery lifecycle, mirroring the
|
|
6220
|
+
* one-time-token manager pattern (`lib/one-time-token.ts`): a mutable module-level
|
|
6221
|
+
* value, set once during `createAuthLifecycle()` and read at request/handler time.
|
|
6222
|
+
*
|
|
6223
|
+
* `purgeCron` is the one field this module does NOT make effective on its own —
|
|
6224
|
+
* `job('auth.deletion.purge').cron(expression)` bakes the cron string in at
|
|
6225
|
+
* *module-import* time, which always runs before `createAuthLifecycle()` executes
|
|
6226
|
+
* (ESM evaluates imports before the importing module's own statements). So a
|
|
6227
|
+
* statically-exported job router can never see a `purgeCron` set here. Apps that
|
|
6228
|
+
* need a non-default cron must build the router themselves, after configuring
|
|
6229
|
+
* deletion, with `createAuthDeletionJobRouter({ purgeCron })` — see
|
|
6230
|
+
* `server/jobs/deletion-purge.ts` and the README.
|
|
6231
|
+
*/
|
|
6232
|
+
|
|
6233
|
+
interface AccountDeletionPurgeUser {
|
|
6234
|
+
id: number;
|
|
6235
|
+
publicId: string;
|
|
6236
|
+
email: string | null;
|
|
6237
|
+
phone: string | null;
|
|
6238
|
+
}
|
|
6239
|
+
interface AuthDeletionConfig {
|
|
6240
|
+
/** Days between a deletion request and the purge becoming eligible. 0 = immediate. */
|
|
6241
|
+
gracePeriodDays: number;
|
|
6242
|
+
/** How the purge job destroys the account once the grace period elapses. */
|
|
6243
|
+
purgeStrategy: PurgeStrategy;
|
|
6244
|
+
/** Whether a self-service caller may request `immediate: true` (skip the grace period). */
|
|
6245
|
+
allowSelfImmediate: boolean;
|
|
6246
|
+
/**
|
|
6247
|
+
* Cron schedule for the purge sweep. NOT read automatically by the static
|
|
6248
|
+
* `authJobRouter` export — see the module doc comment above.
|
|
6249
|
+
*/
|
|
6250
|
+
purgeCron: string;
|
|
6251
|
+
/** Whether to email users (when they have one) at request/recovery/purge time. */
|
|
6252
|
+
sendNotifications: boolean;
|
|
6253
|
+
/**
|
|
6254
|
+
* Synchronous hook invoked immediately before a user is purged. Throw to skip
|
|
6255
|
+
* that user for this sweep (they stay `pending`, retried on the next tick).
|
|
6256
|
+
*/
|
|
6257
|
+
onBeforePurge?: (user: AccountDeletionPurgeUser) => Promise<void>;
|
|
6258
|
+
}
|
|
6259
|
+
declare const DEFAULT_DELETION_GRACE_PERIOD_DAYS = 30;
|
|
6260
|
+
declare const DEFAULT_DELETION_PURGE_STRATEGY: PurgeStrategy;
|
|
6261
|
+
declare const DEFAULT_DELETION_ALLOW_SELF_IMMEDIATE = false;
|
|
6262
|
+
declare const DEFAULT_DELETION_PURGE_CRON = "0 4 * * *";
|
|
6263
|
+
declare const DEFAULT_DELETION_SEND_NOTIFICATIONS = true;
|
|
6264
|
+
/**
|
|
6265
|
+
* Set the resolved deletion config. Called synchronously from `createAuthLifecycle()`
|
|
6266
|
+
* (not from `afterInfrastructure`) so it takes effect before any route/job handler
|
|
6267
|
+
* that reads `getDeletionConfig()` can run.
|
|
6268
|
+
*/
|
|
6269
|
+
declare function configureDeletion(options?: Partial<AuthDeletionConfig>): void;
|
|
6270
|
+
/**
|
|
6271
|
+
* Read the current deletion config. Safe to call any time — defaults apply even
|
|
6272
|
+
* if `createAuthLifecycle()` was never given a `deletion` block.
|
|
6273
|
+
*/
|
|
6274
|
+
declare function getDeletionConfig(): AuthDeletionConfig;
|
|
6275
|
+
|
|
5107
6276
|
/**
|
|
5108
6277
|
* @spfn/auth - Centralized Logger
|
|
5109
6278
|
*
|
|
@@ -5116,7 +6285,9 @@ declare const authLogger: {
|
|
|
5116
6285
|
general: _spfn_core_logger.Logger;
|
|
5117
6286
|
login: _spfn_core_logger.Logger;
|
|
5118
6287
|
keyRotation: _spfn_core_logger.Logger;
|
|
6288
|
+
oauth: _spfn_core_logger.Logger;
|
|
5119
6289
|
};
|
|
6290
|
+
session: _spfn_core_logger.Logger;
|
|
5120
6291
|
service: _spfn_core_logger.Logger;
|
|
5121
6292
|
setup: _spfn_core_logger.Logger;
|
|
5122
6293
|
email: _spfn_core_logger.Logger;
|
|
@@ -5197,6 +6368,337 @@ interface AuthLifecycleConfig {
|
|
|
5197
6368
|
* .build();
|
|
5198
6369
|
* ```
|
|
5199
6370
|
*/
|
|
5200
|
-
|
|
6371
|
+
/**
|
|
6372
|
+
* Options for createAuthLifecycle
|
|
6373
|
+
*/
|
|
6374
|
+
interface AuthLifecycleOptions extends AuthInitOptions {
|
|
6375
|
+
/**
|
|
6376
|
+
* One-time token configuration
|
|
6377
|
+
*
|
|
6378
|
+
* Enables one-time token issuance for direct API access
|
|
6379
|
+
* (file uploads, SSE streaming, etc.)
|
|
6380
|
+
*
|
|
6381
|
+
* @example
|
|
6382
|
+
* ```typescript
|
|
6383
|
+
* createAuthLifecycle({
|
|
6384
|
+
* oneTimeToken: { ttl: 60000 }, // 60 seconds
|
|
6385
|
+
* })
|
|
6386
|
+
* ```
|
|
6387
|
+
*/
|
|
6388
|
+
oneTimeToken?: {
|
|
6389
|
+
/**
|
|
6390
|
+
* Token time-to-live in milliseconds
|
|
6391
|
+
* @default 30000
|
|
6392
|
+
*/
|
|
6393
|
+
ttl?: number;
|
|
6394
|
+
/**
|
|
6395
|
+
* Custom token store (e.g., CacheTokenStore for Redis/Valkey)
|
|
6396
|
+
*
|
|
6397
|
+
* When provided, tokens are stored in the external store instead of in-memory Map.
|
|
6398
|
+
* Required for multi-instance deployments where token issuance and verification
|
|
6399
|
+
* may happen on different server instances.
|
|
6400
|
+
*
|
|
6401
|
+
* @example
|
|
6402
|
+
* ```typescript
|
|
6403
|
+
* import { CacheTokenStore } from '@spfn/core/event/sse';
|
|
6404
|
+
* import { getCache } from '@spfn/core/cache';
|
|
6405
|
+
*
|
|
6406
|
+
* createAuthLifecycle({
|
|
6407
|
+
* oneTimeToken: {
|
|
6408
|
+
* store: new CacheTokenStore(getCache()),
|
|
6409
|
+
* },
|
|
6410
|
+
* })
|
|
6411
|
+
* ```
|
|
6412
|
+
*/
|
|
6413
|
+
store?: SSETokenStore;
|
|
6414
|
+
};
|
|
6415
|
+
/**
|
|
6416
|
+
* Account deletion/recovery lifecycle configuration
|
|
6417
|
+
*
|
|
6418
|
+
* Controls the grace-period request → recover → purge flow exposed by
|
|
6419
|
+
* `POST /_auth/deletion/request` and `POST /_auth/deletion/cancel`.
|
|
6420
|
+
* Registering the purge job itself is a separate step — see `authJobRouter`
|
|
6421
|
+
* (`@spfn/auth/server`) and the README, since job registration happens after
|
|
6422
|
+
* this lifecycle hook runs and can't be triggered from here.
|
|
6423
|
+
*
|
|
6424
|
+
* @example
|
|
6425
|
+
* ```typescript
|
|
6426
|
+
* createAuthLifecycle({
|
|
6427
|
+
* deletion: {
|
|
6428
|
+
* gracePeriodDays: 14,
|
|
6429
|
+
* purgeStrategy: 'anonymize',
|
|
6430
|
+
* allowSelfImmediate: false,
|
|
6431
|
+
* sendNotifications: true,
|
|
6432
|
+
* onBeforePurge: async (user) => {
|
|
6433
|
+
* await appDataCleanup(user.id); // throw to skip this user this sweep
|
|
6434
|
+
* },
|
|
6435
|
+
* },
|
|
6436
|
+
* })
|
|
6437
|
+
* ```
|
|
6438
|
+
*/
|
|
6439
|
+
deletion?: {
|
|
6440
|
+
/**
|
|
6441
|
+
* Days between a deletion request and the purge becoming eligible.
|
|
6442
|
+
* 0 = immediate (still goes through the same request/purge pipeline).
|
|
6443
|
+
* @default 30
|
|
6444
|
+
*/
|
|
6445
|
+
gracePeriodDays?: number;
|
|
6446
|
+
/**
|
|
6447
|
+
* How the purge job destroys the account once the grace period elapses.
|
|
6448
|
+
* - 'anonymize': scrub PII, keep the row (status -> 'deleted') — recommended default
|
|
6449
|
+
* - 'hard-delete': physically remove the `users` row (cascades to child rows)
|
|
6450
|
+
* @default 'anonymize'
|
|
6451
|
+
*/
|
|
6452
|
+
purgeStrategy?: PurgeStrategy;
|
|
6453
|
+
/**
|
|
6454
|
+
* Whether a self-service caller may pass `immediate: true` on
|
|
6455
|
+
* `POST /_auth/deletion/request` to skip the grace period entirely.
|
|
6456
|
+
* @default false
|
|
6457
|
+
*/
|
|
6458
|
+
allowSelfImmediate?: boolean;
|
|
6459
|
+
/**
|
|
6460
|
+
* Cron schedule for the purge sweep. NOTE: this does not reach the static
|
|
6461
|
+
* `authJobRouter` export automatically — job schedules are fixed at
|
|
6462
|
+
* module-import time, before this lifecycle hook runs. For a non-default
|
|
6463
|
+
* cron, build the router yourself after this call with
|
|
6464
|
+
* `createAuthDeletionJobRouter({ purgeCron })` (see `@spfn/auth/server`).
|
|
6465
|
+
* @default '0 4 * * *'
|
|
6466
|
+
*/
|
|
6467
|
+
purgeCron?: string;
|
|
6468
|
+
/**
|
|
6469
|
+
* Whether to email users (when they have one on file) at request,
|
|
6470
|
+
* recovery, and final-purge time.
|
|
6471
|
+
* @default true
|
|
6472
|
+
*/
|
|
6473
|
+
sendNotifications?: boolean;
|
|
6474
|
+
/**
|
|
6475
|
+
* Invoked immediately before a user is purged (both the immediate inline
|
|
6476
|
+
* path and the cron sweep). Throw to skip that user for this run — they
|
|
6477
|
+
* stay `pending` and are retried on the next sweep.
|
|
6478
|
+
*/
|
|
6479
|
+
onBeforePurge?: (user: AccountDeletionPurgeUser) => Promise<void>;
|
|
6480
|
+
};
|
|
6481
|
+
}
|
|
6482
|
+
declare function createAuthLifecycle(options?: AuthLifecycleOptions): AuthLifecycleConfig;
|
|
6483
|
+
|
|
6484
|
+
/**
|
|
6485
|
+
* @spfn/auth - Account Deletion Purge Job
|
|
6486
|
+
*
|
|
6487
|
+
* The package's first background job (`@spfn/core/job`). Sweeps
|
|
6488
|
+
* `account_deletion_requests` for rows whose grace period elapsed and purges
|
|
6489
|
+
* each one (see `sweepDuePurges` in `account-deletion.service.ts`).
|
|
6490
|
+
*
|
|
6491
|
+
* Registration is NOT automatic. `createAuthLifecycle()`'s `afterInfrastructure`
|
|
6492
|
+
* hook runs *before* `initBoss`/`registerJobs` in `@spfn/core`'s server startup
|
|
6493
|
+
* (`initializeInfrastructure()`), so the lifecycle has no opportunity to register
|
|
6494
|
+
* jobs itself. Apps must register explicitly:
|
|
6495
|
+
*
|
|
6496
|
+
* ```typescript
|
|
6497
|
+
* import { authJobRouter } from '@spfn/auth/server';
|
|
6498
|
+
*
|
|
6499
|
+
* export default defineServerConfig()
|
|
6500
|
+
* .lifecycle(createAuthLifecycle())
|
|
6501
|
+
* .jobs(authJobRouter)
|
|
6502
|
+
* .routes(appRouter)
|
|
6503
|
+
* .build();
|
|
6504
|
+
* ```
|
|
6505
|
+
*
|
|
6506
|
+
* Cron schedule caveat: `job(...).cron(expression)` bakes the cron string in at
|
|
6507
|
+
* *module-import* time, which always happens before `createAuthLifecycle()` runs
|
|
6508
|
+
* in the app's own module (ESM evaluates imports before the importing module's
|
|
6509
|
+
* statements). So the static `authJobRouter` export below always uses the default
|
|
6510
|
+
* cron — `deletion.purgeCron` passed to `createAuthLifecycle()` cannot reach it.
|
|
6511
|
+
* To use a custom cron, build the router yourself, after calling
|
|
6512
|
+
* `createAuthLifecycle()`, with `createAuthDeletionJobRouter({ purgeCron })` and
|
|
6513
|
+
* register that instead of the static export.
|
|
6514
|
+
*/
|
|
6515
|
+
/**
|
|
6516
|
+
* Build the `auth.deletion.purge` job with a given cron schedule.
|
|
6517
|
+
*
|
|
6518
|
+
* @param cronExpression - Defaults to `deletion-config`'s default (daily at 04:00).
|
|
6519
|
+
*/
|
|
6520
|
+
declare function createAuthDeletionPurgeJob(cronExpression?: string): _spfn_core_job.JobDef<void, void>;
|
|
6521
|
+
/**
|
|
6522
|
+
* Build a job router containing only the deletion purge job, with a given cron.
|
|
6523
|
+
* Use this (after calling `createAuthLifecycle({ deletion: { purgeCron } })`) when
|
|
6524
|
+
* you need a non-default schedule — see the module doc comment above for why the
|
|
6525
|
+
* static `authJobRouter` export can't pick up `deletion.purgeCron` automatically.
|
|
6526
|
+
*/
|
|
6527
|
+
declare function createAuthDeletionJobRouter(options?: {
|
|
6528
|
+
purgeCron?: string;
|
|
6529
|
+
}): _spfn_core_job.JobRouter<{
|
|
6530
|
+
deletionPurge: _spfn_core_job.JobDef<void, void>;
|
|
6531
|
+
}>;
|
|
6532
|
+
/**
|
|
6533
|
+
* Default job router — the default cron (`0 4 * * *`). Register with
|
|
6534
|
+
* `.jobs(authJobRouter)` in `server.config.ts`.
|
|
6535
|
+
*/
|
|
6536
|
+
declare const authJobRouter: _spfn_core_job.JobRouter<{
|
|
6537
|
+
deletionPurge: _spfn_core_job.JobDef<void, void>;
|
|
6538
|
+
}>;
|
|
6539
|
+
|
|
6540
|
+
/**
|
|
6541
|
+
* @spfn/auth - Auth Events
|
|
6542
|
+
*
|
|
6543
|
+
* 인증 관련 이벤트 정의
|
|
6544
|
+
* - auth.login: 로그인 성공 시 (기존 사용자만)
|
|
6545
|
+
* - auth.register: 회원가입 성공 시 (OAuth 신규 가입 포함)
|
|
6546
|
+
*/
|
|
6547
|
+
/**
|
|
6548
|
+
* Auth provider type
|
|
6549
|
+
*
|
|
6550
|
+
* 직접 인증(email/phone) + 등록 가능한 모든 소셜 provider(SOCIAL_PROVIDERS).
|
|
6551
|
+
*/
|
|
6552
|
+
declare const AuthProviderSchema: _sinclair_typebox.TUnion<[_sinclair_typebox.TLiteral<"email">, _sinclair_typebox.TLiteral<"phone">, ..._sinclair_typebox.TLiteral<"google" | "apple" | "github" | "kakao" | "naver" | "superself">[]]>;
|
|
6553
|
+
/**
|
|
6554
|
+
* auth.login - 로그인 성공 이벤트
|
|
6555
|
+
*
|
|
6556
|
+
* 발행 시점:
|
|
6557
|
+
* - 이메일/전화 로그인 성공 시
|
|
6558
|
+
* - OAuth 기존 사용자 로그인 시
|
|
6559
|
+
*
|
|
6560
|
+
* @example
|
|
6561
|
+
* ```typescript
|
|
6562
|
+
* authLoginEvent.subscribe(async (payload) => {
|
|
6563
|
+
* await analytics.trackLogin(payload.userId, payload.provider);
|
|
6564
|
+
* });
|
|
6565
|
+
* ```
|
|
6566
|
+
*/
|
|
6567
|
+
declare const authLoginEvent: _spfn_core_event.EventDef<{
|
|
6568
|
+
email?: string | undefined;
|
|
6569
|
+
phone?: string | undefined;
|
|
6570
|
+
userId: string;
|
|
6571
|
+
provider: "email" | "phone" | "google" | "apple" | "github" | "kakao" | "naver" | "superself";
|
|
6572
|
+
}>;
|
|
6573
|
+
/**
|
|
6574
|
+
* auth.register - 회원가입 성공 이벤트
|
|
6575
|
+
*
|
|
6576
|
+
* 발행 시점:
|
|
6577
|
+
* - 이메일/전화 회원가입 성공 시
|
|
6578
|
+
* - OAuth 신규 사용자 가입 시
|
|
6579
|
+
*
|
|
6580
|
+
* @example
|
|
6581
|
+
* ```typescript
|
|
6582
|
+
* authRegisterEvent.subscribe(async (payload) => {
|
|
6583
|
+
* await emailService.sendWelcome(payload.email);
|
|
6584
|
+
* });
|
|
6585
|
+
* ```
|
|
6586
|
+
*/
|
|
6587
|
+
declare const authRegisterEvent: _spfn_core_event.EventDef<{
|
|
6588
|
+
email?: string | undefined;
|
|
6589
|
+
phone?: string | undefined;
|
|
6590
|
+
metadata?: {
|
|
6591
|
+
[x: string]: unknown;
|
|
6592
|
+
} | undefined;
|
|
6593
|
+
userId: string;
|
|
6594
|
+
provider: "email" | "phone" | "google" | "apple" | "github" | "kakao" | "naver" | "superself";
|
|
6595
|
+
}>;
|
|
6596
|
+
/**
|
|
6597
|
+
* auth.invitation.created - 초대 생성 이벤트
|
|
6598
|
+
*
|
|
6599
|
+
* 발행 시점:
|
|
6600
|
+
* - createInvitation() 성공 시
|
|
6601
|
+
* - resendInvitation() 성공 시
|
|
6602
|
+
*
|
|
6603
|
+
* @example
|
|
6604
|
+
* ```typescript
|
|
6605
|
+
* invitationCreatedEvent.subscribe(async (payload) => {
|
|
6606
|
+
* const inviteUrl = `${APP_URL}/invite/${payload.token}`;
|
|
6607
|
+
* await notificationService.send({
|
|
6608
|
+
* channel: 'email',
|
|
6609
|
+
* to: payload.email,
|
|
6610
|
+
* subject: 'You are invited!',
|
|
6611
|
+
* html: renderInviteEmail({ inviteUrl, ...payload.metadata }),
|
|
6612
|
+
* });
|
|
6613
|
+
* });
|
|
6614
|
+
* ```
|
|
6615
|
+
*/
|
|
6616
|
+
declare const invitationCreatedEvent: _spfn_core_event.EventDef<{
|
|
6617
|
+
metadata?: {
|
|
6618
|
+
[x: string]: unknown;
|
|
6619
|
+
} | undefined;
|
|
6620
|
+
email: string;
|
|
6621
|
+
roleId: number;
|
|
6622
|
+
token: string;
|
|
6623
|
+
invitedBy: string;
|
|
6624
|
+
expiresAt: string;
|
|
6625
|
+
invitationId: string;
|
|
6626
|
+
isResend: boolean;
|
|
6627
|
+
}>;
|
|
6628
|
+
/**
|
|
6629
|
+
* auth.invitation.accepted - 초대 수락 이벤트
|
|
6630
|
+
*
|
|
6631
|
+
* 발행 시점:
|
|
6632
|
+
* - acceptInvitation() 성공 시
|
|
6633
|
+
*
|
|
6634
|
+
* @example
|
|
6635
|
+
* ```typescript
|
|
6636
|
+
* invitationAcceptedEvent.subscribe(async (payload) => {
|
|
6637
|
+
* await onboardingService.start(payload.userId);
|
|
6638
|
+
* });
|
|
6639
|
+
* ```
|
|
6640
|
+
*/
|
|
6641
|
+
declare const invitationAcceptedEvent: _spfn_core_event.EventDef<{
|
|
6642
|
+
metadata?: {
|
|
6643
|
+
[x: string]: unknown;
|
|
6644
|
+
} | undefined;
|
|
6645
|
+
email: string;
|
|
6646
|
+
userId: string;
|
|
6647
|
+
roleId: number;
|
|
6648
|
+
invitedBy: string;
|
|
6649
|
+
invitationId: string;
|
|
6650
|
+
}>;
|
|
6651
|
+
/**
|
|
6652
|
+
* auth.deletion.requested - 계정 탈퇴 요청 이벤트
|
|
6653
|
+
*
|
|
6654
|
+
* 발행 시점:
|
|
6655
|
+
* - requestAccountDeletionService() 성공 시 (self/admin 공통)
|
|
6656
|
+
*
|
|
6657
|
+
* @example
|
|
6658
|
+
* ```typescript
|
|
6659
|
+
* authDeletionRequestedEvent.subscribe(async (payload) => {
|
|
6660
|
+
* await analytics.trackChurnRisk(payload.userId);
|
|
6661
|
+
* });
|
|
6662
|
+
* ```
|
|
6663
|
+
*/
|
|
6664
|
+
declare const authDeletionRequestedEvent: _spfn_core_event.EventDef<{
|
|
6665
|
+
userId: string;
|
|
6666
|
+
purgeScheduledAt: string;
|
|
6667
|
+
userPublicId: string;
|
|
6668
|
+
requestedBy: "admin" | "self";
|
|
6669
|
+
}>;
|
|
6670
|
+
/**
|
|
6671
|
+
* auth.deletion.cancelled - 계정 탈퇴 복구 이벤트
|
|
6672
|
+
*
|
|
6673
|
+
* 발행 시점:
|
|
6674
|
+
* - cancelAccountDeletionService() 성공 시 (유예 기간 내 복구)
|
|
6675
|
+
*/
|
|
6676
|
+
declare const authDeletionCancelledEvent: _spfn_core_event.EventDef<{
|
|
6677
|
+
userId: string;
|
|
6678
|
+
userPublicId: string;
|
|
6679
|
+
}>;
|
|
6680
|
+
/**
|
|
6681
|
+
* auth.deletion.completed - 계정 파기 완료 이벤트
|
|
6682
|
+
*
|
|
6683
|
+
* 발행 시점:
|
|
6684
|
+
* - purge job(또는 즉시 파기 경로)이 유저를 파기한 직후
|
|
6685
|
+
*
|
|
6686
|
+
* PII를 담지 않는다 — userId(내부 순번)/email/phone 없이 userPublicId만 실어
|
|
6687
|
+
* 파기 완료 이후에도 구독자가 식별 정보를 다시 축적하지 않도록 한다.
|
|
6688
|
+
*/
|
|
6689
|
+
declare const authDeletionCompletedEvent: _spfn_core_event.EventDef<{
|
|
6690
|
+
userPublicId: string;
|
|
6691
|
+
purgeStrategy: "anonymize" | "hard-delete";
|
|
6692
|
+
}>;
|
|
6693
|
+
/**
|
|
6694
|
+
* Auth event payload types
|
|
6695
|
+
*/
|
|
6696
|
+
type AuthLoginPayload = typeof authLoginEvent._payload;
|
|
6697
|
+
type AuthRegisterPayload = typeof authRegisterEvent._payload;
|
|
6698
|
+
type InvitationCreatedPayload = typeof invitationCreatedEvent._payload;
|
|
6699
|
+
type InvitationAcceptedPayload = typeof invitationAcceptedEvent._payload;
|
|
6700
|
+
type AuthDeletionRequestedPayload = typeof authDeletionRequestedEvent._payload;
|
|
6701
|
+
type AuthDeletionCancelledPayload = typeof authDeletionCancelledEvent._payload;
|
|
6702
|
+
type AuthDeletionCompletedPayload = typeof authDeletionCompletedEvent._payload;
|
|
5201
6703
|
|
|
5202
|
-
export { type AuthConfig, AuthContext, COOKIE_NAMES, type
|
|
6704
|
+
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, 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 };
|