@technomoron/api-server-base 1.1.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.txt +25 -10
- package/dist/cjs/api-module.cjs +17 -0
- package/dist/cjs/api-module.d.ts +27 -0
- package/dist/cjs/api-server-base.cjs +4 -40
- package/dist/cjs/api-server-base.d.ts +8 -38
- package/dist/cjs/auth-module.cjs +12 -13
- package/dist/cjs/auth-module.d.ts +8 -5
- package/dist/cjs/index.cjs +3 -2
- package/dist/cjs/index.d.ts +2 -1
- package/dist/esm/api-module.d.ts +27 -0
- package/dist/esm/api-module.js +13 -0
- package/dist/esm/api-server-base.d.ts +8 -38
- package/dist/esm/api-server-base.js +3 -39
- package/dist/esm/auth-module.d.ts +8 -5
- package/dist/esm/auth-module.js +11 -12
- package/dist/esm/index.d.ts +2 -1
- package/dist/esm/index.js +2 -1
- package/package.json +1 -1
package/README.txt
CHANGED
|
@@ -27,18 +27,32 @@ All runtime dependencies and `@types/*` packages are bundled with the distributi
|
|
|
27
27
|
|
|
28
28
|
Quick Start
|
|
29
29
|
-----------
|
|
30
|
-
import { ApiServer, ApiModule, ApiError } from '@technomoron/api-server-base';
|
|
30
|
+
import { ApiServer, ApiModule, ApiError, BaseAuthStorage } from '@technomoron/api-server-base';
|
|
31
|
+
|
|
32
|
+
type DemoUser = { id: string; email: string; password: string };
|
|
33
|
+
|
|
34
|
+
class DemoStorage extends BaseAuthStorage<DemoUser, Omit<DemoUser, 'password'>> {
|
|
35
|
+
private readonly users = new Map<string, DemoUser>([
|
|
36
|
+
['1', { id: '1', email: 'demo@example.com', password: 'secret' }]
|
|
37
|
+
]);
|
|
31
38
|
|
|
32
|
-
class AppServer extends ApiServer {
|
|
33
39
|
async getUser(uid: unknown) {
|
|
34
|
-
return
|
|
40
|
+
return this.users.get(String(uid)) ?? null;
|
|
35
41
|
}
|
|
36
42
|
|
|
37
|
-
|
|
38
|
-
return
|
|
43
|
+
getUserPasswordHash(user: DemoUser) {
|
|
44
|
+
return user.password;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
filterUser(user: DemoUser) {
|
|
48
|
+
const { password: _password, ...safe } = user;
|
|
49
|
+
void _password;
|
|
50
|
+
return safe;
|
|
39
51
|
}
|
|
40
52
|
}
|
|
41
53
|
|
|
54
|
+
class AppServer extends ApiServer {}
|
|
55
|
+
|
|
42
56
|
class UserModule extends ApiModule<AppServer> {
|
|
43
57
|
constructor() {
|
|
44
58
|
super({ namespace: '/users' });
|
|
@@ -51,18 +65,19 @@ class UserModule extends ApiModule<AppServer> {
|
|
|
51
65
|
path: '/',
|
|
52
66
|
auth: { type: 'yes', req: 'any' },
|
|
53
67
|
handler: async ({ server, tokenData }) => {
|
|
54
|
-
const
|
|
68
|
+
const storage = server.getAuthStorage();
|
|
69
|
+
const user = tokenData ? await storage.getUser(tokenData.uid) : null;
|
|
55
70
|
if (!user) {
|
|
56
71
|
throw new ApiError({ code: 404, message: 'User not found' });
|
|
57
72
|
}
|
|
58
|
-
return [200,
|
|
73
|
+
return [200, storage.filterUser(user)];
|
|
59
74
|
},
|
|
60
75
|
},
|
|
61
76
|
];
|
|
62
77
|
}
|
|
63
78
|
}
|
|
64
79
|
|
|
65
|
-
const yourStorageAdapter = new
|
|
80
|
+
const yourStorageAdapter = new DemoStorage();
|
|
66
81
|
|
|
67
82
|
const server = new AppServer({
|
|
68
83
|
apiPort: 3101,
|
|
@@ -116,8 +131,8 @@ Use getClientIp(req) to obtain the most likely client address, skipping loopback
|
|
|
116
131
|
|
|
117
132
|
Extending the Base Classes
|
|
118
133
|
--------------------------
|
|
119
|
-
|
|
120
|
-
|
|
134
|
+
Implement the AuthStorage contract (getUser, verifyPassword, storeToken, updateToken, etc.) to integrate with your persistence layer, then supply it via authStorage().
|
|
135
|
+
Use your storage adapter's filterUser helper to trim sensitive data before returning responses.
|
|
121
136
|
Provide your own authorize method to enforce role based access control using the ApiAuthClass enum.
|
|
122
137
|
Create feature modules by extending ApiModule. Use the optional checkConfig hook to validate prerequisites before routes mount.
|
|
123
138
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ApiModule = void 0;
|
|
4
|
+
class ApiModule {
|
|
5
|
+
constructor(opts = {}) {
|
|
6
|
+
this.mountpath = '';
|
|
7
|
+
this.namespace = opts.namespace ?? this.constructor.defaultNamespace ?? '';
|
|
8
|
+
}
|
|
9
|
+
checkConfig() {
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
defineRoutes() {
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.ApiModule = ApiModule;
|
|
17
|
+
ApiModule.defaultNamespace = '';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ApiRequest } from './api-server-base.js';
|
|
2
|
+
export type ApiHandler = (apiReq: ApiRequest) => Promise<[number] | [number, any] | [number, any, string]>;
|
|
3
|
+
export type ApiAuthType = 'none' | 'maybe' | 'yes';
|
|
4
|
+
export type ApiAuthClass = 'any' | 'admin';
|
|
5
|
+
export interface ApiKey {
|
|
6
|
+
uid: unknown;
|
|
7
|
+
}
|
|
8
|
+
export type ApiRoute = {
|
|
9
|
+
method: 'get' | 'post' | 'put' | 'delete';
|
|
10
|
+
path: string;
|
|
11
|
+
handler: ApiHandler;
|
|
12
|
+
auth: {
|
|
13
|
+
type: ApiAuthType;
|
|
14
|
+
req: ApiAuthClass;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
export declare class ApiModule<T> {
|
|
18
|
+
server: T;
|
|
19
|
+
namespace: string;
|
|
20
|
+
mountpath: string;
|
|
21
|
+
static defaultNamespace: string;
|
|
22
|
+
constructor(opts?: {
|
|
23
|
+
namespace?: string;
|
|
24
|
+
});
|
|
25
|
+
checkConfig(): boolean;
|
|
26
|
+
defineRoutes(): ApiRoute[];
|
|
27
|
+
}
|
|
@@ -17,20 +17,8 @@ const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
|
17
17
|
const multer_1 = __importDefault(require("multer"));
|
|
18
18
|
const auth_module_js_1 = require("./auth-module.cjs");
|
|
19
19
|
const auth_storage_js_1 = require("./auth-storage.cjs");
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
this.mountpath = '';
|
|
23
|
-
this.namespace = opts.namespace ?? this.constructor.defaultNamespace ?? '';
|
|
24
|
-
}
|
|
25
|
-
checkConfig() {
|
|
26
|
-
return true;
|
|
27
|
-
}
|
|
28
|
-
defineRoutes() {
|
|
29
|
-
return [];
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
exports.ApiModule = ApiModule;
|
|
33
|
-
ApiModule.defaultNamespace = '';
|
|
20
|
+
var api_module_js_1 = require("./api-module.cjs");
|
|
21
|
+
Object.defineProperty(exports, "ApiModule", { enumerable: true, get: function () { return api_module_js_1.ApiModule; } });
|
|
34
22
|
function guess_exception_text(error, defMsg = 'Unknown Error') {
|
|
35
23
|
const msg = [];
|
|
36
24
|
if (typeof error === 'string' && error.trim() !== '') {
|
|
@@ -277,31 +265,16 @@ class ApiServer {
|
|
|
277
265
|
void token;
|
|
278
266
|
return null;
|
|
279
267
|
}
|
|
280
|
-
async getUser(uid) {
|
|
281
|
-
return this.storageAdapter.getUser(uid);
|
|
282
|
-
}
|
|
283
|
-
getUserPasswordHash(user) {
|
|
284
|
-
return this.storageAdapter.getUserPasswordHash(user);
|
|
285
|
-
}
|
|
286
|
-
getUserId(user) {
|
|
287
|
-
return this.storageAdapter.getUserId(user);
|
|
288
|
-
}
|
|
289
268
|
async authenticateUser(params) {
|
|
290
269
|
if (!params?.login || !params?.password) {
|
|
291
270
|
return false;
|
|
292
271
|
}
|
|
293
|
-
const user = await this.getUser(params.login);
|
|
272
|
+
const user = await this.storageAdapter.getUser(params.login);
|
|
294
273
|
if (!user) {
|
|
295
274
|
return false;
|
|
296
275
|
}
|
|
297
276
|
const hash = this.storageAdapter.getUserPasswordHash(user);
|
|
298
|
-
return this.verifyPassword(params.password, hash);
|
|
299
|
-
}
|
|
300
|
-
async storeToken(data) {
|
|
301
|
-
await this.storageAdapter.storeToken(data);
|
|
302
|
-
}
|
|
303
|
-
async getToken(query) {
|
|
304
|
-
return this.storageAdapter.getToken(query);
|
|
277
|
+
return this.storageAdapter.verifyPassword(params.password, hash);
|
|
305
278
|
}
|
|
306
279
|
async updateToken(updates) {
|
|
307
280
|
if (typeof this.storageAdapter.updateToken !== 'function') {
|
|
@@ -315,15 +288,6 @@ class ApiServer {
|
|
|
315
288
|
scope: updates.scope
|
|
316
289
|
});
|
|
317
290
|
}
|
|
318
|
-
async deleteToken(query) {
|
|
319
|
-
return this.storageAdapter.deleteToken(query);
|
|
320
|
-
}
|
|
321
|
-
async verifyPassword(password, hash) {
|
|
322
|
-
return this.storageAdapter.verifyPassword(password, hash);
|
|
323
|
-
}
|
|
324
|
-
filterUser(fullUser) {
|
|
325
|
-
return this.storageAdapter.filterUser(fullUser);
|
|
326
|
-
}
|
|
327
291
|
guessExceptionText(error, defMsg = 'Unkown Error') {
|
|
328
292
|
return guess_exception_text(error, defMsg);
|
|
329
293
|
}
|
|
@@ -6,8 +6,10 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { Application, Request, Response } from 'express';
|
|
8
8
|
import jwt, { JwtPayload, SignOptions, VerifyOptions } from 'jsonwebtoken';
|
|
9
|
+
import { ApiModule } from './api-module.js';
|
|
10
|
+
import type { ApiAuthClass, ApiKey } from './api-module.js';
|
|
9
11
|
import type { AuthProviderModule } from './auth-module.js';
|
|
10
|
-
import type {
|
|
12
|
+
import type { AuthStorage } from './auth-storage.js';
|
|
11
13
|
export type { Application, Request, Response, NextFunction, Router } from 'express';
|
|
12
14
|
export type { Multer } from 'multer';
|
|
13
15
|
export type { JwtPayload, SignOptions, VerifyOptions } from 'jsonwebtoken';
|
|
@@ -45,32 +47,8 @@ export interface ApiRequest {
|
|
|
45
47
|
tokenData?: ApiTokenData | null;
|
|
46
48
|
token?: string;
|
|
47
49
|
}
|
|
48
|
-
export
|
|
49
|
-
export type ApiAuthType
|
|
50
|
-
export type ApiAuthClass = 'any' | 'admin';
|
|
51
|
-
export interface ApiKey {
|
|
52
|
-
uid: unknown;
|
|
53
|
-
}
|
|
54
|
-
export type ApiRoute = {
|
|
55
|
-
method: 'get' | 'post' | 'put' | 'delete';
|
|
56
|
-
path: string;
|
|
57
|
-
handler: ApiHandler;
|
|
58
|
-
auth: {
|
|
59
|
-
type: ApiAuthType;
|
|
60
|
-
req: ApiAuthClass;
|
|
61
|
-
};
|
|
62
|
-
};
|
|
63
|
-
export declare class ApiModule<T> {
|
|
64
|
-
server: T;
|
|
65
|
-
namespace: string;
|
|
66
|
-
mountpath: string;
|
|
67
|
-
static defaultNamespace: string;
|
|
68
|
-
constructor(opts?: {
|
|
69
|
-
namespace?: string;
|
|
70
|
-
});
|
|
71
|
-
checkConfig(): boolean;
|
|
72
|
-
defineRoutes(): ApiRoute[];
|
|
73
|
-
}
|
|
50
|
+
export { ApiModule } from './api-module.js';
|
|
51
|
+
export type { ApiHandler, ApiAuthType, ApiAuthClass, ApiRoute, ApiKey } from './api-module.js';
|
|
74
52
|
export interface ApiErrorParams {
|
|
75
53
|
code?: number;
|
|
76
54
|
message?: any;
|
|
@@ -114,26 +92,21 @@ export declare class ApiServer {
|
|
|
114
92
|
* @deprecated Use {@link ApiServer.authStorage} instead.
|
|
115
93
|
*/
|
|
116
94
|
useAuthStorage<UserRow, SafeUser>(storage: AuthStorage<UserRow, SafeUser>): this;
|
|
117
|
-
authModule<UserRow
|
|
95
|
+
authModule<UserRow>(module: AuthProviderModule<UserRow>): this;
|
|
118
96
|
/**
|
|
119
97
|
* @deprecated Use {@link ApiServer.authModule} instead.
|
|
120
98
|
*/
|
|
121
|
-
useAuthModule<UserRow
|
|
99
|
+
useAuthModule<UserRow>(module: AuthProviderModule<UserRow>): this;
|
|
122
100
|
getAuthStorage(): AuthStorage<any, any>;
|
|
123
|
-
getAuthModule(): AuthProviderModule<any
|
|
101
|
+
getAuthModule(): AuthProviderModule<any>;
|
|
124
102
|
jwtSign(payload: any, secret: string, expiresInSeconds: number, options?: SignOptions): JwtSignResult;
|
|
125
103
|
jwtVerify<T>(token: string, secret: string, options?: VerifyOptions): JwtVerifyResult<T>;
|
|
126
104
|
jwtDecode<T>(token: string, options?: jwt.DecodeOptions): JwtDecodeResult<T>;
|
|
127
105
|
getApiKey<T = ApiKey>(token: string): Promise<T | null>;
|
|
128
|
-
getUser(uid: AuthIdentifier): Promise<unknown>;
|
|
129
|
-
getUserPasswordHash(user: unknown): string;
|
|
130
|
-
getUserId(user: unknown): AuthIdentifier;
|
|
131
106
|
authenticateUser(params: {
|
|
132
107
|
login: string;
|
|
133
108
|
password: string;
|
|
134
109
|
}): Promise<boolean>;
|
|
135
|
-
storeToken(data: AuthTokenData): Promise<void>;
|
|
136
|
-
getToken(query: AuthTokenQuery): Promise<AuthTokenData | null>;
|
|
137
110
|
updateToken(updates: {
|
|
138
111
|
accessToken: string;
|
|
139
112
|
refreshToken: string;
|
|
@@ -141,9 +114,6 @@ export declare class ApiServer {
|
|
|
141
114
|
clientId?: string;
|
|
142
115
|
scope?: string[];
|
|
143
116
|
}): Promise<boolean>;
|
|
144
|
-
deleteToken(query: AuthTokenQuery): Promise<number>;
|
|
145
|
-
verifyPassword(password: string, hash: string): Promise<boolean>;
|
|
146
|
-
filterUser<T = any, U = any>(fullUser: T): U;
|
|
147
117
|
guessExceptionText(error: any, defMsg?: string): string;
|
|
148
118
|
protected authorize(apiReq: ApiRequest, requiredClass: ApiAuthClass): Promise<void>;
|
|
149
119
|
private middlewares;
|
package/dist/cjs/auth-module.cjs
CHANGED
|
@@ -1,26 +1,25 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.nullAuthModule = exports.BaseAuthModule = void 0;
|
|
4
|
+
const api_module_js_1 = require("./api-module.cjs");
|
|
4
5
|
// Handy base that you can extend when wiring a real auth module. Subclasses
|
|
5
|
-
// must
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
// must supply a namespace via the constructor and implement token issuance.
|
|
7
|
+
class BaseAuthModule extends api_module_js_1.ApiModule {
|
|
8
|
+
constructor(opts) {
|
|
9
|
+
super(opts);
|
|
9
10
|
this.moduleType = 'auth';
|
|
10
11
|
}
|
|
11
|
-
// Override to mint tokens for the provided user and request context
|
|
12
|
-
async issueTokens(apiReq, user, metadata) {
|
|
13
|
-
void apiReq;
|
|
14
|
-
void user;
|
|
15
|
-
void metadata;
|
|
16
|
-
throw new Error('Auth module not configured');
|
|
17
|
-
}
|
|
18
12
|
}
|
|
19
13
|
exports.BaseAuthModule = BaseAuthModule;
|
|
20
14
|
class NullAuthModule extends BaseAuthModule {
|
|
21
15
|
constructor() {
|
|
22
|
-
super(
|
|
23
|
-
|
|
16
|
+
super({ namespace: '__null__' });
|
|
17
|
+
}
|
|
18
|
+
async issueTokens(apiReq, user, metadata) {
|
|
19
|
+
void apiReq;
|
|
20
|
+
void user;
|
|
21
|
+
void metadata;
|
|
22
|
+
throw new Error('Auth module not configured. Inject a real auth module before issuing tokens.');
|
|
24
23
|
}
|
|
25
24
|
}
|
|
26
25
|
exports.nullAuthModule = new NullAuthModule();
|
|
@@ -1,17 +1,20 @@
|
|
|
1
|
+
import { ApiModule } from './api-module.js';
|
|
1
2
|
import type { ApiRequest } from './api-server-base.js';
|
|
2
3
|
import type { AuthTokenMetadata, AuthTokenPair } from './auth-storage.js';
|
|
3
|
-
export interface AuthProviderModule<UserRow
|
|
4
|
+
export interface AuthProviderModule<UserRow = unknown> {
|
|
4
5
|
readonly moduleType: 'auth';
|
|
5
6
|
readonly namespace: string;
|
|
6
7
|
issueTokens(apiReq: ApiRequest, user: UserRow, metadata?: AuthTokenMetadata & {
|
|
7
8
|
expires?: Date;
|
|
8
9
|
}): Promise<AuthTokenPair>;
|
|
9
10
|
}
|
|
10
|
-
export declare abstract class BaseAuthModule implements AuthProviderModule<
|
|
11
|
+
export declare abstract class BaseAuthModule<UserRow = unknown> extends ApiModule<any> implements AuthProviderModule<UserRow> {
|
|
11
12
|
readonly moduleType: "auth";
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
protected constructor(opts: {
|
|
14
|
+
namespace: string;
|
|
15
|
+
});
|
|
16
|
+
abstract issueTokens(apiReq: ApiRequest, user: UserRow, metadata?: AuthTokenMetadata & {
|
|
14
17
|
expires?: Date;
|
|
15
18
|
}): Promise<AuthTokenPair>;
|
|
16
19
|
}
|
|
17
|
-
export declare const nullAuthModule: AuthProviderModule<unknown
|
|
20
|
+
export declare const nullAuthModule: AuthProviderModule<unknown>;
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -3,12 +3,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.BaseAuthModule = exports.nullAuthModule = exports.BaseAuthStorage = exports.nullAuthStorage = exports.
|
|
6
|
+
exports.BaseAuthModule = exports.nullAuthModule = exports.BaseAuthStorage = exports.nullAuthStorage = exports.ApiModule = exports.ApiError = exports.ApiServer = void 0;
|
|
7
7
|
var api_server_base_js_1 = require("./api-server-base.cjs");
|
|
8
8
|
Object.defineProperty(exports, "ApiServer", { enumerable: true, get: function () { return __importDefault(api_server_base_js_1).default; } });
|
|
9
9
|
var api_server_base_js_2 = require("./api-server-base.cjs");
|
|
10
|
-
Object.defineProperty(exports, "ApiModule", { enumerable: true, get: function () { return api_server_base_js_2.ApiModule; } });
|
|
11
10
|
Object.defineProperty(exports, "ApiError", { enumerable: true, get: function () { return api_server_base_js_2.ApiError; } });
|
|
11
|
+
var api_module_js_1 = require("./api-module.cjs");
|
|
12
|
+
Object.defineProperty(exports, "ApiModule", { enumerable: true, get: function () { return api_module_js_1.ApiModule; } });
|
|
12
13
|
var auth_storage_js_1 = require("./auth-storage.cjs");
|
|
13
14
|
Object.defineProperty(exports, "nullAuthStorage", { enumerable: true, get: function () { return auth_storage_js_1.nullAuthStorage; } });
|
|
14
15
|
Object.defineProperty(exports, "BaseAuthStorage", { enumerable: true, get: function () { return auth_storage_js_1.BaseAuthStorage; } });
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { default as ApiServer } from './api-server-base.js';
|
|
2
|
-
export {
|
|
2
|
+
export { ApiError } from './api-server-base.js';
|
|
3
|
+
export { ApiModule } from './api-module.js';
|
|
3
4
|
export type { ApiErrorParams, ApiHandler, ApiKey, ApiServerConf, ApiRequest, ApiRoute, ApiAuthType, ApiAuthClass, ApiTokenData, RequestWithStuff } from './api-server-base.js';
|
|
4
5
|
export type { AuthIdentifier, AuthTokenMetadata, AuthTokenData, AuthTokenQuery, AuthTokenPair, AuthTokenPayload, PasskeyChallengeParams, PasskeyChallenge, PasskeyVerificationParams, PasskeyVerificationResult, OAuthClient, AuthCodeData, AuthCodeRequest, AuthStorage } from './auth-storage.js';
|
|
5
6
|
export type { AuthProviderModule } from './auth-module.js';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ApiRequest } from './api-server-base.js';
|
|
2
|
+
export type ApiHandler = (apiReq: ApiRequest) => Promise<[number] | [number, any] | [number, any, string]>;
|
|
3
|
+
export type ApiAuthType = 'none' | 'maybe' | 'yes';
|
|
4
|
+
export type ApiAuthClass = 'any' | 'admin';
|
|
5
|
+
export interface ApiKey {
|
|
6
|
+
uid: unknown;
|
|
7
|
+
}
|
|
8
|
+
export type ApiRoute = {
|
|
9
|
+
method: 'get' | 'post' | 'put' | 'delete';
|
|
10
|
+
path: string;
|
|
11
|
+
handler: ApiHandler;
|
|
12
|
+
auth: {
|
|
13
|
+
type: ApiAuthType;
|
|
14
|
+
req: ApiAuthClass;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
export declare class ApiModule<T> {
|
|
18
|
+
server: T;
|
|
19
|
+
namespace: string;
|
|
20
|
+
mountpath: string;
|
|
21
|
+
static defaultNamespace: string;
|
|
22
|
+
constructor(opts?: {
|
|
23
|
+
namespace?: string;
|
|
24
|
+
});
|
|
25
|
+
checkConfig(): boolean;
|
|
26
|
+
defineRoutes(): ApiRoute[];
|
|
27
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export class ApiModule {
|
|
2
|
+
constructor(opts = {}) {
|
|
3
|
+
this.mountpath = '';
|
|
4
|
+
this.namespace = opts.namespace ?? this.constructor.defaultNamespace ?? '';
|
|
5
|
+
}
|
|
6
|
+
checkConfig() {
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
defineRoutes() {
|
|
10
|
+
return [];
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
ApiModule.defaultNamespace = '';
|
|
@@ -6,8 +6,10 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { Application, Request, Response } from 'express';
|
|
8
8
|
import jwt, { JwtPayload, SignOptions, VerifyOptions } from 'jsonwebtoken';
|
|
9
|
+
import { ApiModule } from './api-module.js';
|
|
10
|
+
import type { ApiAuthClass, ApiKey } from './api-module.js';
|
|
9
11
|
import type { AuthProviderModule } from './auth-module.js';
|
|
10
|
-
import type {
|
|
12
|
+
import type { AuthStorage } from './auth-storage.js';
|
|
11
13
|
export type { Application, Request, Response, NextFunction, Router } from 'express';
|
|
12
14
|
export type { Multer } from 'multer';
|
|
13
15
|
export type { JwtPayload, SignOptions, VerifyOptions } from 'jsonwebtoken';
|
|
@@ -45,32 +47,8 @@ export interface ApiRequest {
|
|
|
45
47
|
tokenData?: ApiTokenData | null;
|
|
46
48
|
token?: string;
|
|
47
49
|
}
|
|
48
|
-
export
|
|
49
|
-
export type ApiAuthType
|
|
50
|
-
export type ApiAuthClass = 'any' | 'admin';
|
|
51
|
-
export interface ApiKey {
|
|
52
|
-
uid: unknown;
|
|
53
|
-
}
|
|
54
|
-
export type ApiRoute = {
|
|
55
|
-
method: 'get' | 'post' | 'put' | 'delete';
|
|
56
|
-
path: string;
|
|
57
|
-
handler: ApiHandler;
|
|
58
|
-
auth: {
|
|
59
|
-
type: ApiAuthType;
|
|
60
|
-
req: ApiAuthClass;
|
|
61
|
-
};
|
|
62
|
-
};
|
|
63
|
-
export declare class ApiModule<T> {
|
|
64
|
-
server: T;
|
|
65
|
-
namespace: string;
|
|
66
|
-
mountpath: string;
|
|
67
|
-
static defaultNamespace: string;
|
|
68
|
-
constructor(opts?: {
|
|
69
|
-
namespace?: string;
|
|
70
|
-
});
|
|
71
|
-
checkConfig(): boolean;
|
|
72
|
-
defineRoutes(): ApiRoute[];
|
|
73
|
-
}
|
|
50
|
+
export { ApiModule } from './api-module.js';
|
|
51
|
+
export type { ApiHandler, ApiAuthType, ApiAuthClass, ApiRoute, ApiKey } from './api-module.js';
|
|
74
52
|
export interface ApiErrorParams {
|
|
75
53
|
code?: number;
|
|
76
54
|
message?: any;
|
|
@@ -114,26 +92,21 @@ export declare class ApiServer {
|
|
|
114
92
|
* @deprecated Use {@link ApiServer.authStorage} instead.
|
|
115
93
|
*/
|
|
116
94
|
useAuthStorage<UserRow, SafeUser>(storage: AuthStorage<UserRow, SafeUser>): this;
|
|
117
|
-
authModule<UserRow
|
|
95
|
+
authModule<UserRow>(module: AuthProviderModule<UserRow>): this;
|
|
118
96
|
/**
|
|
119
97
|
* @deprecated Use {@link ApiServer.authModule} instead.
|
|
120
98
|
*/
|
|
121
|
-
useAuthModule<UserRow
|
|
99
|
+
useAuthModule<UserRow>(module: AuthProviderModule<UserRow>): this;
|
|
122
100
|
getAuthStorage(): AuthStorage<any, any>;
|
|
123
|
-
getAuthModule(): AuthProviderModule<any
|
|
101
|
+
getAuthModule(): AuthProviderModule<any>;
|
|
124
102
|
jwtSign(payload: any, secret: string, expiresInSeconds: number, options?: SignOptions): JwtSignResult;
|
|
125
103
|
jwtVerify<T>(token: string, secret: string, options?: VerifyOptions): JwtVerifyResult<T>;
|
|
126
104
|
jwtDecode<T>(token: string, options?: jwt.DecodeOptions): JwtDecodeResult<T>;
|
|
127
105
|
getApiKey<T = ApiKey>(token: string): Promise<T | null>;
|
|
128
|
-
getUser(uid: AuthIdentifier): Promise<unknown>;
|
|
129
|
-
getUserPasswordHash(user: unknown): string;
|
|
130
|
-
getUserId(user: unknown): AuthIdentifier;
|
|
131
106
|
authenticateUser(params: {
|
|
132
107
|
login: string;
|
|
133
108
|
password: string;
|
|
134
109
|
}): Promise<boolean>;
|
|
135
|
-
storeToken(data: AuthTokenData): Promise<void>;
|
|
136
|
-
getToken(query: AuthTokenQuery): Promise<AuthTokenData | null>;
|
|
137
110
|
updateToken(updates: {
|
|
138
111
|
accessToken: string;
|
|
139
112
|
refreshToken: string;
|
|
@@ -141,9 +114,6 @@ export declare class ApiServer {
|
|
|
141
114
|
clientId?: string;
|
|
142
115
|
scope?: string[];
|
|
143
116
|
}): Promise<boolean>;
|
|
144
|
-
deleteToken(query: AuthTokenQuery): Promise<number>;
|
|
145
|
-
verifyPassword(password: string, hash: string): Promise<boolean>;
|
|
146
|
-
filterUser<T = any, U = any>(fullUser: T): U;
|
|
147
117
|
guessExceptionText(error: any, defMsg?: string): string;
|
|
148
118
|
protected authorize(apiReq: ApiRequest, requiredClass: ApiAuthClass): Promise<void>;
|
|
149
119
|
private middlewares;
|
|
@@ -11,19 +11,7 @@ import jwt from 'jsonwebtoken';
|
|
|
11
11
|
import multer from 'multer';
|
|
12
12
|
import { nullAuthModule } from './auth-module.js';
|
|
13
13
|
import { nullAuthStorage } from './auth-storage.js';
|
|
14
|
-
export
|
|
15
|
-
constructor(opts = {}) {
|
|
16
|
-
this.mountpath = '';
|
|
17
|
-
this.namespace = opts.namespace ?? this.constructor.defaultNamespace ?? '';
|
|
18
|
-
}
|
|
19
|
-
checkConfig() {
|
|
20
|
-
return true;
|
|
21
|
-
}
|
|
22
|
-
defineRoutes() {
|
|
23
|
-
return [];
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
ApiModule.defaultNamespace = '';
|
|
14
|
+
export { ApiModule } from './api-module.js';
|
|
27
15
|
function guess_exception_text(error, defMsg = 'Unknown Error') {
|
|
28
16
|
const msg = [];
|
|
29
17
|
if (typeof error === 'string' && error.trim() !== '') {
|
|
@@ -269,31 +257,16 @@ export class ApiServer {
|
|
|
269
257
|
void token;
|
|
270
258
|
return null;
|
|
271
259
|
}
|
|
272
|
-
async getUser(uid) {
|
|
273
|
-
return this.storageAdapter.getUser(uid);
|
|
274
|
-
}
|
|
275
|
-
getUserPasswordHash(user) {
|
|
276
|
-
return this.storageAdapter.getUserPasswordHash(user);
|
|
277
|
-
}
|
|
278
|
-
getUserId(user) {
|
|
279
|
-
return this.storageAdapter.getUserId(user);
|
|
280
|
-
}
|
|
281
260
|
async authenticateUser(params) {
|
|
282
261
|
if (!params?.login || !params?.password) {
|
|
283
262
|
return false;
|
|
284
263
|
}
|
|
285
|
-
const user = await this.getUser(params.login);
|
|
264
|
+
const user = await this.storageAdapter.getUser(params.login);
|
|
286
265
|
if (!user) {
|
|
287
266
|
return false;
|
|
288
267
|
}
|
|
289
268
|
const hash = this.storageAdapter.getUserPasswordHash(user);
|
|
290
|
-
return this.verifyPassword(params.password, hash);
|
|
291
|
-
}
|
|
292
|
-
async storeToken(data) {
|
|
293
|
-
await this.storageAdapter.storeToken(data);
|
|
294
|
-
}
|
|
295
|
-
async getToken(query) {
|
|
296
|
-
return this.storageAdapter.getToken(query);
|
|
269
|
+
return this.storageAdapter.verifyPassword(params.password, hash);
|
|
297
270
|
}
|
|
298
271
|
async updateToken(updates) {
|
|
299
272
|
if (typeof this.storageAdapter.updateToken !== 'function') {
|
|
@@ -307,15 +280,6 @@ export class ApiServer {
|
|
|
307
280
|
scope: updates.scope
|
|
308
281
|
});
|
|
309
282
|
}
|
|
310
|
-
async deleteToken(query) {
|
|
311
|
-
return this.storageAdapter.deleteToken(query);
|
|
312
|
-
}
|
|
313
|
-
async verifyPassword(password, hash) {
|
|
314
|
-
return this.storageAdapter.verifyPassword(password, hash);
|
|
315
|
-
}
|
|
316
|
-
filterUser(fullUser) {
|
|
317
|
-
return this.storageAdapter.filterUser(fullUser);
|
|
318
|
-
}
|
|
319
283
|
guessExceptionText(error, defMsg = 'Unkown Error') {
|
|
320
284
|
return guess_exception_text(error, defMsg);
|
|
321
285
|
}
|
|
@@ -1,17 +1,20 @@
|
|
|
1
|
+
import { ApiModule } from './api-module.js';
|
|
1
2
|
import type { ApiRequest } from './api-server-base.js';
|
|
2
3
|
import type { AuthTokenMetadata, AuthTokenPair } from './auth-storage.js';
|
|
3
|
-
export interface AuthProviderModule<UserRow
|
|
4
|
+
export interface AuthProviderModule<UserRow = unknown> {
|
|
4
5
|
readonly moduleType: 'auth';
|
|
5
6
|
readonly namespace: string;
|
|
6
7
|
issueTokens(apiReq: ApiRequest, user: UserRow, metadata?: AuthTokenMetadata & {
|
|
7
8
|
expires?: Date;
|
|
8
9
|
}): Promise<AuthTokenPair>;
|
|
9
10
|
}
|
|
10
|
-
export declare abstract class BaseAuthModule implements AuthProviderModule<
|
|
11
|
+
export declare abstract class BaseAuthModule<UserRow = unknown> extends ApiModule<any> implements AuthProviderModule<UserRow> {
|
|
11
12
|
readonly moduleType: "auth";
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
protected constructor(opts: {
|
|
14
|
+
namespace: string;
|
|
15
|
+
});
|
|
16
|
+
abstract issueTokens(apiReq: ApiRequest, user: UserRow, metadata?: AuthTokenMetadata & {
|
|
14
17
|
expires?: Date;
|
|
15
18
|
}): Promise<AuthTokenPair>;
|
|
16
19
|
}
|
|
17
|
-
export declare const nullAuthModule: AuthProviderModule<unknown
|
|
20
|
+
export declare const nullAuthModule: AuthProviderModule<unknown>;
|
package/dist/esm/auth-module.js
CHANGED
|
@@ -1,22 +1,21 @@
|
|
|
1
|
+
import { ApiModule } from './api-module.js';
|
|
1
2
|
// Handy base that you can extend when wiring a real auth module. Subclasses
|
|
2
|
-
// must
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
// must supply a namespace via the constructor and implement token issuance.
|
|
4
|
+
export class BaseAuthModule extends ApiModule {
|
|
5
|
+
constructor(opts) {
|
|
6
|
+
super(opts);
|
|
6
7
|
this.moduleType = 'auth';
|
|
7
8
|
}
|
|
8
|
-
|
|
9
|
+
}
|
|
10
|
+
class NullAuthModule extends BaseAuthModule {
|
|
11
|
+
constructor() {
|
|
12
|
+
super({ namespace: '__null__' });
|
|
13
|
+
}
|
|
9
14
|
async issueTokens(apiReq, user, metadata) {
|
|
10
15
|
void apiReq;
|
|
11
16
|
void user;
|
|
12
17
|
void metadata;
|
|
13
|
-
throw new Error('Auth module not configured');
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
class NullAuthModule extends BaseAuthModule {
|
|
17
|
-
constructor() {
|
|
18
|
-
super(...arguments);
|
|
19
|
-
this.namespace = '__null__';
|
|
18
|
+
throw new Error('Auth module not configured. Inject a real auth module before issuing tokens.');
|
|
20
19
|
}
|
|
21
20
|
}
|
|
22
21
|
export const nullAuthModule = new NullAuthModule();
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { default as ApiServer } from './api-server-base.js';
|
|
2
|
-
export {
|
|
2
|
+
export { ApiError } from './api-server-base.js';
|
|
3
|
+
export { ApiModule } from './api-module.js';
|
|
3
4
|
export type { ApiErrorParams, ApiHandler, ApiKey, ApiServerConf, ApiRequest, ApiRoute, ApiAuthType, ApiAuthClass, ApiTokenData, RequestWithStuff } from './api-server-base.js';
|
|
4
5
|
export type { AuthIdentifier, AuthTokenMetadata, AuthTokenData, AuthTokenQuery, AuthTokenPair, AuthTokenPayload, PasskeyChallengeParams, PasskeyChallenge, PasskeyVerificationParams, PasskeyVerificationResult, OAuthClient, AuthCodeData, AuthCodeRequest, AuthStorage } from './auth-storage.js';
|
|
5
6
|
export type { AuthProviderModule } from './auth-module.js';
|
package/dist/esm/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { default as ApiServer } from './api-server-base.js';
|
|
2
|
-
export {
|
|
2
|
+
export { ApiError } from './api-server-base.js';
|
|
3
|
+
export { ApiModule } from './api-module.js';
|
|
3
4
|
export { nullAuthStorage, BaseAuthStorage } from './auth-storage.js';
|
|
4
5
|
export { nullAuthModule, BaseAuthModule } from './auth-module.js';
|