dauth-md-node 3.0.2 → 4.0.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.
@@ -0,0 +1,333 @@
1
+ import {
2
+ decryptSessionWithKeys,
3
+ deriveEncryptionKey,
4
+ encryptSession,
5
+ getServerBasePath
6
+ } from "./chunk-4A7BR4EM.mjs";
7
+
8
+ // src/router.ts
9
+ import { Router } from "express";
10
+ import jwt from "jsonwebtoken";
11
+
12
+ // src/csrf.ts
13
+ import crypto from "crypto";
14
+ function generateCsrfToken() {
15
+ return crypto.randomBytes(32).toString("hex");
16
+ }
17
+ function verifyCsrf(req, csrfCookieName) {
18
+ const headerToken = req.headers["x-csrf-token"];
19
+ const cookieToken = req.cookies?.[csrfCookieName];
20
+ if (!headerToken || !cookieToken) return false;
21
+ return headerToken === cookieToken;
22
+ }
23
+
24
+ // src/router.ts
25
+ var refreshLocks = /* @__PURE__ */ new Map();
26
+ function lockKey(refreshToken) {
27
+ return refreshToken.substring(0, 16);
28
+ }
29
+ function clearStaleLocks() {
30
+ if (refreshLocks.size > 100) refreshLocks.clear();
31
+ }
32
+ async function resolveConfig(opts) {
33
+ const secure = opts.secure ?? process.env.NODE_ENV !== "development";
34
+ const cookieName = opts.cookieName ?? (secure ? "__Host-dauth-session" : "dauth-session");
35
+ const csrfCookieName = opts.csrfCookieName ?? (secure ? "__Host-csrf" : "csrf-token");
36
+ const maxAgeMs = (opts.maxAge ?? 30 * 24 * 3600) * 1e3;
37
+ const keys = [];
38
+ keys.push(await deriveEncryptionKey(opts.tsk, opts.sessionSalt));
39
+ if (opts.previousTsk) {
40
+ keys.push(
41
+ await deriveEncryptionKey(opts.previousTsk, opts.sessionSalt)
42
+ );
43
+ }
44
+ let dauthBasePath;
45
+ if (opts.dauthUrl) {
46
+ dauthBasePath = `${opts.dauthUrl.replace(/\/+$/, "")}/api/v1`;
47
+ } else {
48
+ dauthBasePath = getServerBasePath();
49
+ }
50
+ return {
51
+ domainName: opts.domainName,
52
+ dauthBasePath,
53
+ cookieName,
54
+ csrfCookieName,
55
+ maxAgeMs,
56
+ secure,
57
+ encKeys: keys
58
+ };
59
+ }
60
+ function setSessionCookie(res, payload, config) {
61
+ const encrypted = encryptSession(payload, config.encKeys[0]);
62
+ const cookieOpts = {
63
+ httpOnly: true,
64
+ secure: config.secure,
65
+ sameSite: "lax",
66
+ maxAge: config.maxAgeMs,
67
+ path: "/"
68
+ };
69
+ if (!config.secure) {
70
+ }
71
+ res.cookie(config.cookieName, encrypted, cookieOpts);
72
+ }
73
+ function setCsrfCookie(res, config) {
74
+ const csrfToken = generateCsrfToken();
75
+ res.cookie(config.csrfCookieName, csrfToken, {
76
+ httpOnly: false,
77
+ secure: config.secure,
78
+ sameSite: "lax",
79
+ maxAge: config.maxAgeMs,
80
+ path: "/"
81
+ });
82
+ }
83
+ function clearCookies(res, config) {
84
+ const baseOpts = { path: "/", secure: config.secure };
85
+ res.clearCookie(config.cookieName, baseOpts);
86
+ res.clearCookie(config.csrfCookieName, baseOpts);
87
+ }
88
+ function readSession(req, config) {
89
+ const cookie = req.cookies?.[config.cookieName];
90
+ if (!cookie) return null;
91
+ return decryptSessionWithKeys(cookie, config.encKeys);
92
+ }
93
+ function isTokenExpiringSoon(token, thresholdMs = 3e5) {
94
+ try {
95
+ const decoded = jwt.decode(token);
96
+ if (!decoded?.exp) return true;
97
+ return decoded.exp * 1e3 - Date.now() < thresholdMs;
98
+ } catch {
99
+ return true;
100
+ }
101
+ }
102
+ async function maybeRefreshTokens(session, config, res) {
103
+ if (!isTokenExpiringSoon(session.accessToken)) return session;
104
+ const key = lockKey(session.refreshToken);
105
+ clearStaleLocks();
106
+ const existingLock = refreshLocks.get(key);
107
+ if (existingLock) {
108
+ const result2 = await existingLock;
109
+ return result2 ?? session;
110
+ }
111
+ const refreshPromise = (async () => {
112
+ try {
113
+ const response = await fetch(
114
+ `${config.dauthBasePath}/app/${config.domainName}/refresh-token`,
115
+ {
116
+ method: "POST",
117
+ headers: { "Content-Type": "application/json" },
118
+ body: JSON.stringify({
119
+ refreshToken: session.refreshToken
120
+ })
121
+ }
122
+ );
123
+ if (!response.ok) return null;
124
+ const data = await response.json();
125
+ if (!data.accessToken || !data.refreshToken) return null;
126
+ const newSession = {
127
+ accessToken: data.accessToken,
128
+ refreshToken: data.refreshToken
129
+ };
130
+ setSessionCookie(res, newSession, config);
131
+ return newSession;
132
+ } catch {
133
+ return null;
134
+ }
135
+ })();
136
+ refreshLocks.set(key, refreshPromise);
137
+ const timeout = setTimeout(() => refreshLocks.delete(key), 1e4);
138
+ refreshPromise.finally(() => {
139
+ clearTimeout(timeout);
140
+ refreshLocks.delete(key);
141
+ });
142
+ const result = await refreshPromise;
143
+ return result ?? session;
144
+ }
145
+ function dauthRouter(opts) {
146
+ const router = Router();
147
+ let configPromise = null;
148
+ async function getConfig() {
149
+ if (!configPromise) configPromise = resolveConfig(opts);
150
+ return configPromise;
151
+ }
152
+ router.post("/exchange-code", async (req, res) => {
153
+ const config = await getConfig();
154
+ const { code } = req.body;
155
+ if (!code) {
156
+ return res.status(400).send({ status: "code-required", message: "Code required" });
157
+ }
158
+ const response = await fetch(
159
+ `${config.dauthBasePath}/app/${config.domainName}/exchange-code`,
160
+ {
161
+ method: "POST",
162
+ headers: { "Content-Type": "application/json" },
163
+ body: JSON.stringify({ code })
164
+ }
165
+ );
166
+ if (!response.ok) {
167
+ return res.status(response.status).send({ status: "code-invalid", message: "Code invalid" });
168
+ }
169
+ const data = await response.json();
170
+ setSessionCookie(
171
+ res,
172
+ {
173
+ accessToken: data.accessToken,
174
+ refreshToken: data.refreshToken
175
+ },
176
+ config
177
+ );
178
+ setCsrfCookie(res, config);
179
+ const userResponse = await fetch(
180
+ `${config.dauthBasePath}/app/${config.domainName}/user`,
181
+ {
182
+ method: "GET",
183
+ headers: { Authorization: data.accessToken }
184
+ }
185
+ );
186
+ const userData = await userResponse.json();
187
+ return res.status(200).send({
188
+ user: userData.user,
189
+ domain: userData.domain,
190
+ isNewUser: data.isNewUser
191
+ });
192
+ });
193
+ router.get("/session", async (req, res) => {
194
+ const config = await getConfig();
195
+ const session = readSession(req, config);
196
+ if (!session) {
197
+ return res.status(401).send({ status: "no-session", message: "Not authenticated" });
198
+ }
199
+ const refreshed = await maybeRefreshTokens(session, config, res);
200
+ const userResponse = await fetch(
201
+ `${config.dauthBasePath}/app/${config.domainName}/user`,
202
+ {
203
+ method: "GET",
204
+ headers: { Authorization: refreshed.accessToken }
205
+ }
206
+ );
207
+ if (!userResponse.ok) {
208
+ clearCookies(res, config);
209
+ return res.status(401).send({ status: "session-invalid", message: "Session expired" });
210
+ }
211
+ const userData = await userResponse.json();
212
+ return res.status(200).send({
213
+ user: userData.user,
214
+ domain: userData.domain
215
+ });
216
+ });
217
+ router.post("/logout", async (req, res) => {
218
+ const config = await getConfig();
219
+ if (!verifyCsrf(req, config.csrfCookieName)) {
220
+ return res.status(403).send({ status: "csrf-invalid", message: "CSRF token invalid" });
221
+ }
222
+ const session = readSession(req, config);
223
+ if (session) {
224
+ fetch(
225
+ `${config.dauthBasePath}/app/${config.domainName}/logout`,
226
+ {
227
+ method: "POST",
228
+ headers: { "Content-Type": "application/json" },
229
+ body: JSON.stringify({
230
+ refreshToken: session.refreshToken
231
+ })
232
+ }
233
+ ).catch(() => {
234
+ });
235
+ }
236
+ clearCookies(res, config);
237
+ return res.status(200).send({ status: "success", message: "Logged out" });
238
+ });
239
+ router.patch("/user", async (req, res) => {
240
+ const config = await getConfig();
241
+ if (!verifyCsrf(req, config.csrfCookieName)) {
242
+ return res.status(403).send({ status: "csrf-invalid", message: "CSRF token invalid" });
243
+ }
244
+ const session = readSession(req, config);
245
+ if (!session) {
246
+ return res.status(401).send({ status: "no-session", message: "Not authenticated" });
247
+ }
248
+ const refreshed = await maybeRefreshTokens(session, config, res);
249
+ const response = await fetch(
250
+ `${config.dauthBasePath}/app/${config.domainName}/user`,
251
+ {
252
+ method: "PATCH",
253
+ headers: {
254
+ "Content-Type": "application/json",
255
+ Authorization: refreshed.accessToken
256
+ },
257
+ body: JSON.stringify(req.body)
258
+ }
259
+ );
260
+ const data = await response.json();
261
+ return res.status(response.status).send(data);
262
+ });
263
+ router.delete("/user", async (req, res) => {
264
+ const config = await getConfig();
265
+ if (!verifyCsrf(req, config.csrfCookieName)) {
266
+ return res.status(403).send({ status: "csrf-invalid", message: "CSRF token invalid" });
267
+ }
268
+ const session = readSession(req, config);
269
+ if (!session) {
270
+ return res.status(401).send({ status: "no-session", message: "Not authenticated" });
271
+ }
272
+ const response = await fetch(
273
+ `${config.dauthBasePath}/app/${config.domainName}/user`,
274
+ {
275
+ method: "DELETE",
276
+ headers: { Authorization: session.accessToken }
277
+ }
278
+ );
279
+ const data = await response.json();
280
+ clearCookies(res, config);
281
+ return res.status(response.status).send(data);
282
+ });
283
+ router.get(
284
+ "/profile-redirect",
285
+ async (req, res) => {
286
+ const config = await getConfig();
287
+ if (!verifyCsrf(req, config.csrfCookieName)) {
288
+ return res.status(403).send({
289
+ status: "csrf-invalid",
290
+ message: "CSRF token invalid"
291
+ });
292
+ }
293
+ const session = readSession(req, config);
294
+ if (!session) {
295
+ return res.status(401).send({
296
+ status: "no-session",
297
+ message: "Not authenticated"
298
+ });
299
+ }
300
+ const refreshed = await maybeRefreshTokens(
301
+ session,
302
+ config,
303
+ res
304
+ );
305
+ const response = await fetch(
306
+ `${config.dauthBasePath}/app/${config.domainName}/profile-code`,
307
+ {
308
+ method: "POST",
309
+ headers: {
310
+ "Content-Type": "application/json",
311
+ Authorization: refreshed.accessToken
312
+ }
313
+ }
314
+ );
315
+ if (!response.ok) {
316
+ return res.status(response.status).send({
317
+ status: "profile-code-error",
318
+ message: "Could not generate profile code"
319
+ });
320
+ }
321
+ const data = await response.json();
322
+ const dauthFrontendUrl = opts.dauthUrl ? opts.dauthUrl.replace(/\/+$/, "") : process.env.DAUTH_URL ? process.env.DAUTH_URL.replace(/\/+$/, "") : process.env.NODE_ENV === "development" ? "http://localhost:5185" : "https://dauth.ovh";
323
+ return res.status(200).send({
324
+ redirectUrl: `${dauthFrontendUrl}/${config.domainName}/update-user?code=${data.code}`
325
+ });
326
+ }
327
+ );
328
+ return router;
329
+ }
330
+ export {
331
+ dauthRouter
332
+ };
333
+ //# sourceMappingURL=router.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/router.ts","../src/csrf.ts"],"sourcesContent":["import { Router, Request, Response } from 'express';\nimport jwt from 'jsonwebtoken';\nimport { getServerBasePath } from './api/utils/config';\nimport {\n deriveEncryptionKey,\n encryptSession,\n decryptSessionWithKeys,\n SessionPayload,\n} from './session';\nimport { generateCsrfToken, verifyCsrf } from './csrf';\n\nexport interface DauthRouterOptions {\n domainName: string;\n tsk: string;\n dauthUrl?: string;\n cookieName?: string;\n csrfCookieName?: string;\n maxAge?: number;\n secure?: boolean;\n previousTsk?: string;\n sessionSalt?: string;\n}\n\ninterface ResolvedConfig {\n domainName: string;\n dauthBasePath: string;\n cookieName: string;\n csrfCookieName: string;\n maxAgeMs: number;\n secure: boolean;\n encKeys: Buffer[];\n}\n\n// Refresh lock to prevent race conditions on concurrent token rotation\nconst refreshLocks = new Map<string, Promise<SessionPayload | null>>();\n\nfunction lockKey(refreshToken: string): string {\n return refreshToken.substring(0, 16);\n}\n\nfunction clearStaleLocks(): void {\n if (refreshLocks.size > 100) refreshLocks.clear();\n}\n\nasync function resolveConfig(\n opts: DauthRouterOptions\n): Promise<ResolvedConfig> {\n const secure =\n opts.secure ?? process.env.NODE_ENV !== 'development';\n const cookieName =\n opts.cookieName ??\n (secure ? '__Host-dauth-session' : 'dauth-session');\n const csrfCookieName =\n opts.csrfCookieName ?? (secure ? '__Host-csrf' : 'csrf-token');\n const maxAgeMs = (opts.maxAge ?? 30 * 24 * 3600) * 1000;\n\n const keys: Buffer[] = [];\n keys.push(await deriveEncryptionKey(opts.tsk, opts.sessionSalt));\n if (opts.previousTsk) {\n keys.push(\n await deriveEncryptionKey(opts.previousTsk, opts.sessionSalt)\n );\n }\n\n let dauthBasePath: string;\n if (opts.dauthUrl) {\n dauthBasePath = `${opts.dauthUrl.replace(/\\/+$/, '')}/api/v1`;\n } else {\n dauthBasePath = getServerBasePath();\n }\n\n return {\n domainName: opts.domainName,\n dauthBasePath,\n cookieName,\n csrfCookieName,\n maxAgeMs,\n secure,\n encKeys: keys,\n };\n}\n\nfunction setSessionCookie(\n res: Response,\n payload: SessionPayload,\n config: ResolvedConfig\n): void {\n const encrypted = encryptSession(payload, config.encKeys[0]);\n const cookieOpts: Record<string, unknown> = {\n httpOnly: true,\n secure: config.secure,\n sameSite: 'lax',\n maxAge: config.maxAgeMs,\n path: '/',\n };\n // __Host- prefix requires no domain attribute\n if (!config.secure) {\n // Dev mode: no __Host- prefix, no domain restriction needed\n }\n res.cookie(config.cookieName, encrypted, cookieOpts);\n}\n\nfunction setCsrfCookie(res: Response, config: ResolvedConfig): void {\n const csrfToken = generateCsrfToken();\n res.cookie(config.csrfCookieName, csrfToken, {\n httpOnly: false,\n secure: config.secure,\n sameSite: 'lax',\n maxAge: config.maxAgeMs,\n path: '/',\n });\n}\n\nfunction clearCookies(res: Response, config: ResolvedConfig): void {\n const baseOpts = { path: '/', secure: config.secure };\n res.clearCookie(config.cookieName, baseOpts);\n res.clearCookie(config.csrfCookieName, baseOpts);\n}\n\nfunction readSession(\n req: Request,\n config: ResolvedConfig\n): SessionPayload | null {\n const cookie = req.cookies?.[config.cookieName];\n if (!cookie) return null;\n return decryptSessionWithKeys(cookie, config.encKeys);\n}\n\nfunction isTokenExpiringSoon(token: string, thresholdMs = 300_000): boolean {\n try {\n const decoded = jwt.decode(token) as { exp?: number } | null;\n if (!decoded?.exp) return true;\n return decoded.exp * 1000 - Date.now() < thresholdMs;\n } catch {\n return true;\n }\n}\n\nasync function maybeRefreshTokens(\n session: SessionPayload,\n config: ResolvedConfig,\n res: Response\n): Promise<SessionPayload> {\n if (!isTokenExpiringSoon(session.accessToken)) return session;\n\n const key = lockKey(session.refreshToken);\n clearStaleLocks();\n\n const existingLock = refreshLocks.get(key);\n if (existingLock) {\n const result = await existingLock;\n return result ?? session;\n }\n\n const refreshPromise = (async (): Promise<SessionPayload | null> => {\n try {\n const response = await fetch(\n `${config.dauthBasePath}/app/${config.domainName}/refresh-token`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n refreshToken: session.refreshToken,\n }),\n }\n );\n if (!response.ok) return null;\n const data = (await response.json()) as {\n accessToken?: string;\n refreshToken?: string;\n };\n if (!data.accessToken || !data.refreshToken) return null;\n const newSession: SessionPayload = {\n accessToken: data.accessToken,\n refreshToken: data.refreshToken,\n };\n setSessionCookie(res, newSession, config);\n return newSession;\n } catch {\n return null;\n }\n })();\n\n refreshLocks.set(key, refreshPromise);\n\n // Timeout safety net: clean lock after 10s\n const timeout = setTimeout(() => refreshLocks.delete(key), 10_000);\n refreshPromise.finally(() => {\n clearTimeout(timeout);\n refreshLocks.delete(key);\n });\n\n const result = await refreshPromise;\n return result ?? session;\n}\n\nexport function dauthRouter(opts: DauthRouterOptions): Router {\n const router = Router();\n let configPromise: Promise<ResolvedConfig> | null = null;\n\n async function getConfig(): Promise<ResolvedConfig> {\n if (!configPromise) configPromise = resolveConfig(opts);\n return configPromise;\n }\n\n // POST /exchange-code — no CSRF (no prior session)\n router.post('/exchange-code', async (req: Request, res: Response) => {\n const config = await getConfig();\n const { code } = req.body;\n if (!code) {\n return res\n .status(400)\n .send({ status: 'code-required', message: 'Code required' });\n }\n\n const response = await fetch(\n `${config.dauthBasePath}/app/${config.domainName}/exchange-code`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ code }),\n }\n );\n if (!response.ok) {\n return res\n .status(response.status)\n .send({ status: 'code-invalid', message: 'Code invalid' });\n }\n const data = (await response.json()) as {\n accessToken: string;\n refreshToken: string;\n isNewUser: boolean;\n };\n\n setSessionCookie(\n res,\n {\n accessToken: data.accessToken,\n refreshToken: data.refreshToken,\n },\n config\n );\n setCsrfCookie(res, config);\n\n // Fetch user data to return\n const userResponse = await fetch(\n `${config.dauthBasePath}/app/${config.domainName}/user`,\n {\n method: 'GET',\n headers: { Authorization: data.accessToken },\n }\n );\n const userData = (await userResponse.json()) as {\n user?: unknown;\n domain?: unknown;\n };\n\n return res.status(200).send({\n user: userData.user,\n domain: userData.domain,\n isNewUser: data.isNewUser,\n });\n });\n\n // GET /session — no CSRF (read-only)\n router.get('/session', async (req: Request, res: Response) => {\n const config = await getConfig();\n const session = readSession(req, config);\n if (!session) {\n return res\n .status(401)\n .send({ status: 'no-session', message: 'Not authenticated' });\n }\n\n const refreshed = await maybeRefreshTokens(session, config, res);\n\n const userResponse = await fetch(\n `${config.dauthBasePath}/app/${config.domainName}/user`,\n {\n method: 'GET',\n headers: { Authorization: refreshed.accessToken },\n }\n );\n if (!userResponse.ok) {\n clearCookies(res, config);\n return res\n .status(401)\n .send({ status: 'session-invalid', message: 'Session expired' });\n }\n const userData = (await userResponse.json()) as {\n user?: unknown;\n domain?: unknown;\n };\n return res.status(200).send({\n user: userData.user,\n domain: userData.domain,\n });\n });\n\n // POST /logout — CSRF required\n router.post('/logout', async (req: Request, res: Response) => {\n const config = await getConfig();\n if (!verifyCsrf(req, config.csrfCookieName)) {\n return res\n .status(403)\n .send({ status: 'csrf-invalid', message: 'CSRF token invalid' });\n }\n const session = readSession(req, config);\n if (session) {\n // Revoke refresh token server-to-server (fire-and-forget)\n fetch(\n `${config.dauthBasePath}/app/${config.domainName}/logout`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n refreshToken: session.refreshToken,\n }),\n }\n ).catch(() => {});\n }\n clearCookies(res, config);\n return res\n .status(200)\n .send({ status: 'success', message: 'Logged out' });\n });\n\n // PATCH /user — CSRF required\n router.patch('/user', async (req: Request, res: Response) => {\n const config = await getConfig();\n if (!verifyCsrf(req, config.csrfCookieName)) {\n return res\n .status(403)\n .send({ status: 'csrf-invalid', message: 'CSRF token invalid' });\n }\n const session = readSession(req, config);\n if (!session) {\n return res\n .status(401)\n .send({ status: 'no-session', message: 'Not authenticated' });\n }\n const refreshed = await maybeRefreshTokens(session, config, res);\n\n const response = await fetch(\n `${config.dauthBasePath}/app/${config.domainName}/user`,\n {\n method: 'PATCH',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: refreshed.accessToken,\n },\n body: JSON.stringify(req.body),\n }\n );\n const data = await response.json();\n return res.status(response.status).send(data);\n });\n\n // DELETE /user — CSRF required\n router.delete('/user', async (req: Request, res: Response) => {\n const config = await getConfig();\n if (!verifyCsrf(req, config.csrfCookieName)) {\n return res\n .status(403)\n .send({ status: 'csrf-invalid', message: 'CSRF token invalid' });\n }\n const session = readSession(req, config);\n if (!session) {\n return res\n .status(401)\n .send({ status: 'no-session', message: 'Not authenticated' });\n }\n\n const response = await fetch(\n `${config.dauthBasePath}/app/${config.domainName}/user`,\n {\n method: 'DELETE',\n headers: { Authorization: session.accessToken },\n }\n );\n const data = await response.json();\n clearCookies(res, config);\n return res.status(response.status).send(data);\n });\n\n // GET /profile-redirect — CSRF required (generates profile code)\n router.get(\n '/profile-redirect',\n async (req: Request, res: Response) => {\n const config = await getConfig();\n if (!verifyCsrf(req, config.csrfCookieName)) {\n return res.status(403).send({\n status: 'csrf-invalid',\n message: 'CSRF token invalid',\n });\n }\n const session = readSession(req, config);\n if (!session) {\n return res.status(401).send({\n status: 'no-session',\n message: 'Not authenticated',\n });\n }\n const refreshed = await maybeRefreshTokens(\n session,\n config,\n res\n );\n\n const response = await fetch(\n `${config.dauthBasePath}/app/${config.domainName}/profile-code`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: refreshed.accessToken,\n },\n }\n );\n if (!response.ok) {\n return res.status(response.status).send({\n status: 'profile-code-error',\n message: 'Could not generate profile code',\n });\n }\n const data = (await response.json()) as { code: string };\n\n // Build redirect URL to dauth frontend\n const dauthFrontendUrl = opts.dauthUrl\n ? opts.dauthUrl.replace(/\\/+$/, '')\n : process.env.DAUTH_URL\n ? process.env.DAUTH_URL.replace(/\\/+$/, '')\n : process.env.NODE_ENV === 'development'\n ? 'http://localhost:5185'\n : 'https://dauth.ovh';\n\n return res.status(200).send({\n redirectUrl: `${dauthFrontendUrl}/${config.domainName}/update-user?code=${data.code}`,\n });\n }\n );\n\n return router;\n}\n","import crypto from 'crypto';\nimport type { Request } from 'express';\n\nexport function generateCsrfToken(): string {\n return crypto.randomBytes(32).toString('hex');\n}\n\nexport function verifyCsrf(\n req: Request,\n csrfCookieName: string\n): boolean {\n const headerToken = req.headers['x-csrf-token'] as string | undefined;\n const cookieToken = req.cookies?.[csrfCookieName] as\n | string\n | undefined;\n if (!headerToken || !cookieToken) return false;\n return headerToken === cookieToken;\n}\n"],"mappings":"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","names":["result"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dauth-md-node",
3
- "version": "3.0.2",
3
+ "version": "4.0.0",
4
4
  "license": "MIT",
