dauth-md-node 1.0.1 → 2.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 +13 -13
- package/dist/index.d.mts +66 -0
- package/dist/index.d.ts +66 -43
- package/dist/index.js +168 -5
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +135 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +31 -27
- package/src/api/dauth.api.ts +18 -12
- package/src/api/utils/config.ts +6 -1
- package/src/cache.ts +51 -0
- package/src/index.ts +82 -56
- package/dist/api/dauth.api.d.ts +0 -4
- package/dist/api/utils/config.d.ts +0 -3
- package/dist/dauth-md-node.cjs.development.js +0 -453
- package/dist/dauth-md-node.cjs.development.js.map +0 -1
- package/dist/dauth-md-node.cjs.production.min.js +0 -2
- package/dist/dauth-md-node.cjs.production.min.js.map +0 -1
- package/dist/dauth-md-node.esm.js +0 -450
- package/dist/dauth-md-node.esm.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# dauth-md-node
|
|
2
2
|
|
|
3
|
-
Express middleware for JWT-based authentication against the [
|
|
3
|
+
Express middleware for JWT-based authentication against the [Dauth](https://dauth.ovh) service. Verifies tenant JWTs and fetches the authenticated user from the Dauth backend, attaching it to `req.user`.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -40,14 +40,14 @@ Factory function that returns an Express middleware.
|
|
|
40
40
|
|
|
41
41
|
| Parameter | Type | Description |
|
|
42
42
|
|---|---|---|
|
|
43
|
-
| `domainName` | `string` | Your
|
|
43
|
+
| `domainName` | `string` | Your Dauth domain name (used for API routing) |
|
|
44
44
|
| `tsk` | `string` | Tenant Secret Key for local JWT verification |
|
|
45
45
|
|
|
46
46
|
### Middleware Behavior
|
|
47
47
|
|
|
48
48
|
1. Extracts the `Authorization` header from the request
|
|
49
49
|
2. Verifies the JWT locally using the provided `tsk` (Tenant Secret Key)
|
|
50
|
-
3. Fetches the full user object from the
|
|
50
|
+
3. Fetches the full user object from the Dauth backend (`GET /app/:domainName/user`)
|
|
51
51
|
4. Attaches the user to `req.user`
|
|
52
52
|
5. Calls `next()` on success
|
|
53
53
|
|
|
@@ -58,8 +58,8 @@ Factory function that returns an Express middleware.
|
|
|
58
58
|
| Missing `Authorization` header | 403 | `token-not-found` |
|
|
59
59
|
| JWT expired | 401 | `token-expired` |
|
|
60
60
|
| Invalid JWT or bad TSK | 401 | `tsk-not-invalid` or `token-invalid` |
|
|
61
|
-
| User not found in
|
|
62
|
-
|
|
|
61
|
+
| User not found in Dauth backend | 404 | `user-not-found` |
|
|
62
|
+
| Dauth backend server error | 500 | `error` |
|
|
63
63
|
| Other backend status | 501 | `request-error` |
|
|
64
64
|
|
|
65
65
|
### `req.user` Object
|
|
@@ -89,7 +89,7 @@ Both `IDauthUser` and `IRequestDauth` are exported from the package for type-saf
|
|
|
89
89
|
|
|
90
90
|
## Real-World Integration Example
|
|
91
91
|
|
|
92
|
-
This is the pattern used in `easymediacloud-backend-node`, which delegates all user authentication to
|
|
92
|
+
This is the pattern used in `easymediacloud-backend-node`, which delegates all user authentication to Dauth.
|
|
93
93
|
|
|
94
94
|
### 1. Initialize the middleware from environment variables
|
|
95
95
|
|
|
@@ -169,17 +169,17 @@ export const getMyLicenses = async (req: IRequestDauth, res: Response) => {
|
|
|
169
169
|
## Environment Detection
|
|
170
170
|
|
|
171
171
|
- **Development** (`NODE_ENV=development`): Routes API calls to `http://localhost:4012/api/v1`
|
|
172
|
-
- **Production**: Routes API calls to `https
|
|
172
|
+
- **Production**: Routes API calls to `https://dauth.ovh/api/v1`
|
|
173
173
|
|
|
174
174
|
## Development
|
|
175
175
|
|
|
176
176
|
```bash
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
177
|
+
pnpm start # Watch mode (tsdx watch)
|
|
178
|
+
pnpm build # Production build (CJS + ESM)
|
|
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
|
|
183
183
|
```
|
|
184
184
|
|
|
185
185
|
### Bundle Outputs
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
|
|
3
|
+
interface CacheOptions {
|
|
4
|
+
ttlMs: number;
|
|
5
|
+
}
|
|
6
|
+
declare class UserCache {
|
|
7
|
+
private store;
|
|
8
|
+
private ttlMs;
|
|
9
|
+
constructor(options: CacheOptions);
|
|
10
|
+
get(token: string): IDauthUser | undefined;
|
|
11
|
+
set(token: string, user: IDauthUser): void;
|
|
12
|
+
clear(): void;
|
|
13
|
+
private sweep;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
type AuthMethodType = 'password' | 'magic-link' | 'passkey';
|
|
17
|
+
interface IDauthUser {
|
|
18
|
+
_id: string;
|
|
19
|
+
name: string;
|
|
20
|
+
lastname: string;
|
|
21
|
+
nickname: string;
|
|
22
|
+
email: string;
|
|
23
|
+
isVerified: boolean;
|
|
24
|
+
language: string;
|
|
25
|
+
avatar: {
|
|
26
|
+
id: string;
|
|
27
|
+
url: string;
|
|
28
|
+
};
|
|
29
|
+
role: string;
|
|
30
|
+
telPrefix: string;
|
|
31
|
+
telSuffix: string;
|
|
32
|
+
birthDate?: string;
|
|
33
|
+
country?: string;
|
|
34
|
+
metadata?: Record<string, unknown>;
|
|
35
|
+
authMethods?: AuthMethodType[];
|
|
36
|
+
createdAt: Date;
|
|
37
|
+
updatedAt: Date;
|
|
38
|
+
lastLogin: Date;
|
|
39
|
+
}
|
|
40
|
+
interface IRequestDauth extends Request {
|
|
41
|
+
user: IDauthUser;
|
|
42
|
+
files: {
|
|
43
|
+
image: {
|
|
44
|
+
path: string;
|
|
45
|
+
};
|
|
46
|
+
avatar: {
|
|
47
|
+
path: string;
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
headers: {
|
|
51
|
+
authorization: string;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
interface DauthOptions {
|
|
55
|
+
domainName: string;
|
|
56
|
+
tsk: string;
|
|
57
|
+
cache?: CacheOptions;
|
|
58
|
+
}
|
|
59
|
+
interface TCustomResponse extends Response {
|
|
60
|
+
status(code: number): this;
|
|
61
|
+
send(body?: unknown): this;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
declare const dauth: ({ domainName, tsk, cache }: DauthOptions) => (req: IRequestDauth, res: TCustomResponse, next: NextFunction) => Promise<void | TCustomResponse>;
|
|
65
|
+
|
|
66
|
+
export { type AuthMethodType, type CacheOptions, type DauthOptions, type IDauthUser, type IRequestDauth, UserCache, dauth };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,43 +1,66 @@
|
|
|
1
|
-
import { Request,
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
1
|
+
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
|
|
3
|
+
interface CacheOptions {
|
|
4
|
+
ttlMs: number;
|
|
5
|
+
}
|
|
6
|
+
declare class UserCache {
|
|
7
|
+
private store;
|
|
8
|
+
private ttlMs;
|
|
9
|
+
constructor(options: CacheOptions);
|
|
10
|
+
get(token: string): IDauthUser | undefined;
|
|
11
|
+
set(token: string, user: IDauthUser): void;
|
|
12
|
+
clear(): void;
|
|
13
|
+
private sweep;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
type AuthMethodType = 'password' | 'magic-link' | 'passkey';
|
|
17
|
+
interface IDauthUser {
|
|
18
|
+
_id: string;
|
|
19
|
+
name: string;
|
|
20
|
+
lastname: string;
|
|
21
|
+
nickname: string;
|
|
22
|
+
email: string;
|
|
23
|
+
isVerified: boolean;
|
|
24
|
+
language: string;
|
|
25
|
+
avatar: {
|
|
26
|
+
id: string;
|
|
27
|
+
url: string;
|
|
28
|
+
};
|
|
29
|
+
role: string;
|
|
30
|
+
telPrefix: string;
|
|
31
|
+
telSuffix: string;
|
|
32
|
+
birthDate?: string;
|
|
33
|
+
country?: string;
|
|
34
|
+
metadata?: Record<string, unknown>;
|
|
35
|
+
authMethods?: AuthMethodType[];
|
|
36
|
+
createdAt: Date;
|
|
37
|
+
updatedAt: Date;
|
|
38
|
+
lastLogin: Date;
|
|
39
|
+
}
|
|
40
|
+
interface IRequestDauth extends Request {
|
|
41
|
+
user: IDauthUser;
|
|
42
|
+
files: {
|
|
43
|
+
image: {
|
|
44
|
+
path: string;
|
|
45
|
+
};
|
|
46
|
+
avatar: {
|
|
47
|
+
path: string;
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
headers: {
|
|
51
|
+
authorization: string;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
interface DauthOptions {
|
|
55
|
+
domainName: string;
|
|
56
|
+
tsk: string;
|
|
57
|
+
cache?: CacheOptions;
|
|
58
|
+
}
|
|
59
|
+
interface TCustomResponse extends Response {
|
|
60
|
+
status(code: number): this;
|
|
61
|
+
send(body?: unknown): this;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
declare const dauth: ({ domainName, tsk, cache }: DauthOptions) => (req: IRequestDauth, res: TCustomResponse, next: NextFunction) => Promise<void | TCustomResponse>;
|
|
65
|
+
|
|
66
|
+
export { type AuthMethodType, type CacheOptions, type DauthOptions, type IDauthUser, type IRequestDauth, UserCache, dauth };
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,171 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
1
29
|
|
|
2
|
-
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
UserCache: () => UserCache,
|
|
34
|
+
dauth: () => dauth
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(index_exports);
|
|
37
|
+
var import_jsonwebtoken = __toESM(require("jsonwebtoken"));
|
|
3
38
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
39
|
+
// src/api/utils/config.ts
|
|
40
|
+
var apiVersion = "v1";
|
|
41
|
+
var serverDomain = "dauth.ovh";
|
|
42
|
+
function getServerBasePath() {
|
|
43
|
+
if (process.env.DAUTH_URL) {
|
|
44
|
+
const base = process.env.DAUTH_URL.replace(/\/+$/, "");
|
|
45
|
+
return `${base}/api/${apiVersion}`;
|
|
46
|
+
}
|
|
47
|
+
const isLocalhost = process.env.NODE_ENV === "development";
|
|
48
|
+
const serverPort = 4012;
|
|
49
|
+
const serverLocalUrl = `http://localhost:${serverPort}/api/${apiVersion}`;
|
|
50
|
+
const serverProdUrl = `https://${serverDomain}/api/${apiVersion}`;
|
|
51
|
+
return isLocalhost ? serverLocalUrl : serverProdUrl;
|
|
8
52
|
}
|
|
53
|
+
|
|
54
|
+
// src/api/dauth.api.ts
|
|
55
|
+
async function getUser(token, domainName) {
|
|
56
|
+
const response = await fetch(
|
|
57
|
+
`${getServerBasePath()}/app/${domainName}/user`,
|
|
58
|
+
{
|
|
59
|
+
method: "GET",
|
|
60
|
+
headers: {
|
|
61
|
+
Authorization: token,
|
|
62
|
+
"Content-Type": "application/json"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
);
|
|
66
|
+
const data = await response.json();
|
|
67
|
+
return { response: { status: response.status }, data };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// src/cache.ts
|
|
71
|
+
var UserCache = class {
|
|
72
|
+
store = /* @__PURE__ */ new Map();
|
|
73
|
+
ttlMs;
|
|
74
|
+
constructor(options) {
|
|
75
|
+
this.ttlMs = options.ttlMs;
|
|
76
|
+
}
|
|
77
|
+
get(token) {
|
|
78
|
+
const entry = this.store.get(token);
|
|
79
|
+
if (!entry) return void 0;
|
|
80
|
+
if (Date.now() > entry.expiresAt) {
|
|
81
|
+
this.store.delete(token);
|
|
82
|
+
return void 0;
|
|
83
|
+
}
|
|
84
|
+
return entry.user;
|
|
85
|
+
}
|
|
86
|
+
set(token, user) {
|
|
87
|
+
if (this.store.size > 1e3) {
|
|
88
|
+
this.sweep();
|
|
89
|
+
}
|
|
90
|
+
this.store.set(token, { user, expiresAt: Date.now() + this.ttlMs });
|
|
91
|
+
}
|
|
92
|
+
clear() {
|
|
93
|
+
this.store.clear();
|
|
94
|
+
}
|
|
95
|
+
sweep() {
|
|
96
|
+
const now = Date.now();
|
|
97
|
+
for (const [key, entry] of this.store) {
|
|
98
|
+
if (now > entry.expiresAt) {
|
|
99
|
+
this.store.delete(key);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// src/index.ts
|
|
106
|
+
var dauth = ({ domainName, tsk, cache }) => {
|
|
107
|
+
const userCache = cache ? new UserCache(cache) : null;
|
|
108
|
+
return async (req, res, next) => {
|
|
109
|
+
if (!req.headers.authorization) {
|
|
110
|
+
return res.status(403).send({ status: "token-not-found", message: "Token not found" });
|
|
111
|
+
}
|
|
112
|
+
const token = req.headers.authorization.replace(/['"]+/g, "");
|
|
113
|
+
try {
|
|
114
|
+
import_jsonwebtoken.default.verify(token, tsk);
|
|
115
|
+
} catch (error) {
|
|
116
|
+
const message = error instanceof Error ? error.message : "Token invalid";
|
|
117
|
+
if (message === "jwt expired") {
|
|
118
|
+
return res.status(401).send({ status: "token-expired", message: "jwt expired" });
|
|
119
|
+
}
|
|
120
|
+
if (message === "invalid signature") {
|
|
121
|
+
return res.status(401).send({
|
|
122
|
+
status: "tsk-not-invalid",
|
|
123
|
+
message: "The TSK variable in the backend middleware is not valid"
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
return res.status(401).send({ status: "token-invalid", message });
|
|
127
|
+
}
|
|
128
|
+
if (userCache) {
|
|
129
|
+
const cachedUser = userCache.get(token);
|
|
130
|
+
if (cachedUser) {
|
|
131
|
+
req.user = cachedUser;
|
|
132
|
+
return next();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
try {
|
|
136
|
+
const getUserFetch = await getUser(token, domainName);
|
|
137
|
+
if (getUserFetch.response.status === 404) {
|
|
138
|
+
return res.status(404).send({
|
|
139
|
+
status: "user-not-found",
|
|
140
|
+
message: getUserFetch.data.message ?? "User does not exist"
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
if (getUserFetch.response.status === 500) {
|
|
144
|
+
return res.status(500).send({
|
|
145
|
+
status: "error",
|
|
146
|
+
message: getUserFetch.data.message ?? "Dauth server error"
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
if (getUserFetch.response.status === 200) {
|
|
150
|
+
req.user = getUserFetch.data.user;
|
|
151
|
+
if (userCache) {
|
|
152
|
+
userCache.set(token, req.user);
|
|
153
|
+
}
|
|
154
|
+
return next();
|
|
155
|
+
}
|
|
156
|
+
return res.status(501).send({
|
|
157
|
+
status: "request-error",
|
|
158
|
+
message: getUserFetch.data.message ?? "Dauth server error"
|
|
159
|
+
});
|
|
160
|
+
} catch (error) {
|
|
161
|
+
const message = error instanceof Error ? error.message : "Dauth server error";
|
|
162
|
+
return res.status(500).send({ status: "server-error", message });
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
};
|
|
166
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
167
|
+
0 && (module.exports = {
|
|
168
|
+
UserCache,
|
|
169
|
+
dauth
|
|
170
|
+
});
|
|
171
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/api/utils/config.ts","../src/api/dauth.api.ts","../src/cache.ts"],"sourcesContent":["import { Request, NextFunction, Response as ExpressResponse } from 'express';\nimport jwt from 'jsonwebtoken';\nimport { getUser } from './api/dauth.api';\nimport { UserCache } from './cache';\nimport type { CacheOptions } from './cache';\n\nexport type AuthMethodType = 'password' | 'magic-link' | 'passkey';\n\nexport interface IDauthUser {\n _id: string;\n name: string;\n lastname: string;\n nickname: string;\n email: string;\n isVerified: boolean;\n language: string;\n avatar: {\n id: string;\n url: string;\n };\n role: string;\n telPrefix: string;\n telSuffix: string;\n birthDate?: string;\n country?: string;\n metadata?: Record<string, unknown>;\n authMethods?: AuthMethodType[];\n createdAt: Date;\n updatedAt: Date;\n lastLogin: Date;\n}\n\nexport interface IRequestDauth extends Request {\n user: IDauthUser;\n files: {\n image: { path: string };\n avatar: { path: string };\n };\n headers: {\n authorization: string;\n };\n}\n\nexport interface DauthOptions {\n domainName: string;\n tsk: string;\n cache?: CacheOptions;\n}\n\ninterface TCustomResponse extends ExpressResponse {\n status(code: number): this;\n send(body?: unknown): this;\n}\n\nexport { UserCache };\nexport type { CacheOptions };\n\nexport const dauth = ({ domainName, tsk, cache }: DauthOptions) => {\n const userCache = cache ? new UserCache(cache) : null;\n\n return async (\n req: IRequestDauth,\n res: TCustomResponse,\n next: NextFunction\n ) => {\n if (!req.headers.authorization) {\n return res\n .status(403)\n .send({ status: 'token-not-found', message: 'Token not found' });\n }\n\n const token = req.headers.authorization.replace(/['\"]+/g, '');\n\n try {\n jwt.verify(token, tsk);\n } catch (error) {\n const message =\n error instanceof Error ? error.message : 'Token invalid';\n\n if (message === 'jwt expired') {\n return res\n .status(401)\n .send({ status: 'token-expired', message: 'jwt expired' });\n }\n if (message === 'invalid signature') {\n return res.status(401).send({\n status: 'tsk-not-invalid',\n message:\n 'The TSK variable in the backend middleware is not valid',\n });\n }\n return res\n .status(401)\n .send({ status: 'token-invalid', message });\n }\n\n if (userCache) {\n const cachedUser = userCache.get(token);\n if (cachedUser) {\n req.user = cachedUser;\n return next();\n }\n }\n\n try {\n const getUserFetch = await getUser(token, domainName);\n\n if (getUserFetch.response.status === 404) {\n return res.status(404).send({\n status: 'user-not-found',\n message: getUserFetch.data.message ?? 'User does not exist',\n });\n }\n if (getUserFetch.response.status === 500) {\n return res.status(500).send({\n status: 'error',\n message: getUserFetch.data.message ?? 'Dauth server error',\n });\n }\n if (getUserFetch.response.status === 200) {\n req.user = getUserFetch.data.user;\n if (userCache) {\n userCache.set(token, req.user);\n }\n return next();\n }\n return res.status(501).send({\n status: 'request-error',\n message: getUserFetch.data.message ?? 'Dauth server error',\n });\n } catch (error) {\n const message =\n error instanceof Error ? error.message : 'Dauth server error';\n return res\n .status(500)\n .send({ status: 'server-error', message });\n }\n };\n};\n","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 { getServerBasePath } from './utils/config';\n\ninterface GetUserResponse {\n response: { status: number };\n data: { user?: any; message?: string };\n}\n\nexport async function getUser(\n token: string,\n domainName: string\n): Promise<GetUserResponse> {\n const response = await fetch(\n `${getServerBasePath()}/app/${domainName}/user`,\n {\n method: 'GET',\n headers: {\n Authorization: token,\n 'Content-Type': 'application/json',\n },\n }\n );\n const data = (await response.json()) as GetUserResponse['data'];\n return { response: { status: response.status }, data };\n}\n","import { IDauthUser } from './index';\n\ninterface CacheEntry {\n user: IDauthUser;\n expiresAt: number;\n}\n\nexport interface CacheOptions {\n ttlMs: number;\n}\n\nexport class UserCache {\n private store = new Map<string, CacheEntry>();\n private ttlMs: number;\n\n constructor(options: CacheOptions) {\n this.ttlMs = options.ttlMs;\n }\n\n get(token: string): IDauthUser | undefined {\n const entry = this.store.get(token);\n if (!entry) return undefined;\n\n if (Date.now() > entry.expiresAt) {\n this.store.delete(token);\n return undefined;\n }\n\n return entry.user;\n }\n\n set(token: string, user: IDauthUser): void {\n if (this.store.size > 1000) {\n this.sweep();\n }\n this.store.set(token, { user, expiresAt: Date.now() + this.ttlMs });\n }\n\n clear(): void {\n this.store.clear();\n }\n\n private sweep(): void {\n const now = Date.now();\n for (const [key, entry] of this.store) {\n if (now > entry.expiresAt) {\n this.store.delete(key);\n }\n }\n }\n}\n"],"mappings":"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","names":["jwt"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import jwt from "jsonwebtoken";
|
|
3
|
+
|
|
4
|
+
// src/api/utils/config.ts
|
|
5
|
+
var apiVersion = "v1";
|
|
6
|
+
var serverDomain = "dauth.ovh";
|
|
7
|
+
function getServerBasePath() {
|
|
8
|
+
if (process.env.DAUTH_URL) {
|
|
9
|
+
const base = process.env.DAUTH_URL.replace(/\/+$/, "");
|
|
10
|
+
return `${base}/api/${apiVersion}`;
|
|
11
|
+
}
|
|
12
|
+
const isLocalhost = process.env.NODE_ENV === "development";
|
|
13
|
+
const serverPort = 4012;
|
|
14
|
+
const serverLocalUrl = `http://localhost:${serverPort}/api/${apiVersion}`;
|
|
15
|
+
const serverProdUrl = `https://${serverDomain}/api/${apiVersion}`;
|
|
16
|
+
return isLocalhost ? serverLocalUrl : serverProdUrl;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// src/api/dauth.api.ts
|
|
20
|
+
async function getUser(token, domainName) {
|
|
21
|
+
const response = await fetch(
|
|
22
|
+
`${getServerBasePath()}/app/${domainName}/user`,
|
|
23
|
+
{
|
|
24
|
+
method: "GET",
|
|
25
|
+
headers: {
|
|
26
|
+
Authorization: token,
|
|
27
|
+
"Content-Type": "application/json"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
const data = await response.json();
|
|
32
|
+
return { response: { status: response.status }, data };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// src/cache.ts
|
|
36
|
+
var UserCache = class {
|
|
37
|
+
store = /* @__PURE__ */ new Map();
|
|
38
|
+
ttlMs;
|
|
39
|
+
constructor(options) {
|
|
40
|
+
this.ttlMs = options.ttlMs;
|
|
41
|
+
}
|
|
42
|
+
get(token) {
|
|
43
|
+
const entry = this.store.get(token);
|
|
44
|
+
if (!entry) return void 0;
|
|
45
|
+
if (Date.now() > entry.expiresAt) {
|
|
46
|
+
this.store.delete(token);
|
|
47
|
+
return void 0;
|
|
48
|
+
}
|
|
49
|
+
return entry.user;
|
|
50
|
+
}
|
|
51
|
+
set(token, user) {
|
|
52
|
+
if (this.store.size > 1e3) {
|
|
53
|
+
this.sweep();
|
|
54
|
+
}
|
|
55
|
+
this.store.set(token, { user, expiresAt: Date.now() + this.ttlMs });
|
|
56
|
+
}
|
|
57
|
+
clear() {
|
|
58
|
+
this.store.clear();
|
|
59
|
+
}
|
|
60
|
+
sweep() {
|
|
61
|
+
const now = Date.now();
|
|
62
|
+
for (const [key, entry] of this.store) {
|
|
63
|
+
if (now > entry.expiresAt) {
|
|
64
|
+
this.store.delete(key);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// src/index.ts
|
|
71
|
+
var dauth = ({ domainName, tsk, cache }) => {
|
|
72
|
+
const userCache = cache ? new UserCache(cache) : null;
|
|
73
|
+
return async (req, res, next) => {
|
|
74
|
+
if (!req.headers.authorization) {
|
|
75
|
+
return res.status(403).send({ status: "token-not-found", message: "Token not found" });
|
|
76
|
+
}
|
|
77
|
+
const token = req.headers.authorization.replace(/['"]+/g, "");
|
|
78
|
+
try {
|
|
79
|
+
jwt.verify(token, tsk);
|
|
80
|
+
} catch (error) {
|
|
81
|
+
const message = error instanceof Error ? error.message : "Token invalid";
|
|
82
|
+
if (message === "jwt expired") {
|
|
83
|
+
return res.status(401).send({ status: "token-expired", message: "jwt expired" });
|
|
84
|
+
}
|
|
85
|
+
if (message === "invalid signature") {
|
|
86
|
+
return res.status(401).send({
|
|
87
|
+
status: "tsk-not-invalid",
|
|
88
|
+
message: "The TSK variable in the backend middleware is not valid"
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
return res.status(401).send({ status: "token-invalid", message });
|
|
92
|
+
}
|
|
93
|
+
if (userCache) {
|
|
94
|
+
const cachedUser = userCache.get(token);
|
|
95
|
+
if (cachedUser) {
|
|
96
|
+
req.user = cachedUser;
|
|
97
|
+
return next();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
const getUserFetch = await getUser(token, domainName);
|
|
102
|
+
if (getUserFetch.response.status === 404) {
|
|
103
|
+
return res.status(404).send({
|
|
104
|
+
status: "user-not-found",
|
|
105
|
+
message: getUserFetch.data.message ?? "User does not exist"
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
if (getUserFetch.response.status === 500) {
|
|
109
|
+
return res.status(500).send({
|
|
110
|
+
status: "error",
|
|
111
|
+
message: getUserFetch.data.message ?? "Dauth server error"
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
if (getUserFetch.response.status === 200) {
|
|
115
|
+
req.user = getUserFetch.data.user;
|
|
116
|
+
if (userCache) {
|
|
117
|
+
userCache.set(token, req.user);
|
|
118
|
+
}
|
|
119
|
+
return next();
|
|
120
|
+
}
|
|
121
|
+
return res.status(501).send({
|
|
122
|
+
status: "request-error",
|
|
123
|
+
message: getUserFetch.data.message ?? "Dauth server error"
|
|
124
|
+
});
|
|
125
|
+
} catch (error) {
|
|
126
|
+
const message = error instanceof Error ? error.message : "Dauth server error";
|
|
127
|
+
return res.status(500).send({ status: "server-error", message });
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
};
|
|
131
|
+
export {
|
|
132
|
+
UserCache,
|
|
133
|
+
dauth
|
|
134
|
+
};
|
|
135
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/api/utils/config.ts","../src/api/dauth.api.ts","../src/cache.ts"],"sourcesContent":["import { Request, NextFunction, Response as ExpressResponse } from 'express';\nimport jwt from 'jsonwebtoken';\nimport { getUser } from './api/dauth.api';\nimport { UserCache } from './cache';\nimport type { CacheOptions } from './cache';\n\nexport type AuthMethodType = 'password' | 'magic-link' | 'passkey';\n\nexport interface IDauthUser {\n _id: string;\n name: string;\n lastname: string;\n nickname: string;\n email: string;\n isVerified: boolean;\n language: string;\n avatar: {\n id: string;\n url: string;\n };\n role: string;\n telPrefix: string;\n telSuffix: string;\n birthDate?: string;\n country?: string;\n metadata?: Record<string, unknown>;\n authMethods?: AuthMethodType[];\n createdAt: Date;\n updatedAt: Date;\n lastLogin: Date;\n}\n\nexport interface IRequestDauth extends Request {\n user: IDauthUser;\n files: {\n image: { path: string };\n avatar: { path: string };\n };\n headers: {\n authorization: string;\n };\n}\n\nexport interface DauthOptions {\n domainName: string;\n tsk: string;\n cache?: CacheOptions;\n}\n\ninterface TCustomResponse extends ExpressResponse {\n status(code: number): this;\n send(body?: unknown): this;\n}\n\nexport { UserCache };\nexport type { CacheOptions };\n\nexport const dauth = ({ domainName, tsk, cache }: DauthOptions) => {\n const userCache = cache ? new UserCache(cache) : null;\n\n return async (\n req: IRequestDauth,\n res: TCustomResponse,\n next: NextFunction\n ) => {\n if (!req.headers.authorization) {\n return res\n .status(403)\n .send({ status: 'token-not-found', message: 'Token not found' });\n }\n\n const token = req.headers.authorization.replace(/['\"]+/g, '');\n\n try {\n jwt.verify(token, tsk);\n } catch (error) {\n const message =\n error instanceof Error ? error.message : 'Token invalid';\n\n if (message === 'jwt expired') {\n return res\n .status(401)\n .send({ status: 'token-expired', message: 'jwt expired' });\n }\n if (message === 'invalid signature') {\n return res.status(401).send({\n status: 'tsk-not-invalid',\n message:\n 'The TSK variable in the backend middleware is not valid',\n });\n }\n return res\n .status(401)\n .send({ status: 'token-invalid', message });\n }\n\n if (userCache) {\n const cachedUser = userCache.get(token);\n if (cachedUser) {\n req.user = cachedUser;\n return next();\n }\n }\n\n try {\n const getUserFetch = await getUser(token, domainName);\n\n if (getUserFetch.response.status === 404) {\n return res.status(404).send({\n status: 'user-not-found',\n message: getUserFetch.data.message ?? 'User does not exist',\n });\n }\n if (getUserFetch.response.status === 500) {\n return res.status(500).send({\n status: 'error',\n message: getUserFetch.data.message ?? 'Dauth server error',\n });\n }\n if (getUserFetch.response.status === 200) {\n req.user = getUserFetch.data.user;\n if (userCache) {\n userCache.set(token, req.user);\n }\n return next();\n }\n return res.status(501).send({\n status: 'request-error',\n message: getUserFetch.data.message ?? 'Dauth server error',\n });\n } catch (error) {\n const message =\n error instanceof Error ? error.message : 'Dauth server error';\n return res\n .status(500)\n .send({ status: 'server-error', message });\n }\n };\n};\n","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 { getServerBasePath } from './utils/config';\n\ninterface GetUserResponse {\n response: { status: number };\n data: { user?: any; message?: string };\n}\n\nexport async function getUser(\n token: string,\n domainName: string\n): Promise<GetUserResponse> {\n const response = await fetch(\n `${getServerBasePath()}/app/${domainName}/user`,\n {\n method: 'GET',\n headers: {\n Authorization: token,\n 'Content-Type': 'application/json',\n },\n }\n );\n const data = (await response.json()) as GetUserResponse['data'];\n return { response: { status: response.status }, data };\n}\n","import { IDauthUser } from './index';\n\ninterface CacheEntry {\n user: IDauthUser;\n expiresAt: number;\n}\n\nexport interface CacheOptions {\n ttlMs: number;\n}\n\nexport class UserCache {\n private store = new Map<string, CacheEntry>();\n private ttlMs: number;\n\n constructor(options: CacheOptions) {\n this.ttlMs = options.ttlMs;\n }\n\n get(token: string): IDauthUser | undefined {\n const entry = this.store.get(token);\n if (!entry) return undefined;\n\n if (Date.now() > entry.expiresAt) {\n this.store.delete(token);\n return undefined;\n }\n\n return entry.user;\n }\n\n set(token: string, user: IDauthUser): void {\n if (this.store.size > 1000) {\n this.sweep();\n }\n this.store.set(token, { user, expiresAt: Date.now() + this.ttlMs });\n }\n\n clear(): void {\n this.store.clear();\n }\n\n private sweep(): void {\n const now = Date.now();\n for (const [key, entry] of this.store) {\n if (now > entry.expiresAt) {\n this.store.delete(key);\n }\n }\n }\n}\n"],"mappings":"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","names":[]}
|