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/package.json CHANGED
@@ -1,63 +1,67 @@
1
1
  {
2
- "version": "1.0.1",
2
+ "name": "dauth-md-node",
3
+ "version": "2.1.0",
3
4
  "license": "MIT",
5
+ "author": "David T. Pizarro Frick",
4
6
  "main": "dist/index.js",
7
+ "module": "dist/index.mjs",
5
8
  "typings": "dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.mjs",
13
+ "require": "./dist/index.js"
14
+ }
15
+ },
6
16
  "files": [
7
17
  "dist",
8
18
  "src"
9
19
  ],
10
20
  "engines": {
11
- "node": ">=10"
21
+ "node": ">=18"
12
22
  },
13
23
  "scripts": {
14
- "start": "tsdx watch",
15
- "build": "tsdx build",
16
- "test": "tsdx test",
17
- "lint": "tsdx lint",
24
+ "start": "tsup --watch",
25
+ "build": "tsup",
26
+ "test": "vitest run",
27
+ "typecheck": "tsc --noEmit",
18
28
  "format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,json,md}\"",
19
- "prepare": "tsdx build",
29
+ "prepare": "tsup",
20
30
  "size": "size-limit",
21
31
  "analyze": "size-limit --why"
22
32
  },
23
- "husky": {
24
- "hooks": {
25
- "pre-commit": "tsdx lint"
26
- }
27
- },
28
33
  "prettier": {
29
34
  "printWidth": 80,
30
35
  "semi": true,
31
36
  "singleQuote": true,
32
37
  "trailingComma": "es5"
33
38
  },
34
- "name": "dauth-md-node",
35
- "author": "David T. Pizarro Frick",
36
- "module": "dist/dauth-md-node.esm.js",
39
+ "packageManager": "pnpm@10.30.3",
37
40
  "size-limit": [
38
41
  {
39
- "path": "dist/dauth-md-node.cjs.production.min.js",
42
+ "path": "dist/index.js",
40
43
  "limit": "10 KB"
41
44
  },
42
45
  {
43
- "path": "dist/dauth-md-node.esm.js",
46
+ "path": "dist/index.mjs",
44
47
  "limit": "10 KB"
45
48
  }
46
49
  ],
50
+ "peerDependencies": {
51
+ "express": "^4.18.0 || ^5.0.0"
52
+ },
53
+ "dependencies": {
54
+ "jsonwebtoken": "^9.0.2"
55
+ },
47
56
  "devDependencies": {
48
- "@size-limit/preset-small-lib": "^11.0.2",
57
+ "@size-limit/file": "^11.0.2",
49
58
  "@types/express": "^4.17.21",
50
59
  "@types/jsonwebtoken": "^9.0.5",
51
- "@types/node-fetch": "^2.6.11",
60
+ "@types/node": "~22.9.4",
52
61
  "husky": "^9.0.11",
53
62
  "size-limit": "^11.0.2",
54
- "tsdx": "^0.14.1",
55
- "tslib": "^2.6.2",
56
- "typescript": "^3.9.10"
57
- },
58
- "dependencies": {
59
- "express": "^4.18.2",
60
- "jsonwebtoken": "^9.0.2",
61
- "node-fetch": "^2.6.1"
63
+ "tsup": "~8.5.0",
64
+ "typescript": "~5.9.3",
65
+ "vitest": "~3.1.0"
62
66
  }
63
67
  }
@@ -1,18 +1,24 @@
1
1
  import { getServerBasePath } from './utils/config';
2
- const fetch = require('node-fetch');
3
2
 