5
5
  "author": "David T. Pizarro Frick",
6
6
  "main": "dist/index.js",
@@ -11,6 +11,11 @@
11
11
  "types": "./dist/index.d.ts",
12
12
  "import": "./dist/index.mjs",
13
13
  "require": "./dist/index.js"
14
+ },
15
+ "./router": {
16
+ "types": "./dist/router.d.ts",
17
+ "import": "./dist/router.mjs",
18
+ "require": "./dist/router.js"
14
19
  }
15
20
  },
16
21
  "files": [
@@ -45,10 +50,24 @@
45
50
  {
46
51
  "path": "dist/index.mjs",
47
52
  "limit": "10 KB"
53
+ },
54
+ {
55
+ "path": "dist/router.js",
56
+ "limit": "10 KB"
57
+ },
58
+ {
59
+ "path": "dist/router.mjs",
60
+ "limit": "10 KB"
48
61
  }
49
62
  ],
50
63
  "peerDependencies": {
51
- "express": "^4.18.0 || ^5.0.0"
64
+ "express": "^4.18.0 || ^5.0.0",
65
+ "cookie-parser": "^1.4.0"
66
+ },
67
+ "peerDependenciesMeta": {
68
+ "cookie-parser": {
69
+ "optional": true
70
+ }
52
71
  },
