dauth-md-node 4.0.0 → 4.1.0
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 +161 -134
- package/dist/{chunk-4A7BR4EM.mjs → chunk-RKH7YKIR.mjs} +1 -1
- package/dist/chunk-RKH7YKIR.mjs.map +1 -0
- package/dist/index.d.mts +37 -2
- package/dist/index.d.ts +37 -2
- package/dist/index.js +56 -15
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +53 -15
- package/dist/index.mjs.map +1 -1
- package/dist/router.js +47 -55
- package/dist/router.js.map +1 -1
- package/dist/router.mjs +48 -56
- package/dist/router.mjs.map +1 -1
- package/package.json +15 -1
- package/src/api/dauth.api.ts +80 -0
- package/src/csrf.ts +7 -8
- package/src/index.ts +34 -52
- package/src/router.ts +55 -71
- package/src/session.ts +1 -4
- package/dist/chunk-4A7BR4EM.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,16 +1,37 @@
|
|
|
1
1
|
# dauth-md-node
|
|
2
2
|
|
|
3
|
-
Express middleware for JWT-based authentication against the [
|
|
3
|
+
Express middleware and router for JWT-based authentication against the [dauth](https://dauth.ovh) service. Two exports: `dauth()` middleware that verifies JWTs (via Authorization header or encrypted session cookies) and attaches `req.user`, and `dauthRouter()` that provides pre-built session-based auth routes with encrypted cookies, CSRF protection, and automatic token refresh.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
npm install dauth-md-node
|
|
9
9
|
# or
|
|
10
|
-
|
|
10
|
+
pnpm add dauth-md-node
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
For session cookie mode or the router, also install `cookie-parser`:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pnpm add cookie-parser
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Tech Stack
|
|
20
|
+
|
|
21
|
+
| Technology | Version | Purpose |
|
|
22
|
+
|---|---|---|
|
|
23
|
+
| Node.js | >= 18 | Runtime (native fetch required) |
|
|
24
|
+
| TypeScript | 5.9 | Type safety |
|
|
25
|
+
| jsonwebtoken | 9 | JWT verification (runtime dependency) |
|
|
26
|
+
| Express | 4.18+ / 5 | Peer dependency |
|
|
27
|
+
| cookie-parser | 1.4+ | Optional peer dependency (session/router mode) |
|
|
28
|
+
| tsup | 8 | Build tool (CJS + ESM bundles, two entry points) |
|
|
29
|
+
| vitest | 4 | Testing framework |
|
|
30
|
+
| size-limit | 12 | Bundle size budget (10KB per entry) |
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
### Middleware (Authorization header mode)
|
|
14
35
|
|
|
15
36
|
```typescript
|
|
16
37
|
import express from 'express';
|
|
@@ -18,186 +39,192 @@ import { dauth, IRequestDauth } from 'dauth-md-node';
|
|
|
18
39
|
|
|
19
40
|
const app = express();
|
|
20
41
|
|
|
21
|
-
// Apply DAuth middleware to protected routes
|
|
22
42
|
const dauthMiddleware = dauth({
|
|
23
43
|
domainName: 'your-domain-name',
|
|
24
44
|
tsk: 'your-tenant-secret-key',
|
|
25
45
|
});
|
|
26
46
|
|
|
27
47
|
app.get('/api/protected', dauthMiddleware, (req, res) => {
|
|
28
|
-
|
|
29
|
-
res.json({ user: req.user });
|
|
48
|
+
res.json({ user: (req as IRequestDauth).user });
|
|
30
49
|
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Middleware (session cookie mode)
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import express from 'express';
|
|
56
|
+
import cookieParser from 'cookie-parser';
|
|
57
|
+
import { dauth } from 'dauth-md-node';
|
|
31
58
|
|
|
32
|
-
app
|
|
59
|
+
const app = express();
|
|
60
|
+
app.use(cookieParser());
|
|
61
|
+
|
|
62
|
+
const dauthMiddleware = dauth({
|
|
63
|
+
domainName: 'your-domain-name',
|
|
64
|
+
tsk: 'your-tenant-secret-key',
|
|
65
|
+
session: {},
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
app.get('/api/protected', dauthMiddleware, (req, res) => {
|
|
69
|
+
res.json({ user: req.user });
|
|
70
|
+
});
|
|
33
71
|
```
|
|
34
72
|
|
|
35
|
-
|
|
73
|
+
### Router (session-based auth routes)
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
import express from 'express';
|
|
77
|
+
import cookieParser from 'cookie-parser';
|
|
78
|
+
import { dauthRouter } from 'dauth-md-node/router';
|
|
36
79
|
|
|
37
|
-
|
|
80
|
+
const app = express();
|
|
81
|
+
app.use(express.json());
|
|
82
|
+
app.use(cookieParser());
|
|
38
83
|
|
|
39
|
-
|
|
84
|
+
app.use('/api/auth', dauthRouter({
|
|
85
|
+
domainName: 'your-domain-name',
|
|
86
|
+
tsk: 'your-tenant-secret-key',
|
|
87
|
+
}));
|
|
88
|
+
|
|
89
|
+
// Available routes:
|
|
90
|
+
// POST /api/auth/exchange-code
|
|
91
|
+
// GET /api/auth/session
|
|
92
|
+
// POST /api/auth/logout (CSRF required)
|
|
93
|
+
// PATCH /api/auth/user (CSRF required)
|
|
94
|
+
// DELETE /api/auth/user (CSRF required)
|
|
95
|
+
// GET /api/auth/profile-redirect (CSRF required)
|
|
96
|
+
```
|
|
40
97
|
|
|
41
|
-
|
|
42
|
-
|---|---|---|
|
|
43
|
-
| `domainName` | `string` | Your Dauth domain name (used for API routing) |
|
|
44
|
-
| `tsk` | `string` | Tenant Secret Key for local JWT verification |
|
|
98
|
+
## API Reference
|
|
45
99
|
|
|
46
|
-
###
|
|
100
|
+
### `dauth(options: DauthOptions)`
|
|
47
101
|
|
|
48
|
-
|
|
49
|
-
2. Verifies the JWT locally using the provided `tsk` (Tenant Secret Key)
|
|
50
|
-
3. Fetches the full user object from the Dauth backend (`GET /app/:domainName/user`)
|
|
51
|
-
4. Attaches the user to `req.user`
|
|
52
|
-
5. Calls `next()` on success
|
|
102
|
+
Returns an Express middleware.
|
|
53
103
|
|
|
54
|
-
|
|
104
|
+
| Parameter | Type | Required | Default | Description |
|
|
105
|
+
|---|---|---|---|---|
|
|
106
|
+
| `domainName` | `string` | yes | - | Tenant identifier (used in API path) |
|
|
107
|
+
| `tsk` | `string` | yes | - | Tenant Secret Key for JWT verification |
|
|
108
|
+
| `cache` | `{ ttlMs: number }` | no | - | Enable in-memory user cache with TTL |
|
|
109
|
+
| `session` | `SessionOptions` | no | - | Enable session cookie mode |
|
|
55
110
|
|
|
56
|
-
|
|
57
|
-
|---|---|---|
|
|
58
|
-
| Missing `Authorization` header | 403 | `token-not-found` |
|
|
59
|
-
| JWT expired | 401 | `token-expired` |
|
|
60
|
-
| Invalid JWT or bad TSK | 401 | `tsk-not-invalid` or `token-invalid` |
|
|
61
|
-
| User not found in Dauth backend | 404 | `user-not-found` |
|
|
62
|
-
| Dauth backend server error | 500 | `error` |
|
|
63
|
-
| Other backend status | 501 | `request-error` |
|
|
111
|
+
**SessionOptions:**
|
|
64
112
|
|
|
65
|
-
|
|
113
|
+
| Parameter | Type | Default | Description |
|
|
114
|
+
|---|---|---|---|
|
|
115
|
+
| `cookieName` | `string` | `__Host-dauth-session` (prod) / `dauth-session` (dev) | Session cookie name |
|
|
116
|
+
| `secure` | `boolean` | `true` (except `NODE_ENV=development`) | Cookie `Secure` flag |
|
|
117
|
+
| `previousTsk` | `string` | - | Previous TSK for zero-downtime key rotation |
|
|
118
|
+
| `sessionSalt` | `string` | built-in default | Custom HKDF salt (hex string) |
|
|
66
119
|
|
|
67
|
-
|
|
120
|
+
**Without `session`**: extracts `Authorization` header, verifies JWT with `tsk`, fetches user from dauth backend, attaches to `req.user`.
|
|
68
121
|
|
|
69
|
-
|
|
70
|
-
interface IDauthUser {
|
|
71
|
-
_id: string;
|
|
72
|
-
name: string;
|
|
73
|
-
lastname: string;
|
|
74
|
-
nickname: string;
|
|
75
|
-
email: string;
|
|
76
|
-
isVerified: boolean;
|
|
77
|
-
language: string;
|
|
78
|
-
avatar: { id: string; url: string };
|
|
79
|
-
role: string;
|
|
80
|
-
telPrefix: string;
|
|
81
|
-
telSuffix: string;
|
|
82
|
-
createdAt: Date;
|
|
83
|
-
updatedAt: Date;
|
|
84
|
-
lastLogin: Date;
|
|
85
|
-
}
|
|
86
|
-
```
|
|
122
|
+
**With `session`**: reads encrypted session cookie, decrypts with key derived from `tsk` (falls back to `previousTsk`), extracts access token, verifies JWT, fetches user, attaches to `req.user`.
|
|
87
123
|
|
|
88
|
-
|
|
124
|
+
### `dauthRouter(options: DauthRouterOptions)`
|
|
89
125
|
|
|
90
|
-
|
|
126
|
+
Returns an Express Router. Requires `cookie-parser` middleware. Import from `dauth-md-node/router`.
|
|
91
127
|
|
|
92
|
-
|
|
128
|
+
| Parameter | Type | Default | Description |
|
|
129
|
+
|---|---|---|---|
|
|
130
|
+
| `domainName` | `string` | (required) | Tenant identifier |
|
|
131
|
+
| `tsk` | `string` | (required) | Tenant Secret Key |
|
|
132
|
+
| `dauthUrl` | `string` | auto-detected | Override dauth backend URL |
|
|
133
|
+
| `cookieName` | `string` | `__Host-dauth-session` / `dauth-session` | Session cookie name |
|
|
134
|
+
| `csrfCookieName` | `string` | `__Host-csrf` / `csrf-token` | CSRF cookie name |
|
|
135
|
+
| `maxAge` | `number` | `2592000` (30 days) | Cookie max age in seconds |
|
|
136
|
+
| `secure` | `boolean` | `true` (except dev) | Cookie `Secure` flag |
|
|
137
|
+
| `previousTsk` | `string` | - | Previous TSK for key rotation |
|
|
138
|
+
| `sessionSalt` | `string` | built-in default | Custom HKDF salt (hex string) |
|
|
93
139
|
|
|
94
|
-
|
|
140
|
+
**Router routes:**
|
|
95
141
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
142
|
+
| Method | Path | CSRF | Description |
|
|
143
|
+
|---|---|---|---|
|
|
144
|
+
| `POST` | `/exchange-code` | No | Exchange auth code for session, set cookies, return `{ user, domain, isNewUser }` |
|
|
145
|
+
| `GET` | `/session` | No | Check session, auto-refresh if token expires within 5min, return `{ user, domain }` |
|
|
146
|
+
| `POST` | `/logout` | Yes | Revoke refresh token (fire-and-forget), clear cookies |
|
|
147
|
+
| `PATCH` | `/user` | Yes | Proxy user update to dauth backend with auto-refresh |
|
|
148
|
+
| `DELETE` | `/user` | Yes | Delete user account, clear cookies |
|
|
149
|
+
| `GET` | `/profile-redirect` | Yes | Generate profile code, return `{ redirectUrl }` |
|
|
100
150
|
|
|
101
|
-
|
|
102
|
-
tsk: config.dauth.TSK as string,
|
|
103
|
-
domainName: config.dauth.DOMAIN_NAME as string,
|
|
104
|
-
});
|
|
105
|
-
```
|
|
151
|
+
CSRF uses the double-submit cookie pattern: `POST /exchange-code` sets a CSRF cookie (`httpOnly: false`), and subsequent state-changing requests must include the cookie value as the `x-csrf-token` header.
|
|
106
152
|
|
|
107
|
-
|
|
108
|
-
```
|
|
109
|
-
DAUTH_TSK=your-tenant-secret-key
|
|
110
|
-
DAUTH_DOMAIN_NAME=your-domain-name
|
|
111
|
-
```
|
|
153
|
+
### Exported types (from `dauth-md-node`)
|
|
112
154
|
|
|
113
|
-
|
|
155
|
+
- `IDauthUser` -- user object (name, email, avatar, role, language, authMethods, etc.)
|
|
156
|
+
- `AuthMethodType` -- `'magic-link' | 'passkey'`
|
|
157
|
+
- `IRequestDauth` -- extends Express `Request` with `user: IDauthUser`
|
|
158
|
+
- `DauthOptions` -- middleware factory options
|
|
159
|
+
- `SessionOptions` -- session cookie configuration
|
|
160
|
+
- `UserCache` -- cache class (for direct usage/testing)
|
|
161
|
+
- `CacheOptions` -- cache configuration type
|
|
114
162
|
|
|
115
|
-
|
|
163
|
+
### Exported types (from `dauth-md-node/router`)
|
|
116
164
|
|
|
117
|
-
|
|
118
|
-
// src/middlewares/auth.middleware.ts (continued)
|
|
119
|
-
import { Response, NextFunction } from 'express';
|
|
120
|
-
|
|
121
|
-
export const is_verified = async (req: IRequestDauth, res: Response, next: NextFunction) => {
|
|
122
|
-
if (req.user.isVerified === false) {
|
|
123
|
-
return res.status(401).send({ status: 'not-verified', message: 'Email not verified' });
|
|
124
|
-
}
|
|
125
|
-
next();
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
export const ensure_admin = async (req: IRequestDauth, res: Response, next: NextFunction) => {
|
|
129
|
-
if (req.user.isVerified === false) {
|
|
130
|
-
return res.status(401).send({ status: 'not-verified', message: 'Email not verified' });
|
|
131
|
-
}
|
|
132
|
-
if (req.user.role !== 'admin') {
|
|
133
|
-
return res.status(401).send({ status: 'not-admin', message: 'Admin role required' });
|
|
134
|
-
}
|
|
135
|
-
next();
|
|
136
|
-
};
|
|
137
|
-
```
|
|
165
|
+
- `DauthRouterOptions` -- router factory options
|
|
138
166
|
|
|
139
|
-
###
|
|
167
|
+
### Environment detection
|
|
140
168
|
|
|
141
|
-
|
|
142
|
-
// src/core/licenses/router/licenses.router.ts
|
|
143
|
-
import { Router } from 'express';
|
|
144
|
-
import { dauth_md, is_verified, ensure_admin } from '../../../middlewares/auth.middleware';
|
|
145
|
-
import * as controller from '../controllers/licenses.controller';
|
|
169
|
+
The middleware resolves the dauth backend URL in this order:
|
|
146
170
|
|
|
147
|
-
|
|
171
|
+
1. `DAUTH_URL` environment variable
|
|
172
|
+
2. `NODE_ENV=development` -> `http://localhost:4012/api/v1`
|
|
173
|
+
3. Default -> `https://dauth.ovh/api/v1`
|
|
148
174
|
|
|
149
|
-
|
|
150
|
-
.post('/create-license', [dauth_md, is_verified], controller.createLicense)
|
|
151
|
-
.get('/get-my-licenses', [dauth_md, is_verified], controller.getMyLicenses)
|
|
152
|
-
.patch('/enable-license/:licenseId', [dauth_md, ensure_admin], controller.enableLicense)
|
|
153
|
-
.delete('/delete-license/:licenseId', [dauth_md, is_verified], controller.deleteLicense);
|
|
175
|
+
The router also accepts `dauthUrl` in options, which takes priority over the env var.
|
|
154
176
|
|
|
155
|
-
|
|
156
|
-
```
|
|
177
|
+
### Error responses (middleware)
|
|
157
178
|
|
|
158
|
-
|
|
179
|
+
| Scenario | Status | Status Field |
|
|
180
|
+
|---|---|---|
|
|
181
|
+
| Missing `Authorization` header (header mode) | 403 | `token-not-found` |
|
|
182
|
+
| Missing session cookie (session mode) | 401 | `no-session` |
|
|
183
|
+
| Invalid/undecryptable session cookie | 401 | `session-invalid` |
|
|
184
|
+
| JWT expired | 401 | `token-expired` |
|
|
185
|
+
| Invalid JWT or bad TSK | 401 | `tsk-not-invalid` / `token-invalid` |
|
|
186
|
+
| User not found | 404 | `user-not-found` |
|
|
187
|
+
| Backend server error | 500 | `error` |
|
|
188
|
+
| Other backend status | 501 | `request-error` |
|
|
159
189
|
|
|
160
|
-
|
|
161
|
-
// src/core/licenses/controllers/licenses.controller.ts
|
|
162
|
-
export const getMyLicenses = async (req: IRequestDauth, res: Response) => {
|
|
163
|
-
const userId = req.user._id;
|
|
164
|
-
const licenses = await License.find({ user: userId });
|
|
165
|
-
res.status(200).json({ status: 'success', data: licenses });
|
|
166
|
-
};
|
|
167
|
-
```
|
|
190
|
+
## Scripts
|
|
168
191
|
|
|
169
|
-
|
|
192
|
+
| Script | Description |
|
|
193
|
+
|---|---|
|
|
194
|
+
| `pnpm start` | Watch mode (tsup --watch) |
|
|
195
|
+
| `pnpm build` | Production build (CJS + ESM + types, two entry points) |
|
|
196
|
+
| `pnpm test` | Run vitest tests (80 tests) |
|
|
197
|
+
| `pnpm test:coverage` | Coverage report (v8, 70% threshold) |
|
|
198
|
+
| `pnpm typecheck` | TypeScript type checking |
|
|
199
|
+
| `pnpm size` | Check bundle size (10KB budget per entry) |
|
|
200
|
+
| `pnpm format` | Prettier formatting |
|
|
170
201
|
|
|
171
|
-
|
|
172
|
-
- **Production**: Routes API calls to `https://dauth.ovh/api/v1`
|
|
202
|
+
## Testing
|
|
173
203
|
|
|
174
|
-
|
|
204
|
+
80 tests across 7 files using vitest 4 (node environment).
|
|
175
205
|
|
|
176
206
|
```bash
|
|
177
|
-
pnpm
|
|
178
|
-
pnpm
|
|
179
|
-
pnpm test # Run Jest tests
|
|
180
|
-
pnpm lint # ESLint via tsdx
|
|
181
|
-
pnpm size # Check bundle size (10KB budget per entry)
|
|
182
|
-
pnpm analyze # Bundle size analysis with visualization
|
|
207
|
+
pnpm test
|
|
208
|
+
pnpm test:coverage
|
|
183
209
|
```
|
|
184
210
|
|
|
185
|
-
|
|
211
|
+
## Publishing
|
|
186
212
|
|
|
187
|
-
|
|
188
|
-
- **ESM:** `dist/dauth-md-node.esm.js`
|
|
189
|
-
- **Types:** `dist/index.d.ts`
|
|
213
|
+
Automated via GitHub Actions. Push a `v*` tag to trigger build, test, and `npm publish`.
|
|
190
214
|
|
|
191
|
-
|
|
215
|
+
```bash
|
|
216
|
+
# 1. Bump version in package.json
|
|
217
|
+
# 2. Commit and tag
|
|
218
|
+
git tag v4.0.1
|
|
219
|
+
git push origin main --tags
|
|
220
|
+
# 3. GitHub Actions publishes to npm automatically
|
|
221
|
+
```
|
|
192
222
|
|
|
193
|
-
|
|
194
|
-
- `jsonwebtoken` >= 9
|
|
195
|
-
- `mongoose` >= 8
|
|
196
|
-
- `node-fetch` ^2.6
|
|
223
|
+
Requires `NPM_TOKEN` secret configured in the GitHub repo.
|
|
197
224
|
|
|
198
|
-
##
|
|
225
|
+
## Code Style
|
|
199
226
|
|
|
200
|
-
|
|
227
|
+
Prettier: 80 char width, single quotes, semicolons, trailing commas (es5), 2-space indent.
|
|
201
228
|
|
|
202
229
|
## License
|
|
203
230
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/api/utils/config.ts","../src/session.ts"],"sourcesContent":["export const apiVersion = 'v1';\nexport const serverDomain = 'dauth.ovh';\n\nexport function getServerBasePath(): string {\n if (process.env.DAUTH_URL) {\n const base = process.env.DAUTH_URL.replace(/\\/+$/, '');\n return `${base}/api/${apiVersion}`;\n }\n\n const isLocalhost = process.env.NODE_ENV === 'development';\n const serverPort = 4012;\n const serverLocalUrl = `http://localhost:${serverPort}/api/${apiVersion}`;\n const serverProdUrl = `https://${serverDomain}/api/${apiVersion}`;\n return isLocalhost ? serverLocalUrl : serverProdUrl;\n}\n","import crypto from 'crypto';\n\nexport interface SessionPayload {\n accessToken: string;\n refreshToken: string;\n}\n\nconst INFO = 'dauth-cookie-enc-v1';\nconst DEFAULT_SALT = Buffer.from(\n 'a3f8c1d7e9b24f6081c5d3a7e2f49b0653d81f7a2e94c0b6d8f3a5e1c7b09d42',\n 'hex'\n);\n\nexport async function deriveEncryptionKey(\n tsk: string,\n salt?: string\n): Promise<Buffer> {\n const saltBuf = salt ? Buffer.from(salt, 'hex') : DEFAULT_SALT;\n return new Promise((resolve, reject) => {\n crypto.hkdf(\n 'sha256',\n Buffer.from(tsk),\n saltBuf,\n INFO,\n 32,\n (err, derivedKey) => {\n if (err) return reject(err);\n resolve(Buffer.from(derivedKey));\n }\n );\n });\n}\n\nexport function encryptSession(payload: SessionPayload, key: Buffer): string {\n const nonce = crypto.randomBytes(12);\n const cipher = crypto.createCipheriv('aes-256-gcm', key, nonce);\n const plaintext = JSON.stringify(payload);\n const encrypted = Buffer.concat([\n cipher.update(plaintext, 'utf8'),\n cipher.final(),\n ]);\n const authTag = cipher.getAuthTag();\n // Format: base64(nonce + ciphertext + authTag)\n return Buffer.concat([nonce, encrypted, authTag]).toString('base64');\n}\n\nexport function decryptSession(\n ciphertext: string,\n key: Buffer\n): SessionPayload | null {\n try {\n const buf = Buffer.from(ciphertext, 'base64');\n if (buf.length < 12 + 16) return null; // nonce(12) + authTag(16) minimum\n const nonce = buf.subarray(0, 12);\n const authTag = buf.subarray(buf.length - 16);\n const encrypted = buf.subarray(12, buf.length - 16);\n const decipher = crypto.createDecipheriv('aes-256-gcm', key, nonce);\n decipher.setAuthTag(authTag);\n const decrypted = Buffer.concat([\n decipher.update(encrypted),\n decipher.final(),\n ]);\n return JSON.parse(decrypted.toString('utf8')) as SessionPayload;\n } catch {\n return null;\n }\n}\n\nexport function decryptSessionWithKeys(\n ciphertext: string,\n keys: Buffer[]\n): SessionPayload | null {\n for (const key of keys) {\n const result = decryptSession(ciphertext, key);\n if (result) return result;\n }\n return null;\n}\n"],"mappings":";AAAO,IAAM,aAAa;AACnB,IAAM,eAAe;AAErB,SAAS,oBAA4B;AAC1C,MAAI,QAAQ,IAAI,WAAW;AACzB,UAAM,OAAO,QAAQ,IAAI,UAAU,QAAQ,QAAQ,EAAE;AACrD,WAAO,GAAG,IAAI,QAAQ,UAAU;AAAA,EAClC;AAEA,QAAM,cAAc,QAAQ,IAAI,aAAa;AAC7C,QAAM,aAAa;AACnB,QAAM,iBAAiB,oBAAoB,UAAU,QAAQ,UAAU;AACvE,QAAM,gBAAgB,WAAW,YAAY,QAAQ,UAAU;AAC/D,SAAO,cAAc,iBAAiB;AACxC;;;ACdA,OAAO,YAAY;AAOnB,IAAM,OAAO;AACb,IAAM,eAAe,OAAO;AAAA,EAC1B;AAAA,EACA;AACF;AAEA,eAAsB,oBACpB,KACA,MACiB;AACjB,QAAM,UAAU,OAAO,OAAO,KAAK,MAAM,KAAK,IAAI;AAClD,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,WAAO;AAAA,MACL;AAAA,MACA,OAAO,KAAK,GAAG;AAAA,MACf;AAAA,MACA;AAAA,MACA;AAAA,MACA,CAAC,KAAK,eAAe;AACnB,YAAI,IAAK,QAAO,OAAO,GAAG;AAC1B,gBAAQ,OAAO,KAAK,UAAU,CAAC;AAAA,MACjC;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAEO,SAAS,eAAe,SAAyB,KAAqB;AAC3E,QAAM,QAAQ,OAAO,YAAY,EAAE;AACnC,QAAM,SAAS,OAAO,eAAe,eAAe,KAAK,KAAK;AAC9D,QAAM,YAAY,KAAK,UAAU,OAAO;AACxC,QAAM,YAAY,OAAO,OAAO;AAAA,IAC9B,OAAO,OAAO,WAAW,MAAM;AAAA,IAC/B,OAAO,MAAM;AAAA,EACf,CAAC;AACD,QAAM,UAAU,OAAO,WAAW;AAElC,SAAO,OAAO,OAAO,CAAC,OAAO,WAAW,OAAO,CAAC,EAAE,SAAS,QAAQ;AACrE;AAEO,SAAS,eACd,YACA,KACuB;AACvB,MAAI;AACF,UAAM,MAAM,OAAO,KAAK,YAAY,QAAQ;AAC5C,QAAI,IAAI,SAAS,KAAK,GAAI,QAAO;AACjC,UAAM,QAAQ,IAAI,SAAS,GAAG,EAAE;AAChC,UAAM,UAAU,IAAI,SAAS,IAAI,SAAS,EAAE;AAC5C,UAAM,YAAY,IAAI,SAAS,IAAI,IAAI,SAAS,EAAE;AAClD,UAAM,WAAW,OAAO,iBAAiB,eAAe,KAAK,KAAK;AAClE,aAAS,WAAW,OAAO;AAC3B,UAAM,YAAY,OAAO,OAAO;AAAA,MAC9B,SAAS,OAAO,SAAS;AAAA,MACzB,SAAS,MAAM;AAAA,IACjB,CAAC;AACD,WAAO,KAAK,MAAM,UAAU,SAAS,MAAM,CAAC;AAAA,EAC9C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,uBACd,YACA,MACuB;AACvB,aAAW,OAAO,MAAM;AACtB,UAAM,SAAS,eAAe,YAAY,GAAG;AAC7C,QAAI,OAAQ,QAAO;AAAA,EACrB;AACA,SAAO;AACT;","names":[]}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
import { Request, Response, NextFunction } from 'express';
|
|
2
2
|
|
|
3
|
+
interface TenantUser {
|
|
4
|
+
_id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
lastname: string;
|
|
7
|
+
email: string;
|
|
8
|
+
avatar: {
|
|
9
|
+
id: string;
|
|
10
|
+
url: string;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
interface TenantUserResponse {
|
|
14
|
+
response: {
|
|
15
|
+
status: number;
|
|
16
|
+
};
|
|
17
|
+
data: {
|
|
18
|
+
status?: string;
|
|
19
|
+
data?: TenantUser | null;
|
|
20
|
+
message?: string;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
interface BatchUsersResponse {
|
|
24
|
+
response: {
|
|
25
|
+
status: number;
|
|
26
|
+
};
|
|
27
|
+
data: {
|
|
28
|
+
status?: string;
|
|
29
|
+
data?: TenantUser[];
|
|
30
|
+
message?: string;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
declare function searchUserByEmail(token: string, domainName: string, email: string): Promise<TenantUserResponse>;
|
|
34
|
+
declare function getUserById(token: string, domainName: string, userId: string): Promise<TenantUserResponse>;
|
|
35
|
+
declare function batchGetUsers(token: string, domainName: string, userIds: string[]): Promise<BatchUsersResponse>;
|
|
36
|
+
|
|
3
37
|
interface CacheOptions {
|
|
4
38
|
ttlMs: number;
|
|
5
39
|
}
|
|
@@ -39,6 +73,7 @@ interface IDauthUser {
|
|
|
39
73
|
}
|
|
40
74
|
interface IRequestDauth extends Request {
|
|
41
75
|
user: IDauthUser;
|
|
76
|
+
dauthToken: string;
|
|
42
77
|
files: {
|
|
43
78
|
image: {
|
|
44
79
|
path: string;
|
|
@@ -68,6 +103,6 @@ interface TCustomResponse extends Response {
|
|
|
68
103
|
send(body?: unknown): this;
|
|
69
104
|
}
|
|
70
105
|
|
|
71
|
-
declare const dauth: ({ domainName, tsk, cache, session
|
|
106
|
+
declare const dauth: ({ domainName, tsk, cache, session }: DauthOptions) => (req: IRequestDauth, res: TCustomResponse, next: NextFunction) => Promise<void | TCustomResponse>;
|
|
72
107
|
|
|
73
|
-
export { type AuthMethodType, type CacheOptions, type DauthOptions, type IDauthUser, type IRequestDauth, type SessionOptions, UserCache, dauth };
|
|
108
|
+
export { type AuthMethodType, type CacheOptions, type DauthOptions, type IDauthUser, type IRequestDauth, type SessionOptions, type TenantUser, UserCache, batchGetUsers, dauth, getUserById, searchUserByEmail };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
import { Request, Response, NextFunction } from 'express';
|
|
2
2
|
|
|
3
|
+
interface TenantUser {
|
|
4
|
+
_id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
lastname: string;
|
|
7
|
+
email: string;
|
|
8
|
+
avatar: {
|
|
9
|
+
id: string;
|
|
10
|
+
url: string;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
interface TenantUserResponse {
|
|
14
|
+
response: {
|
|
15
|
+
status: number;
|
|
16
|
+
};
|
|
17
|
+
data: {
|
|
18
|
+
status?: string;
|
|
19
|
+
data?: TenantUser | null;
|
|
20
|
+
message?: string;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
interface BatchUsersResponse {
|
|
24
|
+
response: {
|
|
25
|
+
status: number;
|
|
26
|
+
};
|
|
27
|
+
data: {
|
|
28
|
+
status?: string;
|
|
29
|
+
data?: TenantUser[];
|
|
30
|
+
message?: string;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
declare function searchUserByEmail(token: string, domainName: string, email: string): Promise<TenantUserResponse>;
|
|
34
|
+
declare function getUserById(token: string, domainName: string, userId: string): Promise<TenantUserResponse>;
|
|
35
|
+
declare function batchGetUsers(token: string, domainName: string, userIds: string[]): Promise<BatchUsersResponse>;
|
|
36
|
+
|
|
3
37
|
interface CacheOptions {
|
|
4
38
|
ttlMs: number;
|
|
5
39
|
}
|
|
@@ -39,6 +73,7 @@ interface IDauthUser {
|
|
|
39
73
|
}
|
|
40
74
|
interface IRequestDauth extends Request {
|
|
41
75
|
user: IDauthUser;
|
|
76
|
+
dauthToken: string;
|
|
42
77
|
files: {
|
|
43
78
|
image: {
|
|
44
79
|
path: string;
|
|
@@ -68,6 +103,6 @@ interface TCustomResponse extends Response {
|
|
|
68
103
|
send(body?: unknown): this;
|
|
69
104
|
}
|
|
70
105
|
|
|
71
|
-
declare const dauth: ({ domainName, tsk, cache, session
|
|
106
|
+
declare const dauth: ({ domainName, tsk, cache, session }: DauthOptions) => (req: IRequestDauth, res: TCustomResponse, next: NextFunction) => Promise<void | TCustomResponse>;
|
|
72
107
|
|
|
73
|
-
export { type AuthMethodType, type CacheOptions, type DauthOptions, type IDauthUser, type IRequestDauth, type SessionOptions, UserCache, dauth };
|
|
108
|
+
export { type AuthMethodType, type CacheOptions, type DauthOptions, type IDauthUser, type IRequestDauth, type SessionOptions, type TenantUser, UserCache, batchGetUsers, dauth, getUserById, searchUserByEmail };
|
package/dist/index.js
CHANGED
|
@@ -31,7 +31,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
UserCache: () => UserCache,
|
|
34
|
-
|
|
34
|
+
batchGetUsers: () => batchGetUsers,
|
|
35
|
+
dauth: () => dauth,
|
|
36
|
+
getUserById: () => getUserById,
|
|
37
|
+
searchUserByEmail: () => searchUserByEmail
|
|
35
38
|
});
|
|
36
39
|
module.exports = __toCommonJS(index_exports);
|
|
37
40
|
var import_jsonwebtoken = __toESM(require("jsonwebtoken"));
|
|
@@ -66,6 +69,50 @@ async function getUser(token, domainName) {
|
|
|
66
69
|
const data = await response.json();
|
|
67
70
|
return { response: { status: response.status }, data };
|
|
68
71
|
}
|
|
72
|
+
async function searchUserByEmail(token, domainName, email) {
|
|
73
|
+
const params = new URLSearchParams({ email });
|
|
74
|
+
const response = await fetch(
|
|
75
|
+
`${getServerBasePath()}/app/${domainName}/users/search?${params}`,
|
|
76
|
+
{
|
|
77
|
+
method: "GET",
|
|
78
|
+
headers: {
|
|
79
|
+
Authorization: token,
|
|
80
|
+
"Content-Type": "application/json"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
);
|
|
84
|
+
const data = await response.json();
|
|
85
|
+
return { response: { status: response.status }, data };
|
|
86
|
+
}
|
|
87
|
+
async function getUserById(token, domainName, userId) {
|
|
88
|
+
const response = await fetch(
|
|
89
|
+
`${getServerBasePath()}/app/${domainName}/users/${userId}`,
|
|
90
|
+
{
|
|
91
|
+
method: "GET",
|
|
92
|
+
headers: {
|
|
93
|
+
Authorization: token,
|
|
94
|
+
"Content-Type": "application/json"
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
);
|
|
98
|
+
const data = await response.json();
|
|
99
|
+
return { response: { status: response.status }, data };
|
|
100
|
+
}
|
|
101
|
+
async function batchGetUsers(token, domainName, userIds) {
|
|
102
|
+
const response = await fetch(
|
|
103
|
+
`${getServerBasePath()}/app/${domainName}/users/batch`,
|
|
104
|
+
{
|
|
105
|
+
method: "POST",
|
|
106
|
+
headers: {
|
|
107
|
+
Authorization: token,
|
|
108
|
+
"Content-Type": "application/json"
|
|
109
|
+
},
|
|
110
|
+
body: JSON.stringify({ userIds })
|
|
111
|
+
}
|
|
112
|
+
);
|
|
113
|
+
const data = await response.json();
|
|
114
|
+
return { response: { status: response.status }, data };
|
|
115
|
+
}
|
|
69
116
|
|
|
70
117
|
// src/cache.ts
|
|
71
118
|
var UserCache = class {
|
|
@@ -152,27 +199,17 @@ function decryptSessionWithKeys(ciphertext, keys) {
|
|
|
152
199
|
}
|
|
153
200
|
|
|
154
201
|
// src/index.ts
|
|
155
|
-
var dauth = ({
|
|
156
|
-
domainName,
|
|
157
|
-
tsk,
|
|
158
|
-
cache,
|
|
159
|
-
session
|
|
160
|
-
}) => {
|
|
202
|
+
var dauth = ({ domainName, tsk, cache, session }) => {
|
|
161
203
|
const userCache = cache ? new UserCache(cache) : null;
|
|
162
204
|
let keysPromise = null;
|
|
163
205
|
async function getEncKeys() {
|
|
164
206
|
if (!keysPromise) {
|
|
165
207
|
keysPromise = (async () => {
|
|
166
208
|
const keys = [];
|
|
167
|
-
keys.push(
|
|
168
|
-
await deriveEncryptionKey(tsk, session?.sessionSalt)
|
|
169
|
-
);
|
|
209
|
+
keys.push(await deriveEncryptionKey(tsk, session?.sessionSalt));
|
|
170
210
|
if (session?.previousTsk) {
|
|
171
211
|
keys.push(
|
|
172
|
-
await deriveEncryptionKey(
|
|
173
|
-
session.previousTsk,
|
|
174
|
-
session.sessionSalt
|
|
175
|
-
)
|
|
212
|
+
await deriveEncryptionKey(session.previousTsk, session.sessionSalt)
|
|
176
213
|
);
|
|
177
214
|
}
|
|
178
215
|
return keys;
|
|
@@ -229,6 +266,7 @@ var dauth = ({
|
|
|
229
266
|
}
|
|
230
267
|
return res.status(401).send({ status: "token-invalid", message });
|
|
231
268
|
}
|
|
269
|
+
req.dauthToken = token;
|
|
232
270
|
if (userCache) {
|
|
233
271
|
const cachedUser = userCache.get(token);
|
|
234
272
|
if (cachedUser) {
|
|
@@ -270,6 +308,9 @@ var dauth = ({
|
|
|
270
308
|
// Annotate the CommonJS export names for ESM import in node:
|
|
271
309
|
0 && (module.exports = {
|
|
272
310
|
UserCache,
|
|
273
|
-
|
|
311
|
+
batchGetUsers,
|
|
312
|
+
dauth,
|
|
313
|
+
getUserById,
|
|
314
|
+
searchUserByEmail
|
|
274
315
|
});
|
|
275
316
|
//# sourceMappingURL=index.js.map
|