@nik2208/node-auth 1.1.6 → 1.2.1
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 +289 -11
- package/dist/interfaces/user-store.interface.d.ts +5 -0
- package/dist/interfaces/user-store.interface.d.ts.map +1 -1
- package/dist/middleware/auth.middleware.d.ts.map +1 -1
- package/dist/middleware/auth.middleware.js +8 -0
- package/dist/middleware/auth.middleware.js.map +1 -1
- package/dist/models/auth-config.model.d.ts +41 -0
- package/dist/models/auth-config.model.d.ts.map +1 -1
- package/dist/models/user.model.d.ts +3 -2
- package/dist/models/user.model.d.ts.map +1 -1
- package/dist/router/admin.router.d.ts +8 -2
- package/dist/router/admin.router.d.ts.map +1 -1
- package/dist/router/admin.router.js +388 -28
- package/dist/router/admin.router.js.map +1 -1
- package/dist/router/auth.router.d.ts.map +1 -1
- package/dist/router/auth.router.js +169 -10
- package/dist/router/auth.router.js.map +1 -1
- package/dist/services/token.service.d.ts +1 -1
- package/dist/services/token.service.d.ts.map +1 -1
- package/dist/services/token.service.js +17 -3
- package/dist/services/token.service.js.map +1 -1
- package/dist/strategies/local/local.strategy.d.ts +1 -1
- package/dist/strategies/local/local.strategy.d.ts.map +1 -1
- package/dist/strategies/local/local.strategy.js +4 -1
- package/dist/strategies/local/local.strategy.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -286,12 +286,12 @@ When you mount `auth.router()`, the following endpoints are available:
|
|
|
286
286
|
| `POST` | `/auth/change-email/confirm` | Confirm email change with token |
|
|
287
287
|
| `POST` | `/auth/2fa/setup` | Get TOTP secret + QR code (protected) |
|
|
288
288
|
| `POST` | `/auth/2fa/verify-setup` | Verify TOTP code and enable 2FA (protected) |
|
|
289
|
-
| `POST` | `/auth/2fa/verify` | Complete 2FA login |
|
|
289
|
+
| `POST` | `/auth/2fa/verify` | Complete TOTP 2FA login |
|
|
290
290
|
| `POST` | `/auth/2fa/disable` | Disable 2FA (protected) |
|
|
291
|
-
| `POST` | `/auth/magic-link/send` | Send magic link
|
|
292
|
-
| `POST` | `/auth/magic-link/verify` | Verify magic link
|
|
293
|
-
| `POST` | `/auth/sms/send` | Send SMS
|
|
294
|
-
| `POST` | `/auth/sms/verify` | Verify SMS code |
|
|
291
|
+
| `POST` | `/auth/magic-link/send` | Send magic link — direct login (`mode='login'`, default) or 2FA challenge (`mode='2fa'`, requires `tempToken`) |
|
|
292
|
+
| `POST` | `/auth/magic-link/verify` | Verify magic link — direct login (`mode='login'`, default) or 2FA completion (`mode='2fa'`, requires `tempToken`) |
|
|
293
|
+
| `POST` | `/auth/sms/send` | Send SMS code — direct login (`mode='login'`, default, accepts `userId` **or** `email`) or 2FA challenge (`mode='2fa'`, requires `tempToken`) |
|
|
294
|
+
| `POST` | `/auth/sms/verify` | Verify SMS code — direct login (`mode='login'`, default) or 2FA completion (`mode='2fa'`, requires `tempToken`) |
|
|
295
295
|
| `GET` | `/auth/oauth/google` | Initiate Google OAuth |
|
|
296
296
|
| `GET` | `/auth/oauth/google/callback` | Google OAuth callback |
|
|
297
297
|
| `GET` | `/auth/oauth/github` | Initiate GitHub OAuth |
|
|
@@ -715,6 +715,271 @@ await fetch('/auth/change-email/confirm', {
|
|
|
715
715
|
});
|
|
716
716
|
```
|
|
717
717
|
|
|
718
|
+
## TOTP Two-Factor Authentication — Full UI Integration Guide
|
|
719
|
+
|
|
720
|
+
TOTP (Time-based One-Time Password) is the **Google Authenticator / Authy** style 2FA. The following is the complete flow from both the server and UI perspective.
|
|
721
|
+
|
|
722
|
+
### Prerequisites
|
|
723
|
+
|
|
724
|
+
The user must be logged in (have a valid `accessToken` cookie or Bearer token).
|
|
725
|
+
|
|
726
|
+
### Step 1 — Generate a secret and display the QR code
|
|
727
|
+
|
|
728
|
+
Call `POST /auth/2fa/setup` from your settings page. The response contains:
|
|
729
|
+
- `secret` — base32-encoded TOTP secret (store it temporarily in the UI, **never** in localStorage)
|
|
730
|
+
- `otpauthUrl` — the `otpauth://` URI (used to generate the QR code)
|
|
731
|
+
- `qrCode` — a `data:image/png;base64,...` data URL you can put directly into an `<img>` tag
|
|
732
|
+
|
|
733
|
+
```typescript
|
|
734
|
+
// Client-side (authenticated)
|
|
735
|
+
const res = await fetch('/auth/2fa/setup', {
|
|
736
|
+
method: 'POST',
|
|
737
|
+
credentials: 'include', // sends the accessToken cookie
|
|
738
|
+
});
|
|
739
|
+
const { secret, qrCode } = await res.json();
|
|
740
|
+
|
|
741
|
+
// Display in your UI
|
|
742
|
+
document.getElementById('qr-img').src = qrCode;
|
|
743
|
+
document.getElementById('secret-text').textContent = secret; // for manual entry
|
|
744
|
+
```
|
|
745
|
+
|
|
746
|
+
**UI tip:** Show both the QR code and the plain-text secret. Some users cannot scan QR codes (accessibility, older devices).
|
|
747
|
+
|
|
748
|
+
```html
|
|
749
|
+
<!-- Example setup UI -->
|
|
750
|
+
<div id="totp-setup">
|
|
751
|
+
<p>Scan this QR code with Google Authenticator, Authy, or any TOTP app:</p>
|
|
752
|
+
<img id="qr-img" alt="TOTP QR code" />
|
|
753
|
+
<p>Or enter this code manually: <code id="secret-text"></code></p>
|
|
754
|
+
|
|
755
|
+
<label>Enter the 6-digit code shown in the app to confirm:</label>
|
|
756
|
+
<input id="totp-input" type="text" maxlength="6" inputmode="numeric" autocomplete="one-time-code" />
|
|
757
|
+
<button onclick="verifySetup()">Enable 2FA</button>
|
|
758
|
+
</div>
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
### Step 2 — Verify the setup and persist the secret
|
|
762
|
+
|
|
763
|
+
The user enters the 6-digit code from their authenticator app. Call `POST /auth/2fa/verify-setup` with both the code **and** the secret returned from step 1.
|
|
764
|
+
|
|
765
|
+
```typescript
|
|
766
|
+
async function verifySetup() {
|
|
767
|
+
const code = document.getElementById('totp-input').value.trim();
|
|
768
|
+
const secret = document.getElementById('secret-text').textContent; // from step 1
|
|
769
|
+
|
|
770
|
+
const res = await fetch('/auth/2fa/verify-setup', {
|
|
771
|
+
method: 'POST',
|
|
772
|
+
credentials: 'include',
|
|
773
|
+
headers: { 'Content-Type': 'application/json' },
|
|
774
|
+
body: JSON.stringify({ token: code, secret }),
|
|
775
|
+
});
|
|
776
|
+
|
|
777
|
+
if (res.ok) {
|
|
778
|
+
// 2FA is now enabled — update UI, redirect to settings
|
|
779
|
+
alert('Two-factor authentication enabled!');
|
|
780
|
+
} else {
|
|
781
|
+
const { error } = await res.json();
|
|
782
|
+
alert('Invalid code: ' + error);
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
The server calls `updateTotpSecret(userId, secret)` which sets `isTotpEnabled = true` on the user.
|
|
788
|
+
|
|
789
|
+
### Step 3 — Login with TOTP
|
|
790
|
+
|
|
791
|
+
When a user with `isTotpEnabled = true` calls `POST /auth/login`, the server responds:
|
|
792
|
+
|
|
793
|
+
```json
|
|
794
|
+
{
|
|
795
|
+
"requiresTwoFactor": true,
|
|
796
|
+
"tempToken": "<short-lived JWT>",
|
|
797
|
+
"available2faMethods": ["totp", "sms", "magic-link"]
|
|
798
|
+
}
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
- `tempToken` expires in **5 minutes** — use it immediately.
|
|
802
|
+
- `available2faMethods` lists which 2FA channels are available to this specific user (see [Multi-channel 2FA](#multi-channel-2fa) below).
|
|
803
|
+
|
|
804
|
+
Show a code-entry UI and call `POST /auth/2fa/verify`:
|
|
805
|
+
|
|
806
|
+
```typescript
|
|
807
|
+
// After detecting requiresTwoFactor === true in the login response:
|
|
808
|
+
let tempToken = data.tempToken;
|
|
809
|
+
|
|
810
|
+
async function submit2fa() {
|
|
811
|
+
const totpCode = document.getElementById('totp-code-input').value.trim();
|
|
812
|
+
|
|
813
|
+
const res = await fetch('/auth/2fa/verify', {
|
|
814
|
+
method: 'POST',
|
|
815
|
+
headers: { 'Content-Type': 'application/json' },
|
|
816
|
+
body: JSON.stringify({ tempToken, totpCode }),
|
|
817
|
+
});
|
|
818
|
+
|
|
819
|
+
if (res.ok) {
|
|
820
|
+
// Full session tokens are now set as HttpOnly cookies
|
|
821
|
+
window.location.href = '/dashboard';
|
|
822
|
+
} else {
|
|
823
|
+
const { error } = await res.json();
|
|
824
|
+
alert('Invalid code: ' + error);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
```
|
|
828
|
+
|
|
829
|
+
```html
|
|
830
|
+
<!-- TOTP verification UI (shown after login step 1) -->
|
|
831
|
+
<div id="totp-verify">
|
|
832
|
+
<p>Enter the 6-digit code from your authenticator app:</p>
|
|
833
|
+
<input id="totp-code-input" type="text" maxlength="6" inputmode="numeric"
|
|
834
|
+
autocomplete="one-time-code" autofocus />
|
|
835
|
+
<button onclick="submit2fa()">Verify</button>
|
|
836
|
+
</div>
|
|
837
|
+
```
|
|
838
|
+
|
|
839
|
+
### Step 4 — Disable 2FA
|
|
840
|
+
|
|
841
|
+
Call `POST /auth/2fa/disable` (authenticated):
|
|
842
|
+
|
|
843
|
+
```typescript
|
|
844
|
+
await fetch('/auth/2fa/disable', {
|
|
845
|
+
method: 'POST',
|
|
846
|
+
credentials: 'include',
|
|
847
|
+
});
|
|
848
|
+
```
|
|
849
|
+
|
|
850
|
+
The server clears `totpSecret` and sets `isTotpEnabled = false`.
|
|
851
|
+
|
|
852
|
+
---
|
|
853
|
+
|
|
854
|
+
## Multi-Channel 2FA — SMS and Magic-Link as Second Factor
|
|
855
|
+
|
|
856
|
+
After a successful `POST /auth/login` that returns `requiresTwoFactor: true`, the response includes `available2faMethods` — an array listing which 2FA channels are configured for the user:
|
|
857
|
+
|
|
858
|
+
| Value | Requires |
|
|
859
|
+
|-------|----------|
|
|
860
|
+
| `'totp'` | User has `isTotpEnabled = true` and a stored `totpSecret` |
|
|
861
|
+
| `'sms'` | User has a stored `phoneNumber` **and** `config.sms` is configured |
|
|
862
|
+
| `'magic-link'` | `config.email.sendMagicLink` or `config.email.mailer` is configured |
|
|
863
|
+
|
|
864
|
+
Your UI can let the user pick their preferred channel:
|
|
865
|
+
|
|
866
|
+
```typescript
|
|
867
|
+
const loginRes = await fetch('/auth/login', { /* ... */ });
|
|
868
|
+
const { requiresTwoFactor, tempToken, available2faMethods } = await loginRes.json();
|
|
869
|
+
|
|
870
|
+
if (requiresTwoFactor) {
|
|
871
|
+
// Offer available channels to the user
|
|
872
|
+
show2faChannelPicker(available2faMethods, tempToken);
|
|
873
|
+
}
|
|
874
|
+
```
|
|
875
|
+
|
|
876
|
+
### 2FA via SMS
|
|
877
|
+
|
|
878
|
+
**Step A — Request the code:**
|
|
879
|
+
|
|
880
|
+
```typescript
|
|
881
|
+
await fetch('/auth/sms/send', {
|
|
882
|
+
method: 'POST',
|
|
883
|
+
headers: { 'Content-Type': 'application/json' },
|
|
884
|
+
body: JSON.stringify({ mode: '2fa', tempToken }),
|
|
885
|
+
});
|
|
886
|
+
// The server validates the tempToken, finds the user's stored phoneNumber, and sends an OTP.
|
|
887
|
+
```
|
|
888
|
+
|
|
889
|
+
**Step B — Submit the code:**
|
|
890
|
+
|
|
891
|
+
```typescript
|
|
892
|
+
const res = await fetch('/auth/sms/verify', {
|
|
893
|
+
method: 'POST',
|
|
894
|
+
headers: { 'Content-Type': 'application/json' },
|
|
895
|
+
body: JSON.stringify({ mode: '2fa', tempToken, code: userEnteredCode }),
|
|
896
|
+
});
|
|
897
|
+
// On success, full session tokens are issued via HttpOnly cookies.
|
|
898
|
+
```
|
|
899
|
+
|
|
900
|
+
### 2FA via Magic-Link
|
|
901
|
+
|
|
902
|
+
**Step A — Request the magic link:**
|
|
903
|
+
|
|
904
|
+
```typescript
|
|
905
|
+
await fetch('/auth/magic-link/send', {
|
|
906
|
+
method: 'POST',
|
|
907
|
+
headers: { 'Content-Type': 'application/json' },
|
|
908
|
+
body: JSON.stringify({ mode: '2fa', tempToken }),
|
|
909
|
+
});
|
|
910
|
+
// The server validates the tempToken, finds the user's email, and sends a magic link.
|
|
911
|
+
```
|
|
912
|
+
|
|
913
|
+
**Step B — Verify the link** (called from the link in the email):
|
|
914
|
+
|
|
915
|
+
```typescript
|
|
916
|
+
// Extract `token` from the link: /auth/magic-link/verify?token=...
|
|
917
|
+
const res = await fetch('/auth/magic-link/verify', {
|
|
918
|
+
method: 'POST',
|
|
919
|
+
headers: { 'Content-Type': 'application/json' },
|
|
920
|
+
body: JSON.stringify({ token: tokenFromLink, mode: '2fa', tempToken }),
|
|
921
|
+
});
|
|
922
|
+
// On success, full session tokens are issued via HttpOnly cookies.
|
|
923
|
+
```
|
|
924
|
+
|
|
925
|
+
> **Security note:** In `mode='2fa'`, both the magic-link token and the `tempToken` are validated. The magic link must belong to the same user identified by the `tempToken`, preventing account takeover even if a magic-link token is stolen.
|
|
926
|
+
|
|
927
|
+
---
|
|
928
|
+
|
|
929
|
+
## Direct Passwordless Login
|
|
930
|
+
|
|
931
|
+
### SMS Direct Login
|
|
932
|
+
|
|
933
|
+
Users can log in by phone without a password. You can identify the user by their stored `userId` **or** by the `email` associated with their account (the stored `phoneNumber` is used either way):
|
|
934
|
+
|
|
935
|
+
```typescript
|
|
936
|
+
// Option A — identify by userId
|
|
937
|
+
await fetch('/auth/sms/send', {
|
|
938
|
+
method: 'POST',
|
|
939
|
+
headers: { 'Content-Type': 'application/json' },
|
|
940
|
+
body: JSON.stringify({ userId: '123' }), // mode: 'login' is the default
|
|
941
|
+
});
|
|
942
|
+
|
|
943
|
+
// Option B — identify by email (user enters their email; the stored phone is used)
|
|
944
|
+
await fetch('/auth/sms/send', {
|
|
945
|
+
method: 'POST',
|
|
946
|
+
headers: { 'Content-Type': 'application/json' },
|
|
947
|
+
body: JSON.stringify({ email: 'user@example.com' }),
|
|
948
|
+
});
|
|
949
|
+
// If the email is not found the endpoint silently returns { success: true }
|
|
950
|
+
// to prevent user enumeration.
|
|
951
|
+
```
|
|
952
|
+
|
|
953
|
+
Then verify the code to get full session tokens:
|
|
954
|
+
|
|
955
|
+
```typescript
|
|
956
|
+
await fetch('/auth/sms/verify', {
|
|
957
|
+
method: 'POST',
|
|
958
|
+
headers: { 'Content-Type': 'application/json' },
|
|
959
|
+
body: JSON.stringify({ userId: '123', code: '123456' }),
|
|
960
|
+
});
|
|
961
|
+
```
|
|
962
|
+
|
|
963
|
+
### Magic-Link Direct Login
|
|
964
|
+
|
|
965
|
+
Magic-link direct login is unchanged — no `mode` parameter needed:
|
|
966
|
+
|
|
967
|
+
```typescript
|
|
968
|
+
// Send
|
|
969
|
+
await fetch('/auth/magic-link/send', {
|
|
970
|
+
method: 'POST',
|
|
971
|
+
headers: { 'Content-Type': 'application/json' },
|
|
972
|
+
body: JSON.stringify({ email: 'user@example.com' }),
|
|
973
|
+
});
|
|
974
|
+
|
|
975
|
+
// Verify (called when user clicks the link)
|
|
976
|
+
await fetch('/auth/magic-link/verify', {
|
|
977
|
+
method: 'POST',
|
|
978
|
+
headers: { 'Content-Type': 'application/json' },
|
|
979
|
+
body: JSON.stringify({ token: tokenFromLink }),
|
|
980
|
+
});
|
|
981
|
+
```
|
|
982
|
+
|
|
718
983
|
## Admin Panel
|
|
719
984
|
|
|
720
985
|
`createAdminRouter` mounts a **self-contained admin panel** — both the REST API and a vanilla-JS UI — at any path you choose. No build step, no external UI dependencies.
|
|
@@ -724,9 +989,10 @@ import { createAdminRouter } from 'node-auth';
|
|
|
724
989
|
|
|
725
990
|
app.use('/admin', createAdminRouter(userStore, {
|
|
726
991
|
adminSecret: process.env.ADMIN_SECRET!, // Bearer token required for all admin routes
|
|
727
|
-
sessionStore,
|
|
728
|
-
rbacStore,
|
|
729
|
-
tenantStore,
|
|
992
|
+
sessionStore, // optional — enables Sessions tab
|
|
993
|
+
rbacStore, // optional — enables Roles & Permissions tab + user-role assignment
|
|
994
|
+
tenantStore, // optional — enables Tenants tab + user-tenant membership
|
|
995
|
+
userMetadataStore, // optional — enables Metadata editor in the user panel
|
|
730
996
|
}));
|
|
731
997
|
```
|
|
732
998
|
|
|
@@ -734,12 +1000,16 @@ Open `http://localhost:3000/admin/` in your browser, enter the admin secret, and
|
|
|
734
1000
|
|
|
735
1001
|
| Tab | Requires | Features |
|
|
736
1002
|
|-----|----------|---------|
|
|
737
|
-
| **Users** | `IUserStore.listUsers` | Paginated user table, delete user |
|
|
1003
|
+
| **Users** | `IUserStore.listUsers` | Paginated user table, delete user, **Manage** panel per user |
|
|
738
1004
|
| **Sessions** | `ISessionStore.getAllSessions` | All active sessions, revoke by handle |
|
|
739
1005
|
| **Roles & Permissions** | `IRolesPermissionsStore.getAllRoles` | List roles with permissions, create/delete roles |
|
|
740
|
-
| **Tenants** | `ITenantStore.getAllTenants` | List tenants, create/delete tenants |
|
|
1006
|
+
| **Tenants** | `ITenantStore.getAllTenants` | List tenants, create/delete tenants, manage members |
|
|
1007
|
+
|
|
1008
|
+
The **Manage** panel (click the "Manage" button in the Users table) provides:
|
|
1009
|
+
- **Role assignment** — assign/remove roles when `rbacStore` is configured
|
|
1010
|
+
- **Metadata editor** — view and edit raw JSON metadata when `userMetadataStore` is configured
|
|
741
1011
|
|
|
742
|
-
Tabs that are not configured are hidden automatically.
|
|
1012
|
+
Tabs and features that are not configured are hidden automatically.
|
|
743
1013
|
|
|
744
1014
|
### Admin REST API
|
|
745
1015
|
|
|
@@ -751,6 +1021,11 @@ All admin API endpoints require `Authorization: Bearer <adminSecret>`.
|
|
|
751
1021
|
| `GET` | `/admin/api/users` | List users (`?limit=&offset=`) |
|
|
752
1022
|
| `GET` | `/admin/api/users/:id` | Get single user |
|
|
753
1023
|
| `DELETE` | `/admin/api/users/:id` | Delete user (requires `IUserStore.deleteUser`) |
|
|
1024
|
+
| `GET` | `/admin/api/users/:id/roles` | List roles assigned to a user |
|
|
1025
|
+
| `POST` | `/admin/api/users/:id/roles` | Assign a role to a user (`{ role, tenantId? }`) |
|
|
1026
|
+
| `DELETE` | `/admin/api/users/:id/roles/:role` | Remove a role from a user |
|
|
1027
|
+
| `GET` | `/admin/api/users/:id/metadata` | Get user metadata |
|
|
1028
|
+
| `PUT` | `/admin/api/users/:id/metadata` | Replace user metadata (full JSON body) |
|
|
754
1029
|
| `GET` | `/admin/api/sessions` | List all sessions (`?limit=&offset=`) |
|
|
755
1030
|
| `DELETE` | `/admin/api/sessions/:handle` | Revoke a session |
|
|
756
1031
|
| `GET` | `/admin/api/roles` | List all roles with permissions |
|
|
@@ -759,6 +1034,9 @@ All admin API endpoints require `Authorization: Bearer <adminSecret>`.
|
|
|
759
1034
|
| `GET` | `/admin/api/tenants` | List all tenants |
|
|
760
1035
|
| `POST` | `/admin/api/tenants` | Create a tenant |
|
|
761
1036
|
| `DELETE` | `/admin/api/tenants/:id` | Delete a tenant |
|
|
1037
|
+
| `GET` | `/admin/api/tenants/:id/users` | List user IDs belonging to a tenant |
|
|
1038
|
+
| `POST` | `/admin/api/tenants/:id/users` | Add a user to a tenant (`{ userId }`) |
|
|
1039
|
+
| `DELETE` | `/admin/api/tenants/:id/users/:userId` | Remove a user from a tenant |
|
|
762
1040
|
|
|
763
1041
|
> **Security note:** Mount the admin router behind a VPN or IP allow-list in production. The `adminSecret` is a single shared token — treat it like a root password.
|
|
764
1042
|
|
|
@@ -52,6 +52,11 @@ export interface IUserStore<U extends BaseUser = BaseUser> {
|
|
|
52
52
|
* Required to support POST /auth/change-email/confirm.
|
|
53
53
|
*/
|
|
54
54
|
findByEmailChangeToken?(token: string): Promise<U | null>;
|
|
55
|
+
/**
|
|
56
|
+
* Set or clear the "2FA required" flag for a single user.
|
|
57
|
+
* Required to support `POST /admin/api/2fa-policy` (batch 2FA enforcement).
|
|
58
|
+
*/
|
|
59
|
+
updateRequire2FA?(userId: string, required: boolean): Promise<void>;
|
|
55
60
|
/**
|
|
56
61
|
* Return a paginated list of users.
|
|
57
62
|
* Used by the optional admin router to display the users table.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-store.interface.d.ts","sourceRoot":"","sources":["../../src/interfaces/user-store.interface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEhD,MAAM,WAAW,UAAU,CAAC,CAAC,SAAS,QAAQ,GAAG,QAAQ;IACvD,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAC9C,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IACxC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACrC,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,IAAI,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7F,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,IAAI,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3F,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtE,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,IAAI,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/F,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,IAAI,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvF;;;OAGG;IACH,gBAAgB,CAAC,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAEpD;;;OAGG;IACH,oBAAoB,CAAC,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAIxD;;;OAGG;IACH,4BAA4B,CAAC,CAC3B,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,MAAM,EAAE,IAAI,GAAG,IAAI,GAClB,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB;;;OAGG;IACH,mBAAmB,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzE;;;OAGG;IACH,4BAA4B,CAAC,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAIhE;;;;;OAKG;IACH,sBAAsB,CAAC,CACrB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,MAAM,EAAE,IAAI,GAAG,IAAI,GAClB,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB;;;;OAIG;IACH,WAAW,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE9D;;;OAGG;IACH,sBAAsB,CAAC,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"user-store.interface.d.ts","sourceRoot":"","sources":["../../src/interfaces/user-store.interface.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.middleware.d.ts","sourceRoot":"","sources":["../../src/middleware/auth.middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmC,cAAc,EAAE,MAAM,SAAS,CAAC;AAC1E,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAG3D,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,OAAO,CAAC;QAChB,UAAU,OAAO;YACf,IAAI,CAAC,EAAE,kBAAkB,CAAC;SAC3B;KACF;CACF;AAID,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,UAAU,GAAG,cAAc,
|
|
1
|
+
{"version":3,"file":"auth.middleware.d.ts","sourceRoot":"","sources":["../../src/middleware/auth.middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmC,cAAc,EAAE,MAAM,SAAS,CAAC;AAC1E,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAG3D,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,OAAO,CAAC;QAChB,UAAU,OAAO;YACf,IAAI,CAAC,EAAE,kBAAkB,CAAC;SAC3B;KACF;CACF;AAID,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,UAAU,GAAG,cAAc,CAuBvE"}
|
|
@@ -10,6 +10,14 @@ function createAuthMiddleware(config) {
|
|
|
10
10
|
res.status(403).json({ error: 'No access token provided' });
|
|
11
11
|
return;
|
|
12
12
|
}
|
|
13
|
+
if (config.csrf?.enabled) {
|
|
14
|
+
const csrfCookie = tokenService.extractTokenFromCookie(req, 'csrf-token');
|
|
15
|
+
const csrfHeader = req.headers['x-csrf-token'];
|
|
16
|
+
if (!csrfCookie || !csrfHeader || csrfCookie !== csrfHeader) {
|
|
17
|
+
res.status(403).json({ error: 'CSRF token validation failed', code: 'CSRF_INVALID' });
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
13
21
|
try {
|
|
14
22
|
const payload = tokenService.verifyAccessToken(token, config);
|
|
15
23
|
req.user = payload;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.middleware.js","sourceRoot":"","sources":["../../src/middleware/auth.middleware.ts"],"names":[],"mappings":";;AAeA,
|
|
1
|
+
{"version":3,"file":"auth.middleware.js","sourceRoot":"","sources":["../../src/middleware/auth.middleware.ts"],"names":[],"mappings":";;AAeA,oDAuBC;AAnCD,6DAAyD;AAUzD,MAAM,YAAY,GAAG,IAAI,4BAAY,EAAE,CAAC;AAExC,SAAgB,oBAAoB,CAAC,MAAkB;IACrD,OAAO,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAQ,EAAE;QAC/D,MAAM,KAAK,GAAG,YAAY,CAAC,sBAAsB,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;QACtE,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,0BAA0B,EAAE,CAAC,CAAC;YAC5D,OAAO;QACT,CAAC;QACD,IAAI,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC;YACzB,MAAM,UAAU,GAAG,YAAY,CAAC,sBAAsB,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;YAC1E,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,cAAc,CAAuB,CAAC;YACrE,IAAI,CAAC,UAAU,IAAI,CAAC,UAAU,IAAI,UAAU,KAAK,UAAU,EAAE,CAAC;gBAC5D,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,8BAA8B,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC;gBACtF,OAAO;YACT,CAAC;QACH,CAAC;QACD,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,YAAY,CAAC,iBAAiB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;YAC9D,GAAG,CAAC,IAAI,GAAG,OAAO,CAAC;YACnB,IAAI,EAAE,CAAC;QACT,CAAC;QAAC,MAAM,CAAC;YACP,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,iCAAiC,EAAE,CAAC,CAAC;QACrE,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -41,6 +41,30 @@ export interface AuthConfig {
|
|
|
41
41
|
secure?: boolean;
|
|
42
42
|
sameSite?: 'strict' | 'lax' | 'none';
|
|
43
43
|
domain?: string;
|
|
44
|
+
/**
|
|
45
|
+
* Path for the refresh-token cookie.
|
|
46
|
+
* Defaults to `'/'` so the cookie is sent on every request and the
|
|
47
|
+
* router can be mounted at any prefix.
|
|
48
|
+
* For extra security you can restrict it to your refresh endpoint path
|
|
49
|
+
* (e.g. `'/auth/refresh'`).
|
|
50
|
+
*/
|
|
51
|
+
refreshTokenPath?: string;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Anti-CSRF protection using the double-submit cookie pattern.
|
|
55
|
+
*
|
|
56
|
+
* When enabled the library sets a non-HttpOnly `csrf-token` cookie alongside
|
|
57
|
+
* the JWT cookies. Client-side JavaScript must read this cookie and include
|
|
58
|
+
* its value in the `X-CSRF-Token` header on every authenticated request.
|
|
59
|
+
* The `createAuthMiddleware` middleware will then verify the header matches
|
|
60
|
+
* the cookie.
|
|
61
|
+
*
|
|
62
|
+
* Recommended when `cookieOptions.sameSite` is `'none'` or when you need
|
|
63
|
+
* defence-in-depth beyond `sameSite: 'lax'`.
|
|
64
|
+
*/
|
|
65
|
+
csrf?: {
|
|
66
|
+
/** @default false */
|
|
67
|
+
enabled?: boolean;
|
|
44
68
|
};
|
|
45
69
|
bcryptSaltRounds?: number;
|
|
46
70
|
sms?: {
|
|
@@ -106,6 +130,23 @@ export interface AuthConfig {
|
|
|
106
130
|
twoFactor?: {
|
|
107
131
|
appName?: string;
|
|
108
132
|
};
|
|
133
|
+
/**
|
|
134
|
+
* When `true`, the local (email+password) login endpoint will reject users
|
|
135
|
+
* whose email address has not yet been verified (`isEmailVerified !== true`).
|
|
136
|
+
*
|
|
137
|
+
* **Default:** `false` — email verification is optional by default.
|
|
138
|
+
*
|
|
139
|
+
* If you enable this flag you must also configure the email-verification flow
|
|
140
|
+
* so that users can actually verify their address:
|
|
141
|
+
* - configure `email.sendVerificationEmail` (or `email.mailer`) so the
|
|
142
|
+
* library can send verification emails.
|
|
143
|
+
* - expose the `POST /auth/send-verification-email` and
|
|
144
|
+
* `GET /auth/verify-email` endpoints from `createAuthRouter`.
|
|
145
|
+
*
|
|
146
|
+
* When a user tries to log in with an unverified email the server responds
|
|
147
|
+
* with HTTP **403** and error code `EMAIL_NOT_VERIFIED`.
|
|
148
|
+
*/
|
|
149
|
+
requireEmailVerification?: boolean;
|
|
109
150
|
/**
|
|
110
151
|
* Optional callback to inject custom claims into both the access and refresh JWTs.
|
|
111
152
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-config.model.d.ts","sourceRoot":"","sources":["../../src/models/auth-config.model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAExC;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,YAAY;IAC3B,6EAA6E;IAC7E,QAAQ,EAAE,MAAM,CAAC;IACjB,8CAA8C;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,4BAA4B;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,sCAAsC;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kCAAkC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,WAAW,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,UAAU;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,aAAa,CAAC,EAAE;QACd,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,QAAQ,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;QACrC,MAAM,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"auth-config.model.d.ts","sourceRoot":"","sources":["../../src/models/auth-config.model.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,14 +8,15 @@ export interface BaseUser {
|
|
|
8
8
|
resetToken?: string | null;
|
|
9
9
|
resetTokenExpiry?: Date | null;
|
|
10
10
|
totpSecret?: string | null;
|
|
11
|
+
isEmailVerified?: boolean;
|
|
11
12
|
isTotpEnabled?: boolean;
|
|
12
13
|
magicLinkToken?: string | null;
|
|
13
14
|
magicLinkTokenExpiry?: Date | null;
|
|
14
15
|
smsCode?: string | null;
|
|
15
16
|
smsCodeExpiry?: Date | null;
|
|
16
17
|
phoneNumber?: string | null;
|
|
17
|
-
/**
|
|
18
|
-
|
|
18
|
+
/** When `true` this user must have 2FA active to complete login. */
|
|
19
|
+
require2FA?: boolean;
|
|
19
20
|
/** Token sent in the email-verification link. */
|
|
20
21
|
emailVerificationToken?: string | null;
|
|
21
22
|
/** Expiry for the email-verification token. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user.model.d.ts","sourceRoot":"","sources":["../../src/models/user.model.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,kBAAkB,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACjC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,gBAAgB,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAC/B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,oBAAoB,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACnC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,aAAa,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,
|
|
1
|
+
{"version":3,"file":"user.model.d.ts","sourceRoot":"","sources":["../../src/models/user.model.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,kBAAkB,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACjC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,gBAAgB,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAC/B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,oBAAoB,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACnC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,aAAa,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,oEAAoE;IACpE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,iDAAiD;IACjD,sBAAsB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,+CAA+C;IAC/C,4BAA4B,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAC3C,wEAAwE;IACxE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,8DAA8D;IAC9D,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,yCAAyC;IACzC,sBAAsB,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CACtC"}
|
|
@@ -3,6 +3,7 @@ import { IUserStore } from '../interfaces/user-store.interface';
|
|
|
3
3
|
import { ISessionStore } from '../interfaces/session-store.interface';
|
|
4
4
|
import { IRolesPermissionsStore } from '../interfaces/roles-permissions-store.interface';
|
|
5
5
|
import { ITenantStore } from '../interfaces/tenant-store.interface';
|
|
6
|
+
import { IUserMetadataStore } from '../interfaces/user-metadata-store.interface';
|
|
6
7
|
export interface AdminOptions {
|
|
7
8
|
/**
|
|
8
9
|
* Secret token required to access all admin endpoints.
|
|
@@ -12,10 +13,15 @@ export interface AdminOptions {
|
|
|
12
13
|
adminSecret: string;
|
|
13
14
|
/** Optional session store — enables the Sessions tab in the admin UI. */
|
|
14
15
|
sessionStore?: ISessionStore;
|
|
15
|
-
/** Optional RBAC store — enables the Roles & Permissions tab. */
|
|
16
|
+
/** Optional RBAC store — enables the Roles & Permissions tab and user-role assignment. */
|
|
16
17
|
rbacStore?: IRolesPermissionsStore;
|
|
17
|
-
/** Optional tenant store — enables the Tenants tab. */
|
|
18
|
+
/** Optional tenant store — enables the Tenants tab and user-tenant assignment. */
|
|
18
19
|
tenantStore?: ITenantStore;
|
|
20
|
+
/**
|
|
21
|
+
* Optional user-metadata store — enables the Metadata section in the user detail
|
|
22
|
+
* panel (view and edit arbitrary per-user key/value data).
|
|
23
|
+
*/
|
|
24
|
+
userMetadataStore?: IUserMetadataStore;
|
|
19
25
|
}
|
|
20
26
|
export declare function createAdminRouter(userStore: IUserStore, options: AdminOptions): Router;
|
|
21
27
|
//# sourceMappingURL=admin.router.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin.router.d.ts","sourceRoot":"","sources":["../../src/router/admin.router.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAqC,MAAM,SAAS,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,uCAAuC,CAAC;AACtE,OAAO,EAAE,sBAAsB,EAAE,MAAM,iDAAiD,CAAC;AACzF,OAAO,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;
|
|
1
|
+
{"version":3,"file":"admin.router.d.ts","sourceRoot":"","sources":["../../src/router/admin.router.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAqC,MAAM,SAAS,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,uCAAuC,CAAC;AACtE,OAAO,EAAE,sBAAsB,EAAE,MAAM,iDAAiD,CAAC;AACzF,OAAO,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,6CAA6C,CAAC;AAEjF,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,yEAAyE;IACzE,YAAY,CAAC,EAAE,aAAa,CAAC;IAC7B,0FAA0F;IAC1F,SAAS,CAAC,EAAE,sBAAsB,CAAC;IACnC,kFAAkF;IAClF,WAAW,CAAC,EAAE,YAAY,CAAC;IAC3B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,kBAAkB,CAAC;CACxC;AAmmBD,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,UAAU,EACrB,OAAO,EAAE,YAAY,GACpB,MAAM,CAgVR"}
|