53
72
  "dependencies": {
54
73
  "jsonwebtoken": "^9.0.3"
@@ -63,6 +82,7 @@
63
82
  "@types/express": "^5.0.0",
64
83
  "@types/jsonwebtoken": "^9.0.10",
65
84
  "@types/node": "~22.19.0",
85
+ "@vitest/coverage-v8": "^4.0.18",
66
86
  "express": "^5.0.0",
67
87
  "husky": "^9.1.7",
68
88
  "prettier": "^3.8.1",
package/src/csrf.ts ADDED
@@ -0,0 +1,18 @@
1
+ import crypto from 'crypto';
2
+ import type { Request } from 'express';
3
+
4
+ export function generateCsrfToken(): string {
5
+ return crypto.randomBytes(32).toString('hex');
6
+ }
7
+
8
+ export function verifyCsrf(
9
+ req: Request,
10
+ csrfCookieName: string
11
+ ): boolean {
12
+ const headerToken = req.headers['x-csrf-token'] as string | undefined;
13
+ const cookieToken = req.cookies?.[csrfCookieName] as
14
+ | string
15
+ | undefined;
16
+ if (!headerToken || !cookieToken) return false;
17
+ return headerToken === cookieToken;
18
+ }
package/src/index.ts CHANGED
@@ -3,6 +3,7 @@ import jwt from 'jsonwebtoken';
3
3
  import { getUser } from './api/dauth.api';
4
4
  import { UserCache } from './cache';
5
5
  import type { CacheOptions } from './cache';
6
+ import { deriveEncryptionKey, decryptSessionWithKeys } from './session';
6
7
 
7
8
  export type AuthMethodType = 'magic-link' | 'passkey';
8
9
 
@@ -41,10 +42,18 @@ export interface IRequestDauth extends Request {
41
42
  };
42
43
  }