4
- export async function getUser(token: string, domainName: string) {
5
- const params = {
6
- method: 'GET',
7
- headers: {
8
- Authorization: token,
9
- 'Content-Type': 'application/json',
10
- },
11
- };
3
+ interface GetUserResponse {
4
+ response: { status: number };
5
+ data: { user?: any; message?: string };
6
+ }
7
+
8
+ export async function getUser(
9
+ token: string,
10
+ domainName: string
11
+ ): Promise<GetUserResponse> {
12
12
  const response = await fetch(
13
13
  `${getServerBasePath()}/app/${domainName}/user`,
14
- params
14
+ {
15
+ method: 'GET',
16
+ headers: {
17
+ Authorization: token,
18
+ 'Content-Type': 'application/json',
19
+ },
20
+ }
15
21
  );
16
- const data = await response.json();
17
- return { response, data };
22
+ const data = (await response.json()) as GetUserResponse['data'];
23
+ return { response: { status: response.status }, data };
18
24
  }
@@ -1,7 +1,12 @@
1
1
  export const apiVersion = 'v1';
2
2
  export const serverDomain = 'dauth.ovh';
3
3
 
4
- export function getServerBasePath() {
4
+ export function getServerBasePath(): string {
5
+ if (process.env.DAUTH_URL) {
6
+ const base = process.env.DAUTH_URL.replace(/\/+$/, '');
7
+ return `${base}/api/${apiVersion}`;
8
+ }
9
+
5
10
  const isLocalhost = process.env.NODE_ENV === 'development';
6
11
  const serverPort = 4012;
7
12
  const serverLocalUrl = `http://localhost:${serverPort}/api/${apiVersion}`;
package/src/cache.ts ADDED
@@ -0,0 +1,51 @@
1
+ import { IDauthUser } from './index';
2
+
3
+ interface CacheEntry {
4
+ user: IDauthUser;
5
+ expiresAt: number;
6
+ }
7
+
8
+ export interface CacheOptions {
9
+ ttlMs: number;
10
+ }
11
+
12
+ export class UserCache {
13
+ private store = new Map<string, CacheEntry>();
14
+ private ttlMs: number;
15
+
16
+ constructor(options: CacheOptions) {
17
+ this.ttlMs = options.ttlMs;
18
+ }
19
+
20
+ get(token: string): IDauthUser | undefined {
21
+ const entry = this.store.get(token);
22
+ if (!entry) return undefined;
23
+
24
+ if (Date.now() > entry.expiresAt) {
25
+ this.store.delete(token);
26
+ return undefined;
27
+ }
28
+
29
+ return entry.user;
30
+ }
31
+
32
+ set(token: string, user: IDauthUser): void {
33
+ if (this.store.size > 1000) {
34
+ this.sweep();
35
+ }
36
+ this.store.set(token, { user, expiresAt: Date.now() + this.ttlMs });
37
+ }
38
+
39
+ clear(): void {
40
+ this.store.clear();
41
+ }
42
+
43
+ private sweep(): void {
44
+ const now = Date.now();
45
+ for (const [key, entry] of this.store) {
46
+ if (now > entry.expiresAt) {
47
+ this.store.delete(key);
48
+ }
49
+ }
50
+ }
51
+ }
package/src/index.ts CHANGED
@@ -1,11 +1,10 @@
1
- import {
2
- Request,
3
- NextFunction,
4
- Response as ExpressResponse,
5
- Handler,
6
- } from 'express';
1
+ import { Request, NextFunction, Response as ExpressResponse } from 'express';
7
2
  import jwt from 'jsonwebtoken';
8
3
  import { getUser } from './api/dauth.api';
4
+ import { UserCache } from './cache';
5
+ import type { CacheOptions } from './cache';
6
+
7
+ export type AuthMethodType = 'password' | 'magic-link' | 'passkey';
9
8
 
10
9
  export interface IDauthUser {
11
10
  _id: string;
@@ -22,6 +21,10 @@ export interface IDauthUser {
22
21
  role: string;
23
22
  telPrefix: string;
24
23
  telSuffix: string;
24
+ birthDate?: string;
25
+ country?: string;
26
+ metadata?: Record<string, unknown>;
27
+ authMethods?: AuthMethodType[];
25
28
  createdAt: Date;
26
29
  updatedAt: Date;
27
30
  lastLogin: Date;
@@ -38,76 +41,99 @@ export interface IRequestDauth extends Request {
38
41
  };
39
42
  }
40
43
 
44
+ export interface DauthOptions {
45
+ domainName: string;
46
+ tsk: string;
47
+ cache?: CacheOptions;
48
+ }
49
+
41
50
  interface TCustomResponse extends ExpressResponse {
42
- status(code: number): any;
43
- send(body?: any): any;
51
+ status(code: number): this;
52
+ send(body?: unknown): this;
44
53
  }
45
54
 
46
- export const dauth = ({
47
- domainName,
48
- tsk,
49
- }: {
50
- domainName: string;
51
- tsk: string;
52
- }) => {
53
- return (
55
+ export { UserCache };
56
+ export type { CacheOptions };
57
+
58
+ export const dauth = ({ domainName, tsk, cache }: DauthOptions) => {
59
+ const userCache = cache ? new UserCache(cache) : null;
60
+
61
+ return async (
54
62
  req: IRequestDauth,
55
63
  res: TCustomResponse,
56
64
  next: NextFunction
57
- ): Handler | void => {
65
+ ) => {
58
66
  if (!req.headers.authorization) {
59
67
  return res
60
68
  .status(403)
61
69
  .send({ status: 'token-not-found', message: 'Token not found' });
62
70
  }
71
+
63
72
  const token = req.headers.authorization.replace(/['"]+/g, '');
73
+
64
74
  try {
65
- jwt.verify(token, tsk as string);
66
- return getUser(token, domainName)
67
- .then((getUserFetch: any) => {
68
- if (getUserFetch.response.status === 404) {
69
- return res.status(404).send({
70
- status: 'user-not-found',
71
- message: getUserFetch.data.message ?? 'User does not exist',
72
- });
73
- } else if (getUserFetch.response.status === 500) {
74
- return res.status(500).send({
75
- status: 'error',
76
- message: getUserFetch.data.message ?? 'Dauth server error',
77
- });
78
- } else if (getUserFetch.response.status === 200) {
79
- req.user = getUserFetch.data.user;
80
- return next();
81
- } else {
82
- return res.status(501).send({
83
- status: 'request-error',
84
- message: getUserFetch.data.message ?? 'Dauth server error',
85
- });
86
- }
87
- })
88
- .catch((error: any) => {
89
- return res.status(500).send({
90
- status: 'server-error',
91
- message: error.message ?? 'Dauth server error',
92
- });
93
- });
75
+ jwt.verify(token, tsk);
94
76
  } catch (error) {
95
- if (error && error?.message === 'jwt expired') {
96
- return res.status(401).send({
97
- status: 'token-expired',
98
- message: error?.message ?? 'Token expired',
99
- });
77
+ const message =
78
+ error instanceof Error ? error.message : 'Token invalid';
79
+
80
+ if (message === 'jwt expired') {
81
+ return res
82
+ .status(401)
83
+ .send({ status: 'token-expired', message: 'jwt expired' });
100
84
  }
101
- if (error && error?.message === 'invalid signature') {
85
+ if (message === 'invalid signature') {
102
86
  return res.status(401).send({
103
87
  status: 'tsk-not-invalid',
104
- message: 'The TSK variable in the backend middleware is not valid',
88
+ message:
89
+ 'The TSK variable in the backend middleware is not valid',
105
90
  });
106
91
  }
107
- return res.status(401).send({
108
- status: 'token-invalid',
109
- message: error?.message ?? 'Token invalid',
92
+ return res
93
+ .status(401)
94
+ .send({ status: 'token-invalid', message });
95
+ }
96
+
97
+ if (userCache) {
98
+ const cachedUser = userCache.get(token);
99
+ if (cachedUser) {
100
+ req.user = cachedUser;
101
+ return next();
102
+ }
103
+ }
104
+
105
+ try {
106
+ const getUserFetch = await getUser(token, domainName);
107
+
108
+ if (getUserFetch.response.status === 404) {
109
+ return res.status(404).send({
110
+ status: 'user-not-found',
111
+ message: getUserFetch.data.message ?? 'User does not exist',
112
+ });
113
+ }
114
+ if (getUserFetch.response.status === 500) {
115
+ return res.status(500).send({
116
+ status: 'error',
117
+ message: getUserFetch.data.message ?? 'Dauth server error',
118
+ });
119
+ }
120
+ if (getUserFetch.response.status === 200) {
121
+ req.user = getUserFetch.data.user;
122
+ if (userCache) {
123
+ userCache.set(token, req.user);
124
+ }
125
+ return next();
126
+ }
127
+ return res.status(501).send({
128
+ status: 'request-error',
129
+ message: getUserFetch.data.message ?? 'Dauth server error',
110
130
  });
131
+ } catch (error) {
132
+ const message =
133
+ error instanceof Error ? error.message : 'Dauth server error';
134
+ return res
135
+ .status(500)
136
+ .send({ status: 'server-error', message });
111
137
  }
112
138
  };
113
139
  };
@@ -1,4 +0,0 @@
1
- export declare function getUser(token: string, domainName: string): Promise<{
2
- response: any;
3
- data: any;
4
- }>;
@@ -1,3 +0,0 @@
1
- export declare const apiVersion = "v1";
2
- export declare const serverDomain = "dauth.ovh";
3
- export declare function getServerBasePath(): string;