@spfn/auth 0.2.0-beta.8 → 0.2.0-beta.81
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +561 -1827
- package/dist/authenticate-ofdEmk6x.d.ts +1109 -0
- package/dist/config.d.ts +431 -39
- package/dist/config.js +217 -29
- package/dist/config.js.map +1 -1
- package/dist/errors.d.ts +117 -3
- package/dist/errors.js +83 -1
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +351 -109
- package/dist/index.js +124 -7
- package/dist/index.js.map +1 -1
- package/dist/nextjs/api.js +591 -61
- package/dist/nextjs/api.js.map +1 -1
- package/dist/nextjs/client.d.ts +28 -0
- package/dist/nextjs/client.js +80 -0
- package/dist/nextjs/client.js.map +1 -0
- package/dist/nextjs/server.d.ts +92 -3
- package/dist/nextjs/server.js +288 -24
- package/dist/nextjs/server.js.map +1 -1
- package/dist/server.d.ts +2019 -511
- package/dist/server.js +4435 -1157
- package/dist/server.js.map +1 -1
- package/dist/session-CGxgH3C9.d.ts +53 -0
- package/dist/types-1BMx0OX1.d.ts +84 -0
- package/migrations/0001_smooth_the_fury.sql +3 -0
- package/migrations/0002_deep_iceman.sql +11 -0
- package/migrations/0003_perfect_deathbird.sql +3 -0
- package/migrations/0004_concerned_rawhide_kid.sql +5 -0
- package/migrations/0005_lethal_lifeguard.sql +32 -0
- package/migrations/0006_easy_hardball.sql +24 -0
- package/migrations/0007_glossy_major_mapleleaf.sql +1 -0
- package/migrations/meta/0001_snapshot.json +1660 -0
- package/migrations/meta/0002_snapshot.json +1660 -0
- package/migrations/meta/0003_snapshot.json +1689 -0
- package/migrations/meta/0004_snapshot.json +1721 -0
- package/migrations/meta/0005_snapshot.json +1721 -0
- package/migrations/meta/0006_snapshot.json +1921 -0
- package/migrations/meta/0007_snapshot.json +1916 -0
- package/migrations/meta/_journal.json +49 -0
- package/package.json +43 -39
- package/dist/dto-lZmWuObc.d.ts +0 -645
|
@@ -0,0 +1,1109 @@
|
|
|
1
|
+
import * as _spfn_core_route from '@spfn/core/route';
|
|
2
|
+
import { K as KeyAlgorithmType, e as SocialProvider } from './types-1BMx0OX1.js';
|
|
3
|
+
import * as _sinclair_typebox from '@sinclair/typebox';
|
|
4
|
+
import { Static } from '@sinclair/typebox';
|
|
5
|
+
import { User } from '@spfn/auth/server';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Role information for client/API responses
|
|
9
|
+
*/
|
|
10
|
+
interface Role {
|
|
11
|
+
id: number;
|
|
12
|
+
name: string;
|
|
13
|
+
displayName: string;
|
|
14
|
+
description: string | null;
|
|
15
|
+
isBuiltin: boolean;
|
|
16
|
+
isSystem: boolean;
|
|
17
|
+
isActive: boolean;
|
|
18
|
+
priority: number;
|
|
19
|
+
createdAt: Date;
|
|
20
|
+
updatedAt: Date;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Permission information for client/API responses
|
|
24
|
+
*/
|
|
25
|
+
interface Permission {
|
|
26
|
+
id: number;
|
|
27
|
+
name: string;
|
|
28
|
+
displayName: string;
|
|
29
|
+
description: string | null;
|
|
30
|
+
category: string | null;
|
|
31
|
+
isBuiltin: boolean;
|
|
32
|
+
isSystem: boolean;
|
|
33
|
+
isActive: boolean;
|
|
34
|
+
metadata: Record<string, any> | null;
|
|
35
|
+
createdAt: Date;
|
|
36
|
+
updatedAt: Date;
|
|
37
|
+
}
|
|
38
|
+
interface AuthSession {
|
|
39
|
+
userId: number;
|
|
40
|
+
publicId: string;
|
|
41
|
+
email: string | null;
|
|
42
|
+
emailVerified: boolean;
|
|
43
|
+
phoneVerified: boolean;
|
|
44
|
+
hasPassword: boolean;
|
|
45
|
+
role: Role;
|
|
46
|
+
permissions: Permission[];
|
|
47
|
+
}
|
|
48
|
+
interface ProfileInfo {
|
|
49
|
+
profileId: number;
|
|
50
|
+
displayName: string | null;
|
|
51
|
+
firstName: string | null;
|
|
52
|
+
lastName: string | null;
|
|
53
|
+
avatarUrl: string | null;
|
|
54
|
+
bio: string | null;
|
|
55
|
+
locale: string;
|
|
56
|
+
timezone: string;
|
|
57
|
+
website: string | null;
|
|
58
|
+
location: string | null;
|
|
59
|
+
company: string | null;
|
|
60
|
+
jobTitle: string | null;
|
|
61
|
+
metadata: Record<string, any> | null;
|
|
62
|
+
createdAt: Date;
|
|
63
|
+
updatedAt: Date;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* User Profile Response
|
|
67
|
+
*
|
|
68
|
+
* Complete user data including:
|
|
69
|
+
* - User fields at top level (userId, email, etc.)
|
|
70
|
+
* - Profile data as nested field (optional)
|
|
71
|
+
*
|
|
72
|
+
* Excludes:
|
|
73
|
+
* - Role and permissions (use auth session API)
|
|
74
|
+
*/
|
|
75
|
+
interface UserProfile {
|
|
76
|
+
userId: number;
|
|
77
|
+
publicId: string;
|
|
78
|
+
email: string | null;
|
|
79
|
+
username: string | null;
|
|
80
|
+
emailVerified: boolean;
|
|
81
|
+
phoneVerified: boolean;
|
|
82
|
+
lastLoginAt: Date | null;
|
|
83
|
+
createdAt: Date;
|
|
84
|
+
updatedAt: Date;
|
|
85
|
+
profile: ProfileInfo | null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* @spfn/auth - Auth Service
|
|
90
|
+
*
|
|
91
|
+
* Core authentication logic: registration, login, logout, password management
|
|
92
|
+
*/
|
|
93
|
+
|
|
94
|
+
interface RegisterParams {
|
|
95
|
+
email?: string;
|
|
96
|
+
phone?: string;
|
|
97
|
+
verificationToken: string;
|
|
98
|
+
password: string;
|
|
99
|
+
publicKey: string;
|
|
100
|
+
keyId: string;
|
|
101
|
+
fingerprint: string;
|
|
102
|
+
algorithm?: KeyAlgorithmType;
|
|
103
|
+
metadata?: Record<string, unknown>;
|
|
104
|
+
}
|
|
105
|
+
interface RegisterResult {
|
|
106
|
+
userId: string;
|
|
107
|
+
publicId: string;
|
|
108
|
+
email?: string;
|
|
109
|
+
phone?: string;
|
|
110
|
+
}
|
|
111
|
+
interface LoginParams {
|
|
112
|
+
email?: string;
|
|
113
|
+
phone?: string;
|
|
114
|
+
password: string;
|
|
115
|
+
publicKey: string;
|
|
116
|
+
keyId: string;
|
|
117
|
+
fingerprint: string;
|
|
118
|
+
oldKeyId?: string;
|
|
119
|
+
algorithm?: KeyAlgorithmType;
|
|
120
|
+
}
|
|
121
|
+
interface LoginResult {
|
|
122
|
+
userId: string;
|
|
123
|
+
publicId: string;
|
|
124
|
+
email?: string;
|
|
125
|
+
phone?: string;
|
|
126
|
+
passwordChangeRequired: boolean;
|
|
127
|
+
}
|
|
128
|
+
interface LogoutParams {
|
|
129
|
+
userId: number;
|
|
130
|
+
keyId: string;
|
|
131
|
+
}
|
|
132
|
+
interface ChangePasswordParams {
|
|
133
|
+
userId: number;
|
|
134
|
+
currentPassword?: string;
|
|
135
|
+
newPassword: string;
|
|
136
|
+
passwordHash?: string;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Register a new user account
|
|
140
|
+
*/
|
|
141
|
+
declare function registerService(params: RegisterParams): Promise<RegisterResult>;
|
|
142
|
+
/**
|
|
143
|
+
* Authenticate user and create session
|
|
144
|
+
*/
|
|
145
|
+
declare function loginService(params: LoginParams): Promise<LoginResult>;
|
|
146
|
+
/**
|
|
147
|
+
* Logout user (revoke current key)
|
|
148
|
+
*/
|
|
149
|
+
declare function logoutService(params: LogoutParams): Promise<void>;
|
|
150
|
+
/**
|
|
151
|
+
* Change user password
|
|
152
|
+
*/
|
|
153
|
+
declare function changePasswordService(params: ChangePasswordParams): Promise<void>;
|
|
154
|
+
|
|
155
|
+
declare const EmailSchema: _sinclair_typebox.TString;
|
|
156
|
+
declare const PhoneSchema: _sinclair_typebox.TString;
|
|
157
|
+
declare const PasswordSchema: _sinclair_typebox.TString;
|
|
158
|
+
declare const TargetTypeSchema: _sinclair_typebox.TUnion<[_sinclair_typebox.TLiteral<"email">, _sinclair_typebox.TLiteral<"phone">]>;
|
|
159
|
+
type VerificationTargetType = Static<typeof TargetTypeSchema>;
|
|
160
|
+
declare const VERIFICATION_TARGET_TYPES: readonly ["email", "phone"];
|
|
161
|
+
declare const VerificationPurposeSchema: _sinclair_typebox.TUnion<[_sinclair_typebox.TLiteral<"registration">, _sinclair_typebox.TLiteral<"login">, _sinclair_typebox.TLiteral<"password_reset">, _sinclair_typebox.TLiteral<"email_change">, _sinclair_typebox.TLiteral<"phone_change">, _sinclair_typebox.TLiteral<"account_deletion">]>;
|
|
162
|
+
type VerificationPurpose = Static<typeof VerificationPurposeSchema>;
|
|
163
|
+
declare const VERIFICATION_PURPOSES: readonly ["registration", "login", "password_reset", "email_change", "phone_change", "account_deletion"];
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* @spfn/auth - Verification Service
|
|
167
|
+
*
|
|
168
|
+
* Handles OTP code generation, validation, and delivery
|
|
169
|
+
*/
|
|
170
|
+
|
|
171
|
+
interface SendVerificationCodeParams {
|
|
172
|
+
target: string;
|
|
173
|
+
targetType: VerificationTargetType;
|
|
174
|
+
purpose: VerificationPurpose;
|
|
175
|
+
}
|
|
176
|
+
interface SendVerificationCodeResult {
|
|
177
|
+
success: boolean;
|
|
178
|
+
expiresAt: string;
|
|
179
|
+
}
|
|
180
|
+
interface VerifyCodeParams {
|
|
181
|
+
target: string;
|
|
182
|
+
targetType: VerificationTargetType;
|
|
183
|
+
code: string;
|
|
184
|
+
purpose: VerificationPurpose;
|
|
185
|
+
}
|
|
186
|
+
interface VerifyCodeResult {
|
|
187
|
+
valid: boolean;
|
|
188
|
+
verificationToken: string;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Send verification code via email or SMS
|
|
192
|
+
*/
|
|
193
|
+
declare function sendVerificationCodeService(params: SendVerificationCodeParams): Promise<SendVerificationCodeResult>;
|
|
194
|
+
/**
|
|
195
|
+
* Verify OTP code and return verification token
|
|
196
|
+
*/
|
|
197
|
+
declare function verifyCodeService(params: VerifyCodeParams): Promise<{
|
|
198
|
+
valid: boolean;
|
|
199
|
+
verificationToken: string;
|
|
200
|
+
}>;
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* @spfn/auth - Key Service
|
|
204
|
+
*
|
|
205
|
+
* Handles public key registration, rotation, and revocation
|
|
206
|
+
*/
|
|
207
|
+
|
|
208
|
+
interface RegisterPublicKeyParams {
|
|
209
|
+
userId: number;
|
|
210
|
+
keyId: string;
|
|
211
|
+
publicKey: string;
|
|
212
|
+
fingerprint: string;
|
|
213
|
+
algorithm?: KeyAlgorithmType;
|
|
214
|
+
}
|
|
215
|
+
interface RotateKeyParams {
|
|
216
|
+
userId: number;
|
|
217
|
+
oldKeyId: string;
|
|
218
|
+
newKeyId: string;
|
|
219
|
+
newPublicKey: string;
|
|
220
|
+
fingerprint: string;
|
|
221
|
+
algorithm?: KeyAlgorithmType;
|
|
222
|
+
}
|
|
223
|
+
interface RotateKeyResult {
|
|
224
|
+
success: boolean;
|
|
225
|
+
keyId: string;
|
|
226
|
+
}
|
|
227
|
+
interface RevokeKeyParams {
|
|
228
|
+
userId: number;
|
|
229
|
+
keyId: string;
|
|
230
|
+
reason: string;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Register a new public key for a user
|
|
234
|
+
*/
|
|
235
|
+
declare function registerPublicKeyService(params: RegisterPublicKeyParams): Promise<void>;
|
|
236
|
+
/**
|
|
237
|
+
* Rotate user's public key (revoke old, register new)
|
|
238
|
+
*/
|
|
239
|
+
declare function rotateKeyService(params: RotateKeyParams): Promise<RotateKeyResult>;
|
|
240
|
+
/**
|
|
241
|
+
* Revoke a user's public key
|
|
242
|
+
*/
|
|
243
|
+
declare function revokeKeyService(params: RevokeKeyParams): Promise<void>;
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* @spfn/auth - RBAC Type Definitions
|
|
247
|
+
*
|
|
248
|
+
* Type definitions for role and permission configuration
|
|
249
|
+
*/
|
|
250
|
+
/**
|
|
251
|
+
* Permission category enum values
|
|
252
|
+
* Single source of truth for permission categories
|
|
253
|
+
*/
|
|
254
|
+
declare const PERMISSION_CATEGORIES: readonly ["auth", "user", "rbac", "system", "custom"];
|
|
255
|
+
/**
|
|
256
|
+
* Permission category type derived from the const array
|
|
257
|
+
*/
|
|
258
|
+
type PermissionCategory = typeof PERMISSION_CATEGORIES[number];
|
|
259
|
+
interface RoleConfig {
|
|
260
|
+
name: string;
|
|
261
|
+
displayName: string;
|
|
262
|
+
description?: string;
|
|
263
|
+
priority?: number;
|
|
264
|
+
isSystem?: boolean;
|
|
265
|
+
isBuiltin?: boolean;
|
|
266
|
+
}
|
|
267
|
+
interface PermissionConfig {
|
|
268
|
+
name: string;
|
|
269
|
+
displayName: string;
|
|
270
|
+
description?: string;
|
|
271
|
+
category?: PermissionCategory;
|
|
272
|
+
isSystem?: boolean;
|
|
273
|
+
isBuiltin?: boolean;
|
|
274
|
+
}
|
|
275
|
+
interface AuthInitOptions {
|
|
276
|
+
/**
|
|
277
|
+
* Additional roles to create
|
|
278
|
+
* Built-in roles (user, admin, superadmin) are automatically included
|
|
279
|
+
*/
|
|
280
|
+
roles?: RoleConfig[];
|
|
281
|
+
/**
|
|
282
|
+
* Additional permissions to create
|
|
283
|
+
* Built-in permissions are automatically included
|
|
284
|
+
*/
|
|
285
|
+
permissions?: PermissionConfig[];
|
|
286
|
+
/**
|
|
287
|
+
* Role-Permission mappings
|
|
288
|
+
* Built-in mappings are automatically included
|
|
289
|
+
* You can extend built-in roles or define mappings for custom roles
|
|
290
|
+
*
|
|
291
|
+
* @example
|
|
292
|
+
* ```typescript
|
|
293
|
+
* {
|
|
294
|
+
* // Extend built-in admin role
|
|
295
|
+
* admin: ['project:create', 'project:delete'],
|
|
296
|
+
*
|
|
297
|
+
* // Define custom role permissions
|
|
298
|
+
* 'project-manager': ['project:create', 'task:assign'],
|
|
299
|
+
* }
|
|
300
|
+
* ```
|
|
301
|
+
*/
|
|
302
|
+
rolePermissions?: Record<string, string[]>;
|
|
303
|
+
/**
|
|
304
|
+
* Default role name for new users
|
|
305
|
+
* Must be a valid role name that exists after initialization
|
|
306
|
+
* @default 'user'
|
|
307
|
+
*/
|
|
308
|
+
defaultRole?: string;
|
|
309
|
+
/**
|
|
310
|
+
* Default session TTL (Time To Live)
|
|
311
|
+
*
|
|
312
|
+
* Supports:
|
|
313
|
+
* - Number: seconds (e.g., 2592000)
|
|
314
|
+
* - String: duration format ('30d', '12h', '45m', '3600s')
|
|
315
|
+
*
|
|
316
|
+
* Can be overridden at runtime with `remember` parameter.
|
|
317
|
+
*
|
|
318
|
+
* @default '7d' (7 days)
|
|
319
|
+
*
|
|
320
|
+
* @example
|
|
321
|
+
* ```typescript
|
|
322
|
+
* {
|
|
323
|
+
* sessionTtl: '30d', // 30 days
|
|
324
|
+
* }
|
|
325
|
+
* ```
|
|
326
|
+
*/
|
|
327
|
+
sessionTtl?: string | number;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* One-Time Token Service
|
|
332
|
+
*
|
|
333
|
+
* Issues and verifies one-time tokens for direct API access.
|
|
334
|
+
*/
|
|
335
|
+
interface IssueOneTimeTokenResult {
|
|
336
|
+
token: string;
|
|
337
|
+
expiresAt: string;
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Issue a one-time token for the authenticated user
|
|
341
|
+
*
|
|
342
|
+
* @param userId - Authenticated user's ID
|
|
343
|
+
* @returns Token string and ISO expiration timestamp
|
|
344
|
+
*/
|
|
345
|
+
declare function issueOneTimeTokenService(userId: string): Promise<IssueOneTimeTokenResult>;
|
|
346
|
+
/**
|
|
347
|
+
* Verify and consume a one-time token
|
|
348
|
+
*
|
|
349
|
+
* @param token - The one-time token to verify
|
|
350
|
+
* @returns userId if valid, null if invalid/expired/consumed
|
|
351
|
+
*/
|
|
352
|
+
declare function verifyOneTimeTokenService(token: string): Promise<string | null>;
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* OAuth Provider 추상화
|
|
356
|
+
*
|
|
357
|
+
* Provider별로 하드코딩된 분기를 제거하기 위한 공통 인터페이스와 registry.
|
|
358
|
+
* - 내장 provider(google)는 패키지 로드 시점에 자기 등록(dogfood)
|
|
359
|
+
* - 외부 패키지(@superself/auth 등)는 registerOAuthProvider()로 런타임 등록
|
|
360
|
+
*
|
|
361
|
+
* @spfn/auth는 토큰 issuer가 아니라 소비(client) 측이므로, 이 추상화는
|
|
362
|
+
* web 흐름("authorize URL 생성 → code 교환 → 사용자 정보 정규화")과
|
|
363
|
+
* native 흐름(네이티브/웹 SDK가 받은 id_token 직접 검증)을 다룬다.
|
|
364
|
+
*/
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Provider 사용자 정보를 공통 형태로 정규화한 신원
|
|
368
|
+
*
|
|
369
|
+
* provider별 응답 형태(snake_case 등)를 service에 노출하지 않기 위한 경계.
|
|
370
|
+
*/
|
|
371
|
+
interface NormalizedIdentity {
|
|
372
|
+
providerUserId: string;
|
|
373
|
+
email: string | null;
|
|
374
|
+
emailVerified: boolean;
|
|
375
|
+
name?: string;
|
|
376
|
+
avatar?: string;
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* 정규화된 OAuth 토큰 응답
|
|
380
|
+
*
|
|
381
|
+
* @property expiresIn - access token 만료까지 남은 초(seconds)
|
|
382
|
+
*/
|
|
383
|
+
interface OAuthTokens {
|
|
384
|
+
accessToken: string;
|
|
385
|
+
refreshToken?: string;
|
|
386
|
+
expiresIn: number;
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* 네이티브 id_token 검증 옵션
|
|
390
|
+
*/
|
|
391
|
+
interface NativeVerifyOptions {
|
|
392
|
+
/** 클라이언트가 생성한 raw nonce. provider별 규약(raw 또는 SHA-256 해시)으로 대조된다. */
|
|
393
|
+
nonce: string;
|
|
394
|
+
}
|
|
395
|
+
interface OAuthCodeExchangeOptions {
|
|
396
|
+
/** Provider가 callback에 돌려준 원본 state. 일부 provider는 token 교환에도 요구한다. */
|
|
397
|
+
state: string;
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* OAuth provider 구현 인터페이스
|
|
401
|
+
*
|
|
402
|
+
* google, superself 등 모든 provider가 이 형태를 만족해야 registry에 등록된다.
|
|
403
|
+
*/
|
|
404
|
+
interface OAuthProvider {
|
|
405
|
+
id: SocialProvider;
|
|
406
|
+
/**
|
|
407
|
+
* provider가 사용 가능한 상태인지(필수 env 등) 확인
|
|
408
|
+
*/
|
|
409
|
+
isEnabled(): boolean;
|
|
410
|
+
/**
|
|
411
|
+
* provider 로그인 페이지로 보낼 authorization URL 생성
|
|
412
|
+
*
|
|
413
|
+
* @param state - CSRF 방지용 암호화 state
|
|
414
|
+
* @param scopes - 요청할 scope (미지정 시 provider 기본값)
|
|
415
|
+
*/
|
|
416
|
+
getAuthUrl(state: string, scopes?: string[]): string;
|
|
417
|
+
/**
|
|
418
|
+
* authorization code를 토큰으로 교환
|
|
419
|
+
*/
|
|
420
|
+
exchangeCodeForTokens(code: string, options: OAuthCodeExchangeOptions): Promise<OAuthTokens>;
|
|
421
|
+
/**
|
|
422
|
+
* access token으로 사용자 정보를 조회하고 공통 형태로 정규화
|
|
423
|
+
*/
|
|
424
|
+
getUserInfo(accessToken: string): Promise<NormalizedIdentity>;
|
|
425
|
+
/**
|
|
426
|
+
* refresh token으로 access token 갱신 (provider가 지원하는 경우)
|
|
427
|
+
*
|
|
428
|
+
* 저장된 provider 토큰을 이후 API 호출에 재사용할 때 사용한다.
|
|
429
|
+
* 미구현 provider는 갱신 불가로 간주한다.
|
|
430
|
+
*/
|
|
431
|
+
refreshTokens?(refreshToken: string): Promise<OAuthTokens>;
|
|
432
|
+
/**
|
|
433
|
+
* 네이티브/웹 SDK가 받은 id_token을 직접 검증하고 신원을 정규화한다.
|
|
434
|
+
*
|
|
435
|
+
* authorization code 교환 없이 provider JWKS로 서명을 검증하므로 client secret이
|
|
436
|
+
* 필요 없다. native sign-in을 지원하는 provider만 구현한다(Apple은 web SDK 부재로
|
|
437
|
+
* Android·웹도 이 경로를 쓴다).
|
|
438
|
+
*/
|
|
439
|
+
verifyNativeIdToken?(idToken: string, options: NativeVerifyOptions): Promise<NormalizedIdentity>;
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* OAuth provider 등록 (public)
|
|
443
|
+
*
|
|
444
|
+
* 동일 id로 다시 등록하면 덮어쓴다(외부 패키지의 override 허용).
|
|
445
|
+
*/
|
|
446
|
+
declare function registerOAuthProvider(provider: OAuthProvider): void;
|
|
447
|
+
/**
|
|
448
|
+
* 등록된 provider 조회. 미등록이면 undefined.
|
|
449
|
+
*/
|
|
450
|
+
declare function getOAuthProvider(id: SocialProvider): OAuthProvider | undefined;
|
|
451
|
+
/**
|
|
452
|
+
* 등록된 모든 provider 목록
|
|
453
|
+
*/
|
|
454
|
+
declare function getRegisteredProviders(): OAuthProvider[];
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* @spfn/auth - OAuth Service
|
|
458
|
+
*
|
|
459
|
+
* OAuth 인증 비즈니스 로직
|
|
460
|
+
* - Google OAuth Authorization Code Flow
|
|
461
|
+
* - 소셜 계정 연결/생성
|
|
462
|
+
* - publicKey는 state에서 추출하여 등록
|
|
463
|
+
*/
|
|
464
|
+
|
|
465
|
+
interface OAuthStartParams {
|
|
466
|
+
provider: SocialProvider;
|
|
467
|
+
returnUrl: string;
|
|
468
|
+
publicKey: string;
|
|
469
|
+
keyId: string;
|
|
470
|
+
fingerprint: string;
|
|
471
|
+
algorithm: KeyAlgorithmType;
|
|
472
|
+
metadata?: Record<string, unknown>;
|
|
473
|
+
/** CSRF nonce bound into the state; the route sets the matching oauth_csrf cookie. */
|
|
474
|
+
nonce?: string;
|
|
475
|
+
}
|
|
476
|
+
interface OAuthStartResult {
|
|
477
|
+
authUrl: string;
|
|
478
|
+
}
|
|
479
|
+
interface OAuthCallbackParams {
|
|
480
|
+
provider: SocialProvider;
|
|
481
|
+
code: string;
|
|
482
|
+
state: string;
|
|
483
|
+
/**
|
|
484
|
+
* Value(s) of the oauth_csrf cookie from the callback request. One of them
|
|
485
|
+
* must equal the nonce bound into the (encrypted) state — otherwise the flow
|
|
486
|
+
* wasn't initiated by this browser (login CSRF). An array arises because the
|
|
487
|
+
* cookie name carries the PORT suffix of the process that set it (the Next.js
|
|
488
|
+
* web process), which differs from the API process in a split deployment, so
|
|
489
|
+
* the callback collects every spfn_oauth_csrf* candidate. Pass `undefined` or
|
|
490
|
+
* an empty array when absent; verification then fails closed.
|
|
491
|
+
*/
|
|
492
|
+
expectedNonce: string | string[] | undefined;
|
|
493
|
+
}
|
|
494
|
+
interface OAuthCallbackResult {
|
|
495
|
+
redirectUrl: string;
|
|
496
|
+
userId: string;
|
|
497
|
+
keyId: string;
|
|
498
|
+
isNewUser: boolean;
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* registry에서 provider를 찾아 사용 가능한지 검증 후 반환
|
|
502
|
+
*
|
|
503
|
+
* 미등록과 비활성을 구분해 디버깅 신호를 남긴다.
|
|
504
|
+
* 라우트 레이어에서도 재사용한다(중복 조회/non-null 단언 제거).
|
|
505
|
+
*/
|
|
506
|
+
declare function requireEnabledProvider(provider: SocialProvider): OAuthProvider;
|
|
507
|
+
/**
|
|
508
|
+
* OAuth 로그인 시작 - Provider 로그인 페이지로 리다이렉트할 URL 생성
|
|
509
|
+
*
|
|
510
|
+
* Next.js에서 키쌍을 생성한 후, publicKey를 state에 포함하여 호출
|
|
511
|
+
*/
|
|
512
|
+
declare function oauthStartService(params: OAuthStartParams): Promise<OAuthStartResult>;
|
|
513
|
+
/**
|
|
514
|
+
* OAuth 콜백 처리 - Code를 Token으로 교환하고 사용자 생성/연결
|
|
515
|
+
*
|
|
516
|
+
* state에서 publicKey를 추출하여 서버에 등록
|
|
517
|
+
* Next.js는 반환된 userId, keyId로 세션을 구성
|
|
518
|
+
*/
|
|
519
|
+
declare function oauthCallbackService(params: OAuthCallbackParams): Promise<OAuthCallbackResult>;
|
|
520
|
+
/**
|
|
521
|
+
* OAuth 에러 리다이렉트 URL 생성
|
|
522
|
+
*/
|
|
523
|
+
declare function buildOAuthErrorUrl(error: string): string;
|
|
524
|
+
/**
|
|
525
|
+
* OAuth provider가 등록되어 있고 활성화되어 있는지 확인
|
|
526
|
+
*/
|
|
527
|
+
declare function isOAuthProviderEnabled(provider: SocialProvider): boolean;
|
|
528
|
+
/**
|
|
529
|
+
* 활성화된 모든 OAuth provider 목록 (registry 기반)
|
|
530
|
+
*/
|
|
531
|
+
declare function getEnabledOAuthProviders(): SocialProvider[];
|
|
532
|
+
/**
|
|
533
|
+
* Google access token 조회 (만료 시 자동 리프레시)
|
|
534
|
+
*
|
|
535
|
+
* 저장된 토큰이 만료 임박(5분 이내) 또는 만료 상태이면
|
|
536
|
+
* refresh token으로 자동 갱신 후 DB 업데이트하여 유효한 토큰 반환.
|
|
537
|
+
*
|
|
538
|
+
* @param userId - 사용자 ID
|
|
539
|
+
* @returns 유효한 Google access token
|
|
540
|
+
*/
|
|
541
|
+
declare function getGoogleAccessToken(userId: number): Promise<string>;
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* @spfn/auth - Native Social Login Service
|
|
545
|
+
*
|
|
546
|
+
* 네이티브/웹 SDK가 받은 id_token을 JWKS로 검증하고, 검증된 신원에 클라이언트가 만든
|
|
547
|
+
* 공개키를 등록한다. 토큰은 발급하지 않는다 — 클라이언트가 등록한 키로 client token을
|
|
548
|
+
* 직접 서명해 Bearer로 사용한다(client-signs / server-verifies 모델).
|
|
549
|
+
*
|
|
550
|
+
* 흐름은 두 단계로 분리한다:
|
|
551
|
+
* 1) id_token 검증 — 외부 JWKS 네트워크 조회. DB 트랜잭션 밖에서 수행한다.
|
|
552
|
+
* 2) persist — 사용자 link/create + 공개키 등록을 한 트랜잭션으로. 이벤트는 커밋 후 발행.
|
|
553
|
+
*/
|
|
554
|
+
|
|
555
|
+
interface OAuthNativeParams {
|
|
556
|
+
provider: SocialProvider;
|
|
557
|
+
idToken: string;
|
|
558
|
+
nonce: string;
|
|
559
|
+
publicKey: string;
|
|
560
|
+
keyId: string;
|
|
561
|
+
fingerprint: string;
|
|
562
|
+
algorithm: KeyAlgorithmType;
|
|
563
|
+
/** Apple은 첫 로그인에만 이름을 별도로 주므로 클라이언트가 전달할 수 있다. */
|
|
564
|
+
profile?: {
|
|
565
|
+
name?: string;
|
|
566
|
+
};
|
|
567
|
+
metadata?: Record<string, unknown>;
|
|
568
|
+
}
|
|
569
|
+
interface OAuthNativeResult {
|
|
570
|
+
userId: string;
|
|
571
|
+
keyId: string;
|
|
572
|
+
isNewUser: boolean;
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* native id_token 로그인 처리
|
|
576
|
+
*
|
|
577
|
+
* @throws ValidationError provider가 native sign-in을 지원하지 않을 때
|
|
578
|
+
* @throws InvalidSocialTokenError id_token 검증 실패 시
|
|
579
|
+
*/
|
|
580
|
+
declare function oauthNativeService(params: OAuthNativeParams): Promise<OAuthNativeResult>;
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* @spfn/auth - Main Router
|
|
584
|
+
*
|
|
585
|
+
* Combines all auth-related routes into a single router
|
|
586
|
+
*/
|
|
587
|
+
/**
|
|
588
|
+
* Main auth router
|
|
589
|
+
* Exports all authentication-related routes
|
|
590
|
+
*
|
|
591
|
+
* Routes:
|
|
592
|
+
* - Auth: /_auth/codes, /_auth/login, /_auth/logout, etc.
|
|
593
|
+
* - OAuth: /_auth/oauth/google, /_auth/oauth/google/callback, etc.
|
|
594
|
+
* - Invitations: /_auth/invitations/*
|
|
595
|
+
* - Users: /_auth/users/*
|
|
596
|
+
* - Deletion: /_auth/deletion/request, /_auth/deletion/cancel
|
|
597
|
+
* - Admin: /_auth/admin/* (superadmin only)
|
|
598
|
+
*/
|
|
599
|
+
declare const mainAuthRouter: _spfn_core_route.Router<{
|
|
600
|
+
sendVerificationCode: _spfn_core_route.RouteDef<{
|
|
601
|
+
body: _sinclair_typebox.TObject<{
|
|
602
|
+
target: _sinclair_typebox.TString;
|
|
603
|
+
targetType: _sinclair_typebox.TUnion<[_sinclair_typebox.TLiteral<"email">, _sinclair_typebox.TLiteral<"phone">]>;
|
|
604
|
+
purpose: _sinclair_typebox.TUnion<[_sinclair_typebox.TLiteral<"registration">, _sinclair_typebox.TLiteral<"login">, _sinclair_typebox.TLiteral<"password_reset">, _sinclair_typebox.TLiteral<"email_change">, _sinclair_typebox.TLiteral<"phone_change">, _sinclair_typebox.TLiteral<"account_deletion">]>;
|
|
605
|
+
}>;
|
|
606
|
+
}, {}, SendVerificationCodeResult>;
|
|
607
|
+
verifyCode: _spfn_core_route.RouteDef<{
|
|
608
|
+
body: _sinclair_typebox.TObject<{
|
|
609
|
+
target: _sinclair_typebox.TString;
|
|
610
|
+
targetType: _sinclair_typebox.TUnion<[_sinclair_typebox.TLiteral<"email">, _sinclair_typebox.TLiteral<"phone">]>;
|
|
611
|
+
code: _sinclair_typebox.TString;
|
|
612
|
+
purpose: _sinclair_typebox.TUnion<[_sinclair_typebox.TLiteral<"registration">, _sinclair_typebox.TLiteral<"login">, _sinclair_typebox.TLiteral<"password_reset">, _sinclair_typebox.TLiteral<"email_change">, _sinclair_typebox.TLiteral<"phone_change">, _sinclair_typebox.TLiteral<"account_deletion">]>;
|
|
613
|
+
}>;
|
|
614
|
+
}, {}, {
|
|
615
|
+
valid: boolean;
|
|
616
|
+
verificationToken: string;
|
|
617
|
+
}>;
|
|
618
|
+
register: _spfn_core_route.RouteDef<{
|
|
619
|
+
body: _sinclair_typebox.TObject<{
|
|
620
|
+
email: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
621
|
+
phone: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
622
|
+
verificationToken: _sinclair_typebox.TString;
|
|
623
|
+
password: _sinclair_typebox.TString;
|
|
624
|
+
metadata: _sinclair_typebox.TOptional<_sinclair_typebox.TRecord<_sinclair_typebox.TString, _sinclair_typebox.TUnknown>>;
|
|
625
|
+
}>;
|
|
626
|
+
}, {
|
|
627
|
+
body: _sinclair_typebox.TObject<{
|
|
628
|
+
publicKey: _sinclair_typebox.TString;
|
|
629
|
+
keyId: _sinclair_typebox.TString;
|
|
630
|
+
fingerprint: _sinclair_typebox.TString;
|
|
631
|
+
algorithm: _sinclair_typebox.TUnion<_sinclair_typebox.TLiteral<"ES256" | "RS256">[]>;
|
|
632
|
+
}>;
|
|
633
|
+
}, RegisterResult>;
|
|
634
|
+
login: _spfn_core_route.RouteDef<{
|
|
635
|
+
body: _sinclair_typebox.TObject<{
|
|
636
|
+
email: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
637
|
+
phone: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
638
|
+
password: _sinclair_typebox.TString;
|
|
639
|
+
}>;
|
|
640
|
+
}, {
|
|
641
|
+
body: _sinclair_typebox.TObject<{
|
|
642
|
+
publicKey: _sinclair_typebox.TString;
|
|
643
|
+
keyId: _sinclair_typebox.TString;
|
|
644
|
+
fingerprint: _sinclair_typebox.TString;
|
|
645
|
+
algorithm: _sinclair_typebox.TUnion<_sinclair_typebox.TLiteral<"ES256" | "RS256">[]>;
|
|
646
|
+
oldKeyId: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
647
|
+
}>;
|
|
648
|
+
}, LoginResult>;
|
|
649
|
+
logout: _spfn_core_route.RouteDef<{}, {}, void>;
|
|
650
|
+
rotateKey: _spfn_core_route.RouteDef<{}, {
|
|
651
|
+
body: _sinclair_typebox.TObject<{
|
|
652
|
+
publicKey: _sinclair_typebox.TString;
|
|
653
|
+
keyId: _sinclair_typebox.TString;
|
|
654
|
+
fingerprint: _sinclair_typebox.TString;
|
|
655
|
+
algorithm: _sinclair_typebox.TUnion<_sinclair_typebox.TLiteral<"ES256" | "RS256">[]>;
|
|
656
|
+
}>;
|
|
657
|
+
}, RotateKeyResult>;
|
|
658
|
+
changePassword: _spfn_core_route.RouteDef<{
|
|
659
|
+
body: _sinclair_typebox.TObject<{
|
|
660
|
+
currentPassword: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
661
|
+
newPassword: _sinclair_typebox.TString;
|
|
662
|
+
}>;
|
|
663
|
+
}, {}, void>;
|
|
664
|
+
getAuthSession: _spfn_core_route.RouteDef<{}, {}, {
|
|
665
|
+
role: {
|
|
666
|
+
id: number;
|
|
667
|
+
name: string;
|
|
668
|
+
displayName: string;
|
|
669
|
+
priority: number;
|
|
670
|
+
};
|
|
671
|
+
permissions: {
|
|
672
|
+
id: number;
|
|
673
|
+
name: string;
|
|
674
|
+
displayName: string;
|
|
675
|
+
category: "auth" | "custom" | "user" | "rbac" | "system" | undefined;
|
|
676
|
+
}[];
|
|
677
|
+
userId: number;
|
|
678
|
+
publicId: string;
|
|
679
|
+
email: string | null;
|
|
680
|
+
emailVerified: boolean;
|
|
681
|
+
phoneVerified: boolean;
|
|
682
|
+
hasPassword: boolean;
|
|
683
|
+
}>;
|
|
684
|
+
issueOneTimeToken: _spfn_core_route.RouteDef<{}, {}, IssueOneTimeTokenResult>;
|
|
685
|
+
requestAccountDeletion: _spfn_core_route.RouteDef<{
|
|
686
|
+
body: _sinclair_typebox.TObject<{
|
|
687
|
+
password: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
688
|
+
verificationToken: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
689
|
+
reason: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
690
|
+
immediate: _sinclair_typebox.TOptional<_sinclair_typebox.TBoolean>;
|
|
691
|
+
}>;
|
|
692
|
+
}, {}, {
|
|
693
|
+
purgeScheduledAt: string;
|
|
694
|
+
}>;
|
|
695
|
+
cancelAccountDeletion: _spfn_core_route.RouteDef<{
|
|
696
|
+
body: _sinclair_typebox.TObject<{
|
|
697
|
+
email: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
698
|
+
phone: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
699
|
+
password: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
700
|
+
verificationToken: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
701
|
+
}>;
|
|
702
|
+
}, {}, void>;
|
|
703
|
+
oauthGoogleStart: _spfn_core_route.RouteDef<{
|
|
704
|
+
query: _sinclair_typebox.TObject<{
|
|
705
|
+
state: _sinclair_typebox.TString;
|
|
706
|
+
}>;
|
|
707
|
+
}, {}, Response>;
|
|
708
|
+
oauthGoogleCallback: _spfn_core_route.RouteDef<{
|
|
709
|
+
query: _sinclair_typebox.TObject<{
|
|
710
|
+
code: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
711
|
+
state: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
712
|
+
error: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
713
|
+
error_description: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
714
|
+
}>;
|
|
715
|
+
}, {}, Response>;
|
|
716
|
+
oauthStart: _spfn_core_route.RouteDef<{
|
|
717
|
+
body: _sinclair_typebox.TObject<{
|
|
718
|
+
provider: _sinclair_typebox.TUnion<_sinclair_typebox.TLiteral<"google" | "apple" | "github" | "kakao" | "naver" | "superself">[]>;
|
|
719
|
+
returnUrl: _sinclair_typebox.TString;
|
|
720
|
+
publicKey: _sinclair_typebox.TString;
|
|
721
|
+
keyId: _sinclair_typebox.TString;
|
|
722
|
+
fingerprint: _sinclair_typebox.TString;
|
|
723
|
+
algorithm: _sinclair_typebox.TUnion<_sinclair_typebox.TLiteral<"ES256" | "RS256">[]>;
|
|
724
|
+
metadata: _sinclair_typebox.TOptional<_sinclair_typebox.TRecord<_sinclair_typebox.TString, _sinclair_typebox.TUnknown>>;
|
|
725
|
+
}>;
|
|
726
|
+
}, {}, OAuthStartResult>;
|
|
727
|
+
oauthProviders: _spfn_core_route.RouteDef<{}, {}, {
|
|
728
|
+
providers: ("google" | "apple" | "github" | "kakao" | "naver" | "superself")[];
|
|
729
|
+
}>;
|
|
730
|
+
getGoogleOAuthUrl: _spfn_core_route.RouteDef<{
|
|
731
|
+
body: _sinclair_typebox.TObject<{
|
|
732
|
+
returnUrl: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
733
|
+
metadata: _sinclair_typebox.TOptional<_sinclair_typebox.TRecord<_sinclair_typebox.TString, _sinclair_typebox.TUnknown>>;
|
|
734
|
+
state: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
735
|
+
}>;
|
|
736
|
+
}, {}, {
|
|
737
|
+
authUrl: string;
|
|
738
|
+
}>;
|
|
739
|
+
oauthFinalize: _spfn_core_route.RouteDef<{
|
|
740
|
+
body: _sinclair_typebox.TObject<{
|
|
741
|
+
userId: _sinclair_typebox.TString;
|
|
742
|
+
keyId: _sinclair_typebox.TString;
|
|
743
|
+
returnUrl: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
744
|
+
}>;
|
|
745
|
+
}, {}, {
|
|
746
|
+
success: boolean;
|
|
747
|
+
userId: string;
|
|
748
|
+
keyId: string;
|
|
749
|
+
returnUrl: string;
|
|
750
|
+
}>;
|
|
751
|
+
oauthProviderStart: _spfn_core_route.RouteDef<{
|
|
752
|
+
params: _sinclair_typebox.TObject<{
|
|
753
|
+
provider: _sinclair_typebox.TUnion<_sinclair_typebox.TLiteral<"google" | "apple" | "github" | "kakao" | "naver" | "superself">[]>;
|
|
754
|
+
}>;
|
|
755
|
+
query: _sinclair_typebox.TObject<{
|
|
756
|
+
state: _sinclair_typebox.TString;
|
|
757
|
+
}>;
|
|
758
|
+
}, {}, Response>;
|
|
759
|
+
oauthProviderCallback: _spfn_core_route.RouteDef<{
|
|
760
|
+
params: _sinclair_typebox.TObject<{
|
|
761
|
+
provider: _sinclair_typebox.TUnion<_sinclair_typebox.TLiteral<"google" | "apple" | "github" | "kakao" | "naver" | "superself">[]>;
|
|
762
|
+
}>;
|
|
763
|
+
query: _sinclair_typebox.TObject<{
|
|
764
|
+
code: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
765
|
+
state: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
766
|
+
error: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
767
|
+
error_description: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
768
|
+
}>;
|
|
769
|
+
}, {}, Response>;
|
|
770
|
+
getProviderOAuthUrl: _spfn_core_route.RouteDef<{
|
|
771
|
+
params: _sinclair_typebox.TObject<{
|
|
772
|
+
provider: _sinclair_typebox.TUnion<_sinclair_typebox.TLiteral<"google" | "apple" | "github" | "kakao" | "naver" | "superself">[]>;
|
|
773
|
+
}>;
|
|
774
|
+
body: _sinclair_typebox.TObject<{
|
|
775
|
+
returnUrl: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
776
|
+
metadata: _sinclair_typebox.TOptional<_sinclair_typebox.TRecord<_sinclair_typebox.TString, _sinclair_typebox.TUnknown>>;
|
|
777
|
+
state: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
778
|
+
}>;
|
|
779
|
+
}, {}, {
|
|
780
|
+
authUrl: string;
|
|
781
|
+
}>;
|
|
782
|
+
oauthNative: _spfn_core_route.RouteDef<{
|
|
783
|
+
params: _sinclair_typebox.TObject<{
|
|
784
|
+
provider: _sinclair_typebox.TUnion<_sinclair_typebox.TLiteral<"google" | "apple" | "github" | "kakao" | "naver" | "superself">[]>;
|
|
785
|
+
}>;
|
|
786
|
+
body: _sinclair_typebox.TObject<{
|
|
787
|
+
idToken: _sinclair_typebox.TString;
|
|
788
|
+
nonce: _sinclair_typebox.TString;
|
|
789
|
+
publicKey: _sinclair_typebox.TString;
|
|
790
|
+
keyId: _sinclair_typebox.TString;
|
|
791
|
+
fingerprint: _sinclair_typebox.TString;
|
|
792
|
+
algorithm: _sinclair_typebox.TUnion<_sinclair_typebox.TLiteral<"ES256" | "RS256">[]>;
|
|
793
|
+
profile: _sinclair_typebox.TOptional<_sinclair_typebox.TObject<{
|
|
794
|
+
name: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
795
|
+
}>>;
|
|
796
|
+
metadata: _sinclair_typebox.TOptional<_sinclair_typebox.TRecord<_sinclair_typebox.TString, _sinclair_typebox.TUnknown>>;
|
|
797
|
+
}>;
|
|
798
|
+
}, {}, OAuthNativeResult>;
|
|
799
|
+
getInvitation: _spfn_core_route.RouteDef<{
|
|
800
|
+
params: _sinclair_typebox.TObject<{
|
|
801
|
+
token: _sinclair_typebox.TString;
|
|
802
|
+
}>;
|
|
803
|
+
}, {}, {
|
|
804
|
+
email: string;
|
|
805
|
+
role: string;
|
|
806
|
+
roleDisplayName: string;
|
|
807
|
+
invitedBy: string;
|
|
808
|
+
expiresAt: string;
|
|
809
|
+
metadata: Record<string, any> | undefined;
|
|
810
|
+
}>;
|
|
811
|
+
acceptInvitation: _spfn_core_route.RouteDef<{
|
|
812
|
+
body: _sinclair_typebox.TObject<{
|
|
813
|
+
token: _sinclair_typebox.TString;
|
|
814
|
+
password: _sinclair_typebox.TString;
|
|
815
|
+
}>;
|
|
816
|
+
}, {
|
|
817
|
+
body: _sinclair_typebox.TObject<{
|
|
818
|
+
publicKey: _sinclair_typebox.TString;
|
|
819
|
+
keyId: _sinclair_typebox.TString;
|
|
820
|
+
fingerprint: _sinclair_typebox.TString;
|
|
821
|
+
algorithm: _sinclair_typebox.TUnion<_sinclair_typebox.TLiteral<"ES256" | "RS256">[]>;
|
|
822
|
+
}>;
|
|
823
|
+
}, {
|
|
824
|
+
userId: number;
|
|
825
|
+
email: string;
|
|
826
|
+
role: string;
|
|
827
|
+
}>;
|
|
828
|
+
createInvitation: _spfn_core_route.RouteDef<{
|
|
829
|
+
body: _sinclair_typebox.TObject<{
|
|
830
|
+
email: _sinclair_typebox.TString;
|
|
831
|
+
roleId: _sinclair_typebox.TNumber;
|
|
832
|
+
expiresInDays: _sinclair_typebox.TOptional<_sinclair_typebox.TNumber>;
|
|
833
|
+
expiresAt: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
834
|
+
metadata: _sinclair_typebox.TOptional<_sinclair_typebox.TAny>;
|
|
835
|
+
}>;
|
|
836
|
+
}, {}, {
|
|
837
|
+
id: number;
|
|
838
|
+
email: string;
|
|
839
|
+
token: string;
|
|
840
|
+
roleId: number;
|
|
841
|
+
expiresAt: string;
|
|
842
|
+
invitationUrl: string;
|
|
843
|
+
}>;
|
|
844
|
+
listInvitations: _spfn_core_route.RouteDef<{
|
|
845
|
+
query: _sinclair_typebox.TObject<{
|
|
846
|
+
status: _sinclair_typebox.TOptional<_sinclair_typebox.TUnion<_sinclair_typebox.TLiteral<"pending" | "accepted" | "expired" | "cancelled">[]>>;
|
|
847
|
+
page: _sinclair_typebox.TOptional<_sinclair_typebox.TNumber>;
|
|
848
|
+
limit: _sinclair_typebox.TOptional<_sinclair_typebox.TNumber>;
|
|
849
|
+
}>;
|
|
850
|
+
}, {}, {
|
|
851
|
+
invitations: {
|
|
852
|
+
id: number;
|
|
853
|
+
email: string;
|
|
854
|
+
token: string;
|
|
855
|
+
roleId: number;
|
|
856
|
+
invitedBy: number;
|
|
857
|
+
status: "pending" | "accepted" | "expired" | "cancelled";
|
|
858
|
+
expiresAt: Date;
|
|
859
|
+
acceptedAt: Date | null;
|
|
860
|
+
cancelledAt: Date | null;
|
|
861
|
+
metadata: Record<string, any> | null;
|
|
862
|
+
createdAt: Date;
|
|
863
|
+
updatedAt: Date;
|
|
864
|
+
role: {
|
|
865
|
+
id: number;
|
|
866
|
+
name: string;
|
|
867
|
+
displayName: string;
|
|
868
|
+
};
|
|
869
|
+
inviter: {
|
|
870
|
+
id: number;
|
|
871
|
+
email: string | null;
|
|
872
|
+
};
|
|
873
|
+
}[];
|
|
874
|
+
total: number;
|
|
875
|
+
page: number;
|
|
876
|
+
limit: number;
|
|
877
|
+
totalPages: number;
|
|
878
|
+
}>;
|
|
879
|
+
cancelInvitation: _spfn_core_route.RouteDef<{
|
|
880
|
+
body: _sinclair_typebox.TObject<{
|
|
881
|
+
id: _sinclair_typebox.TNumber;
|
|
882
|
+
reason: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
883
|
+
}>;
|
|
884
|
+
}, {}, {
|
|
885
|
+
cancelledAt: string;
|
|
886
|
+
}>;
|
|
887
|
+
resendInvitation: _spfn_core_route.RouteDef<{
|
|
888
|
+
body: _sinclair_typebox.TObject<{
|
|
889
|
+
id: _sinclair_typebox.TNumber;
|
|
890
|
+
expiresInDays: _sinclair_typebox.TOptional<_sinclair_typebox.TNumber>;
|
|
891
|
+
}>;
|
|
892
|
+
}, {}, {
|
|
893
|
+
expiresAt: string;
|
|
894
|
+
}>;
|
|
895
|
+
deleteInvitation: _spfn_core_route.RouteDef<{
|
|
896
|
+
body: _sinclair_typebox.TObject<{
|
|
897
|
+
id: _sinclair_typebox.TNumber;
|
|
898
|
+
}>;
|
|
899
|
+
}, {}, void>;
|
|
900
|
+
getUserProfile: _spfn_core_route.RouteDef<{}, {}, UserProfile>;
|
|
901
|
+
updateUserProfile: _spfn_core_route.RouteDef<{
|
|
902
|
+
body: _sinclair_typebox.TObject<{
|
|
903
|
+
displayName: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
904
|
+
firstName: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
905
|
+
lastName: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
906
|
+
avatarUrl: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
907
|
+
bio: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
908
|
+
locale: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
909
|
+
timezone: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
910
|
+
dateOfBirth: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
911
|
+
gender: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
912
|
+
website: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
913
|
+
location: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
914
|
+
company: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
915
|
+
jobTitle: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
916
|
+
metadata: _sinclair_typebox.TOptional<_sinclair_typebox.TRecord<_sinclair_typebox.TString, _sinclair_typebox.TAny>>;
|
|
917
|
+
}>;
|
|
918
|
+
}, {}, ProfileInfo>;
|
|
919
|
+
checkUsername: _spfn_core_route.RouteDef<{
|
|
920
|
+
query: _sinclair_typebox.TObject<{
|
|
921
|
+
username: _sinclair_typebox.TString;
|
|
922
|
+
}>;
|
|
923
|
+
}, {}, {
|
|
924
|
+
available: boolean;
|
|
925
|
+
}>;
|
|
926
|
+
updateUsername: _spfn_core_route.RouteDef<{
|
|
927
|
+
body: _sinclair_typebox.TObject<{
|
|
928
|
+
username: _sinclair_typebox.TUnion<[_sinclair_typebox.TString, _sinclair_typebox.TNull]>;
|
|
929
|
+
}>;
|
|
930
|
+
}, {}, {
|
|
931
|
+
deletedAt: Date | null;
|
|
932
|
+
deletedBy: string | null;
|
|
933
|
+
createdAt: Date;
|
|
934
|
+
updatedAt: Date;
|
|
935
|
+
id: number;
|
|
936
|
+
publicId: string;
|
|
937
|
+
email: string | null;
|
|
938
|
+
phone: string | null;
|
|
939
|
+
username: string | null;
|
|
940
|
+
passwordHash: string | null;
|
|
941
|
+
passwordChangeRequired: boolean;
|
|
942
|
+
roleId: number;
|
|
943
|
+
status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
|
|
944
|
+
emailVerifiedAt: Date | null;
|
|
945
|
+
phoneVerifiedAt: Date | null;
|
|
946
|
+
lastLoginAt: Date | null;
|
|
947
|
+
}>;
|
|
948
|
+
updateLocale: _spfn_core_route.RouteDef<{
|
|
949
|
+
body: _sinclair_typebox.TObject<{
|
|
950
|
+
locale: _sinclair_typebox.TString;
|
|
951
|
+
}>;
|
|
952
|
+
}, {}, {
|
|
953
|
+
locale: string;
|
|
954
|
+
}>;
|
|
955
|
+
listRoles: _spfn_core_route.RouteDef<{
|
|
956
|
+
query: _sinclair_typebox.TObject<{
|
|
957
|
+
includeInactive: _sinclair_typebox.TOptional<_sinclair_typebox.TBoolean>;
|
|
958
|
+
}>;
|
|
959
|
+
}, {}, {
|
|
960
|
+
roles: {
|
|
961
|
+
description: string | null;
|
|
962
|
+
id: number;
|
|
963
|
+
name: string;
|
|
964
|
+
displayName: string;
|
|
965
|
+
isBuiltin: boolean;
|
|
966
|
+
isSystem: boolean;
|
|
967
|
+
isActive: boolean;
|
|
968
|
+
priority: number;
|
|
969
|
+
createdAt: Date;
|
|
970
|
+
updatedAt: Date;
|
|
971
|
+
}[];
|
|
972
|
+
}>;
|
|
973
|
+
createAdminRole: _spfn_core_route.RouteDef<{
|
|
974
|
+
body: _sinclair_typebox.TObject<{
|
|
975
|
+
name: _sinclair_typebox.TString;
|
|
976
|
+
displayName: _sinclair_typebox.TString;
|
|
977
|
+
description: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
978
|
+
priority: _sinclair_typebox.TOptional<_sinclair_typebox.TNumber>;
|
|
979
|
+
permissionIds: _sinclair_typebox.TOptional<_sinclair_typebox.TArray<_sinclair_typebox.TNumber>>;
|
|
980
|
+
}>;
|
|
981
|
+
}, {}, {
|
|
982
|
+
role: {
|
|
983
|
+
description: string | null;
|
|
984
|
+
id: number;
|
|
985
|
+
name: string;
|
|
986
|
+
displayName: string;
|
|
987
|
+
isBuiltin: boolean;
|
|
988
|
+
isSystem: boolean;
|
|
989
|
+
isActive: boolean;
|
|
990
|
+
priority: number;
|
|
991
|
+
createdAt: Date;
|
|
992
|
+
updatedAt: Date;
|
|
993
|
+
};
|
|
994
|
+
}>;
|
|
995
|
+
updateAdminRole: _spfn_core_route.RouteDef<{
|
|
996
|
+
params: _sinclair_typebox.TObject<{
|
|
997
|
+
id: _sinclair_typebox.TNumber;
|
|
998
|
+
}>;
|
|
999
|
+
body: _sinclair_typebox.TObject<{
|
|
1000
|
+
displayName: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
1001
|
+
description: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
|
|
1002
|
+
priority: _sinclair_typebox.TOptional<_sinclair_typebox.TNumber>;
|
|
1003
|
+
isActive: _sinclair_typebox.TOptional<_sinclair_typebox.TBoolean>;
|
|
1004
|
+
}>;
|
|
1005
|
+
}, {}, {
|
|
1006
|
+
role: {
|
|
1007
|
+
description: string | null;
|
|
1008
|
+
id: number;
|
|
1009
|
+
name: string;
|
|
1010
|
+
displayName: string;
|
|
1011
|
+
isBuiltin: boolean;
|
|
1012
|
+
isSystem: boolean;
|
|
1013
|
+
isActive: boolean;
|
|
1014
|
+
priority: number;
|
|
1015
|
+
createdAt: Date;
|
|
1016
|
+
updatedAt: Date;
|
|
1017
|
+
};
|
|
1018
|
+
}>;
|
|
1019
|
+
deleteAdminRole: _spfn_core_route.RouteDef<{
|
|
1020
|
+
params: _sinclair_typebox.TObject<{
|
|
1021
|
+
id: _sinclair_typebox.TNumber;
|
|
1022
|
+
}>;
|
|
1023
|
+
}, {}, void>;
|
|
1024
|
+
updateUserRole: _spfn_core_route.RouteDef<{
|
|
1025
|
+
params: _sinclair_typebox.TObject<{
|
|
1026
|
+
userId: _sinclair_typebox.TNumber;
|
|
1027
|
+
}>;
|
|
1028
|
+
body: _sinclair_typebox.TObject<{
|
|
1029
|
+
roleId: _sinclair_typebox.TNumber;
|
|
1030
|
+
}>;
|
|
1031
|
+
}, {}, {
|
|
1032
|
+
userId: number;
|
|
1033
|
+
roleId: number;
|
|
1034
|
+
}>;
|
|
1035
|
+
}>;
|
|
1036
|
+
|
|
1037
|
+
interface AuthContext {
|
|
1038
|
+
user: User;
|
|
1039
|
+
userId: string;
|
|
1040
|
+
keyId: string;
|
|
1041
|
+
role: string | null;
|
|
1042
|
+
locale: string;
|
|
1043
|
+
}
|
|
1044
|
+
declare module 'hono' {
|
|
1045
|
+
interface ContextVariableMap {
|
|
1046
|
+
auth: AuthContext;
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
/**
|
|
1050
|
+
* Authentication middleware
|
|
1051
|
+
*
|
|
1052
|
+
* Verifies client-signed JWT token using stored public key
|
|
1053
|
+
* Must be applied to routes that require authentication
|
|
1054
|
+
*
|
|
1055
|
+
* @example
|
|
1056
|
+
* ```typescript
|
|
1057
|
+
* // In server.config.ts
|
|
1058
|
+
* import { authenticate } from '@spfn/auth/server/middleware';
|
|
1059
|
+
*
|
|
1060
|
+
* export default defineServerConfig()
|
|
1061
|
+
* .middlewares([authenticate])
|
|
1062
|
+
* .routes(appRouter)
|
|
1063
|
+
* .build();
|
|
1064
|
+
*
|
|
1065
|
+
* // In route file - skip auth for public routes
|
|
1066
|
+
* export const publicRoute = route.get('/health')
|
|
1067
|
+
* .skip(['auth']) // Type-safe skip
|
|
1068
|
+
* .handler(async (c) => c.success({ status: 'ok' }));
|
|
1069
|
+
*
|
|
1070
|
+
* // Protected route - auth applied automatically
|
|
1071
|
+
* export const protectedRoute = route.get('/profile')
|
|
1072
|
+
* .handler(async (c) => {
|
|
1073
|
+
* const auth = c.get('auth'); // Get auth context
|
|
1074
|
+
* const { user, userId, keyId } = auth;
|
|
1075
|
+
* // Or access directly: c.get('auth').user
|
|
1076
|
+
* });
|
|
1077
|
+
* ```
|
|
1078
|
+
*/
|
|
1079
|
+
declare const authenticate: _spfn_core_route.NamedMiddleware<"auth">;
|
|
1080
|
+
/**
|
|
1081
|
+
* Optional authentication middleware
|
|
1082
|
+
*
|
|
1083
|
+
* Same as `authenticate` but does NOT reject unauthenticated requests.
|
|
1084
|
+
* - No token → continues without auth context
|
|
1085
|
+
* - Invalid token → continues without auth context
|
|
1086
|
+
* - Valid token → sets auth context normally
|
|
1087
|
+
*
|
|
1088
|
+
* Auto-skips the global 'auth' middleware when used at route level.
|
|
1089
|
+
*
|
|
1090
|
+
* @example
|
|
1091
|
+
* ```typescript
|
|
1092
|
+
* // No need for .skip(['auth']) — handled automatically
|
|
1093
|
+
* export const getProducts = route.get('/products')
|
|
1094
|
+
* .use([optionalAuth])
|
|
1095
|
+
* .handler(async (c) => {
|
|
1096
|
+
* const auth = getOptionalAuth(c); // AuthContext | undefined
|
|
1097
|
+
*
|
|
1098
|
+
* if (auth)
|
|
1099
|
+
* {
|
|
1100
|
+
* return getPersonalizedProducts(auth.userId);
|
|
1101
|
+
* }
|
|
1102
|
+
*
|
|
1103
|
+
* return getPublicProducts();
|
|
1104
|
+
* });
|
|
1105
|
+
* ```
|
|
1106
|
+
*/
|
|
1107
|
+
declare const optionalAuth: _spfn_core_route.NamedMiddleware<"optionalAuth">;
|
|
1108
|
+
|
|
1109
|
+
export { oauthNativeService as $, type AuthSession as A, rotateKeyService as B, type ChangePasswordParams as C, revokeKeyService as D, type RegisterPublicKeyParams as E, type RotateKeyParams as F, type RevokeKeyParams as G, issueOneTimeTokenService as H, type IssueOneTimeTokenResult as I, verifyOneTimeTokenService as J, oauthStartService as K, type LoginResult as L, oauthCallbackService as M, buildOAuthErrorUrl as N, type OAuthStartResult as O, type PermissionConfig as P, isOAuthProviderEnabled as Q, type RoleConfig as R, type SendVerificationCodeResult as S, requireEnabledProvider as T, type UserProfile as U, type VerificationTargetType as V, getEnabledOAuthProviders as W, getGoogleAccessToken as X, type OAuthStartParams as Y, type OAuthCallbackParams as Z, type OAuthCallbackResult as _, type RegisterResult as a, type OAuthNativeParams as a0, authenticate as a1, optionalAuth as a2, EmailSchema as a3, PhoneSchema as a4, PasswordSchema as a5, TargetTypeSchema as a6, VerificationPurposeSchema as a7, type NormalizedIdentity as a8, type OAuthTokens as a9, type NativeVerifyOptions as aa, type OAuthCodeExchangeOptions as ab, registerOAuthProvider as ac, getOAuthProvider as ad, getRegisteredProviders as ae, type RotateKeyResult as b, type OAuthNativeResult as c, type ProfileInfo as d, type VerificationPurpose as e, VERIFICATION_TARGET_TYPES as f, VERIFICATION_PURPOSES as g, PERMISSION_CATEGORIES as h, type PermissionCategory as i, type AuthInitOptions as j, type OAuthProvider as k, type AuthContext as l, mainAuthRouter as m, loginService as n, logoutService as o, changePasswordService as p, type RegisterParams as q, registerService as r, type LoginParams as s, type LogoutParams as t, sendVerificationCodeService as u, verifyCodeService as v, type SendVerificationCodeParams as w, type VerifyCodeParams as x, type VerifyCodeResult as y, registerPublicKeyService as z };
|