@spfn/auth 0.2.0-beta.10 → 0.2.0-beta.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +169 -168
- package/dist/{dto-CRlgoCP5.d.ts → authenticate-CU6_zQaa.d.ts} +182 -182
- package/dist/index.d.ts +143 -130
- package/dist/index.js +24 -1
- package/dist/index.js.map +1 -1
- package/dist/nextjs/server.js +0 -1
- package/dist/nextjs/server.js.map +1 -1
- package/dist/server.d.ts +3 -366
- package/dist/server.js +49 -466
- package/dist/server.js.map +1 -1
- package/package.json +4 -11
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @spfn/auth - Technical Documentation
|
|
2
2
|
|
|
3
|
-
**Version:** 0.
|
|
3
|
+
**Version:** 0.2.0-beta.11
|
|
4
4
|
**Status:** Alpha - Internal Development
|
|
5
5
|
|
|
6
6
|
> **Note:** This is a technical documentation for developers working on the @spfn/auth package.
|
|
@@ -12,11 +12,11 @@
|
|
|
12
12
|
|
|
13
13
|
- [Overview](#overview)
|
|
14
14
|
- [Installation](#installation)
|
|
15
|
+
- [Admin Account Setup](#6-admin-account-setup)
|
|
15
16
|
- [Architecture](#architecture)
|
|
16
17
|
- [Package Structure](#package-structure)
|
|
17
18
|
- [Module Exports](#module-exports)
|
|
18
19
|
- [Email & SMS Services](#email--sms-services)
|
|
19
|
-
- [Email Templates](#email-templates)
|
|
20
20
|
- [Server-Side API](#server-side-api)
|
|
21
21
|
- [Database Schema](#database-schema)
|
|
22
22
|
- [RBAC System](#rbac-system)
|
|
@@ -90,18 +90,26 @@ export const appRouter = defineRouter({
|
|
|
90
90
|
|
|
91
91
|
### 3. Configure Client (Next.js)
|
|
92
92
|
|
|
93
|
-
####
|
|
93
|
+
#### Option A: Use the built-in `authApi` (Recommended)
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
import { authApi } from '@spfn/auth';
|
|
97
|
+
|
|
98
|
+
// Type-safe API calls for auth routes
|
|
99
|
+
const session = await authApi.getAuthSession.call({});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
#### Option B: Register Error Registry in Custom API Client
|
|
94
103
|
|
|
95
104
|
```typescript
|
|
96
105
|
import { createApi } from '@spfn/core/nextjs';
|
|
97
106
|
import type { AppRouter } from '@/server/router';
|
|
98
|
-
import { appMetadata as authAppMetadata } from "@spfn/auth";
|
|
99
107
|
import { authErrorRegistry } from "@spfn/auth/errors";
|
|
100
108
|
import { appMetadata } from '@/server/router.metadata';
|
|
101
109
|
import { errorRegistry } from "@spfn/core/errors";
|
|
102
110
|
|
|
103
111
|
export const api = createApi<AppRouter>({
|
|
104
|
-
metadata:
|
|
112
|
+
metadata: appMetadata,
|
|
105
113
|
errorRegistry: errorRegistry.concat(authErrorRegistry),
|
|
106
114
|
});
|
|
107
115
|
```
|
|
@@ -144,6 +152,83 @@ pnpm spfn db generate
|
|
|
144
152
|
pnpm spfn db migrate
|
|
145
153
|
```
|
|
146
154
|
|
|
155
|
+
### 6. Admin Account Setup
|
|
156
|
+
|
|
157
|
+
Admin accounts are automatically created on server startup via `createAuthLifecycle()`.
|
|
158
|
+
Choose one of the following methods:
|
|
159
|
+
|
|
160
|
+
#### Method 1: JSON Format (Recommended)
|
|
161
|
+
|
|
162
|
+
Best for multiple accounts with full configuration:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
SPFN_AUTH_ADMIN_ACCOUNTS='[
|
|
166
|
+
{"email": "superadmin@example.com", "password": "secure-pass-1", "role": "superadmin"},
|
|
167
|
+
{"email": "admin@example.com", "password": "secure-pass-2", "role": "admin"},
|
|
168
|
+
{"email": "manager@example.com", "password": "secure-pass-3", "role": "user"}
|
|
169
|
+
]'
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**JSON Schema:**
|
|
173
|
+
```typescript
|
|
174
|
+
interface AdminAccountConfig {
|
|
175
|
+
email: string; // Required
|
|
176
|
+
password: string; // Required
|
|
177
|
+
role?: string; // Default: 'user' (options: 'user', 'admin', 'superadmin')
|
|
178
|
+
phone?: string; // Optional
|
|
179
|
+
passwordChangeRequired?: boolean; // Default: true
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
#### Method 2: CSV Format
|
|
184
|
+
|
|
185
|
+
For multiple accounts with simpler configuration:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
SPFN_AUTH_ADMIN_EMAILS=admin@example.com,manager@example.com
|
|
189
|
+
SPFN_AUTH_ADMIN_PASSWORDS=admin-pass,manager-pass
|
|
190
|
+
SPFN_AUTH_ADMIN_ROLES=superadmin,admin
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
#### Method 3: Single Account (Legacy)
|
|
194
|
+
|
|
195
|
+
Simplest format for a single superadmin:
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
SPFN_AUTH_ADMIN_EMAIL=admin@example.com
|
|
199
|
+
SPFN_AUTH_ADMIN_PASSWORD=secure-password
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
> **Note:** This method always creates a `superadmin` role account.
|
|
203
|
+
|
|
204
|
+
#### Default Behavior
|
|
205
|
+
|
|
206
|
+
All admin accounts created via environment variables have:
|
|
207
|
+
- `emailVerifiedAt`: Auto-verified (current timestamp)
|
|
208
|
+
- `passwordChangeRequired`: `true` (must change on first login)
|
|
209
|
+
- `status`: `active`
|
|
210
|
+
|
|
211
|
+
#### Programmatic Creation
|
|
212
|
+
|
|
213
|
+
You can also create admin accounts programmatically:
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
import { usersRepository, getRoleByName, hashPassword } from '@spfn/auth/server';
|
|
217
|
+
|
|
218
|
+
// After initializeAuth() has been called
|
|
219
|
+
const role = await getRoleByName('admin');
|
|
220
|
+
const passwordHash = await hashPassword('secure-password');
|
|
221
|
+
|
|
222
|
+
await usersRepository.create({
|
|
223
|
+
email: 'admin@example.com',
|
|
224
|
+
passwordHash,
|
|
225
|
+
roleId: role.id,
|
|
226
|
+
emailVerifiedAt: new Date(),
|
|
227
|
+
passwordChangeRequired: true,
|
|
228
|
+
status: 'active',
|
|
229
|
+
});
|
|
230
|
+
```
|
|
231
|
+
|
|
147
232
|
---
|
|
148
233
|
|
|
149
234
|
## Architecture
|
|
@@ -335,20 +420,15 @@ packages/auth/
|
|
|
335
420
|
|
|
336
421
|
### Common Module (`@spfn/auth`)
|
|
337
422
|
|
|
338
|
-
**
|
|
423
|
+
**API Client:**
|
|
339
424
|
```typescript
|
|
340
|
-
import {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
userPermissions,
|
|
348
|
-
userInvitations,
|
|
349
|
-
userSocialAccounts,
|
|
350
|
-
userProfiles
|
|
351
|
-
} from '@spfn/auth';
|
|
425
|
+
import { authApi } from '@spfn/auth';
|
|
426
|
+
|
|
427
|
+
// Type-safe API calls
|
|
428
|
+
const session = await authApi.getAuthSession.call({});
|
|
429
|
+
const result = await authApi.login.call({
|
|
430
|
+
body: { email, password, fingerprint, publicKey, keyId }
|
|
431
|
+
});
|
|
352
432
|
```
|
|
353
433
|
|
|
354
434
|
**Types:**
|
|
@@ -359,6 +439,9 @@ import type {
|
|
|
359
439
|
VerificationCode,
|
|
360
440
|
Role,
|
|
361
441
|
Permission,
|
|
442
|
+
AuthSession,
|
|
443
|
+
UserProfile,
|
|
444
|
+
ProfileInfo,
|
|
362
445
|
// ... etc
|
|
363
446
|
} from '@spfn/auth';
|
|
364
447
|
```
|
|
@@ -380,6 +463,34 @@ import type {
|
|
|
380
463
|
} from '@spfn/auth';
|
|
381
464
|
```
|
|
382
465
|
|
|
466
|
+
**Validation Patterns:**
|
|
467
|
+
```typescript
|
|
468
|
+
import {
|
|
469
|
+
UUID_PATTERN,
|
|
470
|
+
EMAIL_PATTERN,
|
|
471
|
+
BASE64_PATTERN,
|
|
472
|
+
FINGERPRINT_PATTERN,
|
|
473
|
+
PHONE_PATTERN,
|
|
474
|
+
} from '@spfn/auth';
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
**Route Map (for RPC Proxy):**
|
|
478
|
+
```typescript
|
|
479
|
+
import { authRouteMap } from '@spfn/auth';
|
|
480
|
+
|
|
481
|
+
// Use in Next.js RPC proxy (app/api/rpc/[routeName]/route.ts)
|
|
482
|
+
import '@spfn/auth/nextjs/api'; // Auto-register auth interceptors
|
|
483
|
+
import { routeMap } from '@/generated/route-map';
|
|
484
|
+
import { authRouteMap } from '@spfn/auth';
|
|
485
|
+
import { createRpcProxy } from '@spfn/core/nextjs/proxy';
|
|
486
|
+
|
|
487
|
+
export const { GET, POST } = createRpcProxy({
|
|
488
|
+
routeMap: { ...routeMap, ...authRouteMap }
|
|
489
|
+
});
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
> **Note:** Database entities (`users`, `userPublicKeys`, etc.) are exported from `@spfn/auth/server`, not the common module.
|
|
493
|
+
|
|
383
494
|
---
|
|
384
495
|
|
|
385
496
|
### Server Module (`@spfn/auth/server`)
|
|
@@ -456,22 +567,10 @@ import {
|
|
|
456
567
|
|
|
457
568
|
// Session
|
|
458
569
|
getAuthSessionService,
|
|
459
|
-
getUserProfileService,
|
|
460
570
|
|
|
461
|
-
//
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
// SMS
|
|
466
|
-
sendSMS,
|
|
467
|
-
registerSMSProvider,
|
|
468
|
-
|
|
469
|
-
// Email Templates
|
|
470
|
-
registerEmailTemplates,
|
|
471
|
-
getVerificationCodeTemplate,
|
|
472
|
-
getWelcomeTemplate,
|
|
473
|
-
getPasswordResetTemplate,
|
|
474
|
-
getInvitationTemplate,
|
|
571
|
+
// User Profile
|
|
572
|
+
getUserProfileService,
|
|
573
|
+
updateUserProfileService,
|
|
475
574
|
} from '@spfn/auth/server';
|
|
476
575
|
```
|
|
477
576
|
|
|
@@ -495,10 +594,11 @@ import {
|
|
|
495
594
|
import {
|
|
496
595
|
authenticate,
|
|
497
596
|
requirePermissions,
|
|
597
|
+
requireAnyPermission,
|
|
498
598
|
requireRole,
|
|
499
599
|
} from '@spfn/auth/server';
|
|
500
600
|
|
|
501
|
-
// Usage
|
|
601
|
+
// Usage - all permissions required
|
|
502
602
|
app.bind(
|
|
503
603
|
myContract,
|
|
504
604
|
[authenticate, requirePermissions('user:delete')],
|
|
@@ -506,6 +606,15 @@ app.bind(
|
|
|
506
606
|
// Handler
|
|
507
607
|
}
|
|
508
608
|
);
|
|
609
|
+
|
|
610
|
+
// Usage - any of the permissions
|
|
611
|
+
app.bind(
|
|
612
|
+
myContract,
|
|
613
|
+
[authenticate, requireAnyPermission('content:read', 'admin:access')],
|
|
614
|
+
async (c) => {
|
|
615
|
+
// User has either content:read OR admin:access
|
|
616
|
+
}
|
|
617
|
+
);
|
|
509
618
|
```
|
|
510
619
|
|
|
511
620
|
**Helpers:**
|
|
@@ -679,141 +788,24 @@ export default async function DashboardPage()
|
|
|
679
788
|
|
|
680
789
|
## Email & SMS Services
|
|
681
790
|
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
The email service uses AWS SES by default, with fallback to console logging in development.
|
|
685
|
-
|
|
686
|
-
**Send Email:**
|
|
687
|
-
```typescript
|
|
688
|
-
import { sendEmail } from '@spfn/auth/server';
|
|
689
|
-
|
|
690
|
-
await sendEmail({
|
|
691
|
-
to: 'user@example.com',
|
|
692
|
-
subject: 'Welcome!',
|
|
693
|
-
text: 'Plain text content',
|
|
694
|
-
html: '<h1>HTML content</h1>',
|
|
695
|
-
purpose: 'welcome', // for logging
|
|
696
|
-
});
|
|
697
|
-
```
|
|
698
|
-
|
|
699
|
-
**Custom Email Provider:**
|
|
700
|
-
```typescript
|
|
701
|
-
import { registerEmailProvider } from '@spfn/auth/server';
|
|
702
|
-
|
|
703
|
-
// Register SendGrid provider
|
|
704
|
-
registerEmailProvider({
|
|
705
|
-
name: 'sendgrid',
|
|
706
|
-
sendEmail: async ({ to, subject, text, html }) => {
|
|
707
|
-
// Your SendGrid implementation
|
|
708
|
-
return { success: true, messageId: '...' };
|
|
709
|
-
},
|
|
710
|
-
});
|
|
711
|
-
```
|
|
712
|
-
|
|
713
|
-
---
|
|
714
|
-
|
|
715
|
-
### SMS Service
|
|
716
|
-
|
|
717
|
-
The SMS service uses AWS SNS by default.
|
|
718
|
-
|
|
719
|
-
**Send SMS:**
|
|
720
|
-
```typescript
|
|
721
|
-
import { sendSMS } from '@spfn/auth/server';
|
|
722
|
-
|
|
723
|
-
await sendSMS({
|
|
724
|
-
phone: '+821012345678', // E.164 format
|
|
725
|
-
message: 'Your code is: 123456',
|
|
726
|
-
purpose: 'verification',
|
|
727
|
-
});
|
|
728
|
-
```
|
|
791
|
+
> **⚠️ DEPRECATED:** Email and SMS functionality has been moved to `@spfn/notification` package.
|
|
729
792
|
|
|
730
|
-
|
|
731
|
-
```typescript
|
|
732
|
-
import { registerSMSProvider } from '@spfn/auth/server';
|
|
733
|
-
|
|
734
|
-
// Register Twilio provider
|
|
735
|
-
registerSMSProvider({
|
|
736
|
-
name: 'twilio',
|
|
737
|
-
sendSMS: async ({ phone, message }) => {
|
|
738
|
-
// Your Twilio implementation
|
|
739
|
-
return { success: true, messageId: '...' };
|
|
740
|
-
},
|
|
741
|
-
});
|
|
742
|
-
```
|
|
793
|
+
### Migration Guide
|
|
743
794
|
|
|
744
|
-
---
|
|
745
|
-
|
|
746
|
-
## Email Templates
|
|
747
|
-
|
|
748
|
-
### Built-in Templates
|
|
749
|
-
|
|
750
|
-
| Template | Function | Purpose |
|
|
751
|
-
|----------|----------|---------|
|
|
752
|
-
| `verificationCode` | `getVerificationCodeTemplate` | Verification codes (registration, login, password reset) |
|
|
753
|
-
| `welcome` | `getWelcomeTemplate` | Welcome email after registration |
|
|
754
|
-
| `passwordReset` | `getPasswordResetTemplate` | Password reset link |
|
|
755
|
-
| `invitation` | `getInvitationTemplate` | User invitation |
|
|
756
|
-
|
|
757
|
-
**Usage:**
|
|
758
795
|
```typescript
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
const { subject, text, html } = getVerificationCodeTemplate({
|
|
762
|
-
code: '123456',
|
|
763
|
-
purpose: 'registration',
|
|
764
|
-
expiresInMinutes: 5,
|
|
765
|
-
appName: 'MyApp',
|
|
766
|
-
});
|
|
796
|
+
// Before (deprecated)
|
|
797
|
+
import { sendEmail, sendSMS } from '@spfn/auth/server';
|
|
767
798
|
|
|
768
|
-
|
|
799
|
+
// After (recommended)
|
|
800
|
+
import { sendEmail, sendSMS } from '@spfn/notification/server';
|
|
769
801
|
```
|
|
770
802
|
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
Register custom templates to override defaults with your brand design:
|
|
776
|
-
|
|
777
|
-
```typescript
|
|
778
|
-
import { registerEmailTemplates } from '@spfn/auth/server';
|
|
779
|
-
|
|
780
|
-
// Register at app initialization (e.g., server.config.ts)
|
|
781
|
-
registerEmailTemplates({
|
|
782
|
-
// Override verification code template
|
|
783
|
-
verificationCode: ({ code, purpose, expiresInMinutes, appName }) => ({
|
|
784
|
-
subject: `[${appName}] Your verification code`,
|
|
785
|
-
text: `Your code: ${code}\nExpires in ${expiresInMinutes} minutes.`,
|
|
786
|
-
html: `
|
|
787
|
-
<div style="font-family: Arial, sans-serif;">
|
|
788
|
-
<img src="https://myapp.com/logo.png" alt="Logo" />
|
|
789
|
-
<h1>Verification Code</h1>
|
|
790
|
-
<div style="font-size: 32px; font-weight: bold;">${code}</div>
|
|
791
|
-
<p>This code expires in ${expiresInMinutes} minutes.</p>
|
|
792
|
-
</div>
|
|
793
|
-
`,
|
|
794
|
-
}),
|
|
795
|
-
|
|
796
|
-
// Override invitation template
|
|
797
|
-
invitation: ({ inviteLink, inviterName, roleName, appName }) => ({
|
|
798
|
-
subject: `${inviterName} invited you to ${appName}`,
|
|
799
|
-
text: `Accept invitation: ${inviteLink}`,
|
|
800
|
-
html: `
|
|
801
|
-
<h1>You're Invited!</h1>
|
|
802
|
-
<p>${inviterName} invited you to join ${appName} as ${roleName}.</p>
|
|
803
|
-
<a href="${inviteLink}">Accept Invitation</a>
|
|
804
|
-
`,
|
|
805
|
-
}),
|
|
806
|
-
});
|
|
807
|
-
```
|
|
803
|
+
The `@spfn/notification` package provides:
|
|
804
|
+
- Multi-channel support (Email, SMS, Slack, Push)
|
|
805
|
+
- Template system with variable substitution
|
|
806
|
+
- Multiple provider support (AWS SES, SNS, SendGrid, Twilio, etc.)
|
|
808
807
|
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
| Template | Parameters |
|
|
812
|
-
|----------|------------|
|
|
813
|
-
| `verificationCode` | `code`, `purpose`, `expiresInMinutes?`, `appName?` |
|
|
814
|
-
| `welcome` | `email`, `appName?` |
|
|
815
|
-
| `passwordReset` | `resetLink`, `expiresInMinutes?`, `appName?` |
|
|
816
|
-
| `invitation` | `inviteLink`, `inviterName?`, `roleName?`, `appName?` |
|
|
808
|
+
For documentation, see `@spfn/notification` package README.
|
|
817
809
|
|
|
818
810
|
---
|
|
819
811
|
|
|
@@ -1167,7 +1159,7 @@ CREATE TABLE permissions (
|
|
|
1167
1159
|
|
|
1168
1160
|
**Built-in Permissions:**
|
|
1169
1161
|
- `auth:self:manage`
|
|
1170
|
-
- `user:read`, `user:write`, `user:delete`
|
|
1162
|
+
- `user:read`, `user:write`, `user:delete`, `user:invite`
|
|
1171
1163
|
- `rbac:role:manage`, `rbac:permission:manage`
|
|
1172
1164
|
|
|
1173
1165
|
---
|
|
@@ -1327,7 +1319,7 @@ await initializeAuth({
|
|
|
1327
1319
|
|
|
1328
1320
|
**Permissions:**
|
|
1329
1321
|
- `auth:self:manage` - Change password, rotate keys
|
|
1330
|
-
- `user:read`, `user:write`, `user:delete`
|
|
1322
|
+
- `user:read`, `user:write`, `user:delete`, `user:invite`
|
|
1331
1323
|
- `rbac:role:manage`, `rbac:permission:manage`
|
|
1332
1324
|
|
|
1333
1325
|
---
|
|
@@ -1335,7 +1327,7 @@ await initializeAuth({
|
|
|
1335
1327
|
### Middleware Usage
|
|
1336
1328
|
|
|
1337
1329
|
```typescript
|
|
1338
|
-
import { authenticate, requirePermissions, requireRole } from '@spfn/auth/server';
|
|
1330
|
+
import { authenticate, requirePermissions, requireAnyPermission, requireRole } from '@spfn/auth/server';
|
|
1339
1331
|
|
|
1340
1332
|
// Single permission
|
|
1341
1333
|
app.bind(
|
|
@@ -1355,6 +1347,15 @@ app.bind(
|
|
|
1355
1347
|
}
|
|
1356
1348
|
);
|
|
1357
1349
|
|
|
1350
|
+
// Any of the permissions (at least one required)
|
|
1351
|
+
app.bind(
|
|
1352
|
+
viewContentContract,
|
|
1353
|
+
[authenticate, requireAnyPermission('content:read', 'admin:access')],
|
|
1354
|
+
async (c) => {
|
|
1355
|
+
// User has either content:read OR admin:access
|
|
1356
|
+
}
|
|
1357
|
+
);
|
|
1358
|
+
|
|
1358
1359
|
// Role-based
|
|
1359
1360
|
app.bind(
|
|
1360
1361
|
adminDashboardContract,
|
|
@@ -1957,6 +1958,6 @@ MIT License - See LICENSE file for details.
|
|
|
1957
1958
|
|
|
1958
1959
|
---
|
|
1959
1960
|
|
|
1960
|
-
**Last Updated:**
|
|
1961
|
-
**Document Version:** 2.
|
|
1962
|
-
**Package Version:** 0.
|
|
1961
|
+
**Last Updated:** 2026-01-25
|
|
1962
|
+
**Document Version:** 2.3.0 (Technical Documentation)
|
|
1963
|
+
**Package Version:** 0.2.0-beta.11
|