43
44
 
45
+ export interface SessionOptions {
46
+ cookieName?: string;
47
+ secure?: boolean;
48
+ previousTsk?: string;
49
+ sessionSalt?: string;
50
+ }
51
+
44
52
  export interface DauthOptions {
45
53
  domainName: string;
46
54
  tsk: string;
47
55
  cache?: CacheOptions;
56
+ session?: SessionOptions;
48
57
  }
49
58
 
50
59
  interface TCustomResponse extends ExpressResponse {
@@ -55,26 +64,92 @@ interface TCustomResponse extends ExpressResponse {
55
64
  export { UserCache };
56
65
  export type { CacheOptions };
57
66
 
58
- export const dauth = ({ domainName, tsk, cache }: DauthOptions) => {
67
+ export const dauth = ({
68
+ domainName,
69
+ tsk,
70
+ cache,
71
+ session,
72
+ }: DauthOptions) => {
59
73
  const userCache = cache ? new UserCache(cache) : null;
60
74
 
75
+ // Lazy-init encryption keys for session cookie mode
76
+ let keysPromise: Promise<Buffer[]> | null = null;
77
+ async function getEncKeys(): Promise<Buffer[]> {
78
+ if (!keysPromise) {
79
+ keysPromise = (async () => {
80
+ const keys: Buffer[] = [];
81
+ keys.push(
82
+ await deriveEncryptionKey(tsk, session?.sessionSalt)
83
+ );
84
+ if (session?.previousTsk) {
85
+ keys.push(
86
+ await deriveEncryptionKey(
87
+ session.previousTsk,
88
+ session.sessionSalt
89
+ )
90
+ );
91
+ }
92
+ return keys;
93
+ })();
94
+ }
95
+ return keysPromise;
96
+ }
97
+
98
+ function getSessionCookieName(): string {
99
+ if (session?.cookieName) return session.cookieName;
100
+ const secure =
101
+ session?.secure ?? process.env.NODE_ENV !== 'development';
102
+ return secure ? '__Host-dauth-session' : 'dauth-session';
103
+ }
104
+
61
105
  return async (
62
106
  req: IRequestDauth,
63
107
  res: TCustomResponse,
64
108
  next: NextFunction
65
109
  ) => {
66
- if (!req.headers.authorization) {
67
- return res
68
- .status(403)
69
- .send({ status: 'token-not-found', message: 'Token not found' });
70
- }
110
+ let token: string;
71
111
 
72
- const token = req.headers.authorization.replace(/['"]+/g, '');
112
+ if (session) {
113
+ // Session cookie mode: read encrypted cookie
114
+ const cookieName = getSessionCookieName();
115
+ const cookie = req.cookies?.[cookieName];
116
+ if (!cookie) {
117
+ return res
118
+ .status(401)
119
+ .send({
120
+ status: 'no-session',
121
+ message: 'Not authenticated',
122
+ });
123
+ }
124
+ const keys = await getEncKeys();
125
+ const payload = decryptSessionWithKeys(cookie, keys);
126
+ if (!payload) {
127
+ return res
128
+ .status(401)
129
+ .send({
130
+ status: 'session-invalid',
131
+ message: 'Invalid session',
132
+ });
133
+ }
134
+ token = payload.accessToken;
135
+ } else {
136
+ // Authorization header mode
137
+ if (!req.headers.authorization) {
138
+ return res
139
+ .status(403)
140
+ .send({
141
+ status: 'token-not-found',
142
+ message: 'Token not found',
143
+ });
144
+ }
145
+ token = req.headers.authorization.replace(/['"]+/g, '');
146
+ }
73
147
 
74
148
  try {
75
149
  jwt.verify(token, tsk);
76
150
  } catch (error) {
77
- const message = error instanceof Error ? error.message : 'Token invalid';
151
+ const message =
152
+ error instanceof Error ? error.message : 'Token invalid';
78
153
 
79
154
  if (message === 'jwt expired') {
80
155
  return res
@@ -84,10 +159,13 @@ export const dauth = ({ domainName, tsk, cache }: DauthOptions) => {
84
159
  if (message === 'invalid signature') {
85
160
  return res.status(401).send({
86
161
  status: 'tsk-not-invalid',
87
- message: 'The TSK variable in the backend middleware is not valid',
162
+ message:
163
+ 'The TSK variable in the backend middleware is not valid',
88
164
  });
89
165
  }
90
- return res.status(401).send({ status: 'token-invalid', message });
166
+ return res
167
+ .status(401)
168
+ .send({ status: 'token-invalid', message });
91
169
  }
92
170
 
93
171
  if (userCache) {
@@ -104,13 +182,15 @@ export const dauth = ({ domainName, tsk, cache }: DauthOptions) => {
104
182
  if (getUserFetch.response.status === 404) {
105
183
  return res.status(404).send({
106
184
  status: 'user-not-found',
107
- message: getUserFetch.data.message ?? 'User does not exist',
185
+ message:
186
+ getUserFetch.data.message ?? 'User does not exist',
108
187
  });
109
188
  }
110
189
  if (getUserFetch.response.status === 500) {
111
190
  return res.status(500).send({
112
191
  status: 'error',
113
- message: getUserFetch.data.message ?? 'Dauth server error',
192
+ message:
193
+ getUserFetch.data.message ?? 'Dauth server error',
114
194
  });
115
195
  }
116
196
  if (getUserFetch.response.status === 200) {
@@ -122,12 +202,17 @@ export const dauth = ({ domainName, tsk, cache }: DauthOptions) => {
122
202
  }
123
203
  return res.status(501).send({
124
204
  status: 'request-error',
125
- message: getUserFetch.data.message ?? 'Dauth server error',
205
+ message:
206
+ getUserFetch.data.message ?? 'Dauth server error',
126
207
  });
127
208
  } catch (error) {
128
209
  const message =
129
- error instanceof Error ? error.message : 'Dauth server error';
130
- return res.status(500).send({ status: 'server-error', message });
210
+ error instanceof Error
211
+ ? error.message
212
+ : 'Dauth server error';
213
+ return res
214
+ .status(500)
215
+ .send({ status: 'server-error', message });
131
216
  }
132
217
  };
133
218
  };