@owox/idp-protocol 0.4.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 +298 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/middleware/index.d.ts +2 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +1 -0
- package/dist/middleware/protocol-middleware.d.ts +99 -0
- package/dist/middleware/protocol-middleware.d.ts.map +1 -0
- package/dist/middleware/protocol-middleware.js +131 -0
- package/dist/providers/index.d.ts +2 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +1 -0
- package/dist/providers/null-provider.d.ts +24 -0
- package/dist/providers/null-provider.d.ts.map +1 -0
- package/dist/providers/null-provider.js +58 -0
- package/dist/types/cli.d.ts +27 -0
- package/dist/types/cli.d.ts.map +1 -0
- package/dist/types/cli.js +1 -0
- package/dist/types/config.d.ts +14 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +1 -0
- package/dist/types/errors.d.ts +33 -0
- package/dist/types/errors.d.ts.map +1 -0
- package/dist/types/errors.js +45 -0
- package/dist/types/index.d.ts +9 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +13 -0
- package/dist/types/models.d.ts +24 -0
- package/dist/types/models.d.ts.map +1 -0
- package/dist/types/models.js +1 -0
- package/dist/types/provider.d.ts +63 -0
- package/dist/types/provider.d.ts.map +1 -0
- package/dist/types/provider.js +1 -0
- package/package.json +52 -0
- package/src/index.ts +8 -0
- package/src/middleware/index.ts +1 -0
- package/src/middleware/protocol-middleware.ts +178 -0
- package/src/providers/index.ts +1 -0
- package/src/providers/null-provider.ts +71 -0
- package/src/types/cli.ts +30 -0
- package/src/types/config.ts +14 -0
- package/src/types/errors.ts +49 -0
- package/src/types/index.ts +18 -0
- package/src/types/models.ts +28 -0
- package/src/types/provider.ts +72 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration for the IDP provider
|
|
3
|
+
*/
|
|
4
|
+
export interface IdpConfig {
|
|
5
|
+
/**
|
|
6
|
+
* Base URL for the IDP provider
|
|
7
|
+
*/
|
|
8
|
+
baseUrl: string;
|
|
9
|
+
/**
|
|
10
|
+
* Additional provider-specific configuration
|
|
11
|
+
*/
|
|
12
|
+
[key: string]: unknown;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base error class for the IDP protocol
|
|
3
|
+
*/
|
|
4
|
+
export declare class IdpError extends Error {
|
|
5
|
+
code: string;
|
|
6
|
+
statusCode: number;
|
|
7
|
+
constructor(message: string, code: string, statusCode?: number);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Authentication error
|
|
11
|
+
*/
|
|
12
|
+
export declare class AuthenticationError extends IdpError {
|
|
13
|
+
constructor(message?: string);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Authorization error
|
|
17
|
+
*/
|
|
18
|
+
export declare class AuthorizationError extends IdpError {
|
|
19
|
+
constructor(message?: string);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Token expired error
|
|
23
|
+
*/
|
|
24
|
+
export declare class TokenExpiredError extends IdpError {
|
|
25
|
+
constructor(message?: string);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Invalid token error
|
|
29
|
+
*/
|
|
30
|
+
export declare class InvalidTokenError extends IdpError {
|
|
31
|
+
constructor(message?: string);
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/types/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,QAAS,SAAQ,KAAK;IAGxB,IAAI,EAAE,MAAM;IACZ,UAAU,EAAE,MAAM;gBAFzB,OAAO,EAAE,MAAM,EACR,IAAI,EAAE,MAAM,EACZ,UAAU,GAAE,MAAY;CAKlC;AAED;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,QAAQ;gBACnC,OAAO,GAAE,MAAgC;CAGtD;AAED;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,QAAQ;gBAClC,OAAO,GAAE,MAAmC;CAGzD;AAED;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,QAAQ;gBACjC,OAAO,GAAE,MAA4B;CAGlD;AAED;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,QAAQ;gBACjC,OAAO,GAAE,MAAwB;CAG9C"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base error class for the IDP protocol
|
|
3
|
+
*/
|
|
4
|
+
export class IdpError extends Error {
|
|
5
|
+
code;
|
|
6
|
+
statusCode;
|
|
7
|
+
constructor(message, code, statusCode = 400) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.code = code;
|
|
10
|
+
this.statusCode = statusCode;
|
|
11
|
+
this.name = 'IdpError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Authentication error
|
|
16
|
+
*/
|
|
17
|
+
export class AuthenticationError extends IdpError {
|
|
18
|
+
constructor(message = 'Authentication failed') {
|
|
19
|
+
super(message, 'AUTHENTICATION_ERROR', 401);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Authorization error
|
|
24
|
+
*/
|
|
25
|
+
export class AuthorizationError extends IdpError {
|
|
26
|
+
constructor(message = 'Insufficient permissions') {
|
|
27
|
+
super(message, 'AUTHORIZATION_ERROR', 403);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Token expired error
|
|
32
|
+
*/
|
|
33
|
+
export class TokenExpiredError extends IdpError {
|
|
34
|
+
constructor(message = 'Token has expired') {
|
|
35
|
+
super(message, 'TOKEN_EXPIRED', 401);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Invalid token error
|
|
40
|
+
*/
|
|
41
|
+
export class InvalidTokenError extends IdpError {
|
|
42
|
+
constructor(message = 'Invalid token') {
|
|
43
|
+
super(message, 'INVALID_TOKEN', 401);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,cAAc,eAAe,CAAC;AAG9B,cAAc,aAAa,CAAC;AAG5B,cAAc,aAAa,CAAC;AAG5B,cAAc,aAAa,CAAC;AAG5B,cAAc,UAAU,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main types export - organized by category
|
|
3
|
+
*/
|
|
4
|
+
// Core provider interface
|
|
5
|
+
export * from './provider.js';
|
|
6
|
+
// Domain models
|
|
7
|
+
export * from './models.js';
|
|
8
|
+
// Configuration
|
|
9
|
+
export * from './config.js';
|
|
10
|
+
// Error classes
|
|
11
|
+
export * from './errors.js';
|
|
12
|
+
// CLI commands
|
|
13
|
+
export * from './cli.js';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The roles that are supported by the IDP.
|
|
3
|
+
*/
|
|
4
|
+
export type Role = 'admin' | 'editor' | 'viewer';
|
|
5
|
+
/**
|
|
6
|
+
* Standardized token payload that all IDP implementations must return when introspecting their native tokens.
|
|
7
|
+
*/
|
|
8
|
+
export interface Payload {
|
|
9
|
+
userId: string;
|
|
10
|
+
projectId: string;
|
|
11
|
+
email?: string;
|
|
12
|
+
fullName?: string;
|
|
13
|
+
avatar?: string;
|
|
14
|
+
roles?: Role[];
|
|
15
|
+
projectTitle?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Authentication result from IDP callback
|
|
19
|
+
*/
|
|
20
|
+
export interface AuthResult {
|
|
21
|
+
accessToken: string;
|
|
22
|
+
refreshToken?: string;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=models.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../src/types/models.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,IAAI,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAElB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;IAEf,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Payload, AuthResult } from './models.js';
|
|
2
|
+
import { NextFunction, Request, Response } from 'express';
|
|
3
|
+
/**
|
|
4
|
+
* Simplified IDP Provider interface.
|
|
5
|
+
*/
|
|
6
|
+
export interface IdpProvider {
|
|
7
|
+
/**
|
|
8
|
+
* Sign in middleware. This method is used to handle the sign in request and use response to send the sign in response.
|
|
9
|
+
* <br/>
|
|
10
|
+
* If the IDP implementation does not support sign in, this method should call the `next()` function.
|
|
11
|
+
*/
|
|
12
|
+
signInMiddleware(req: Request, res: Response, next: NextFunction): Promise<void | Response>;
|
|
13
|
+
/**
|
|
14
|
+
* Sign out middleware. This method is used to handle the sign out request and use response to send the sign out response.
|
|
15
|
+
* <br/>
|
|
16
|
+
* If the IDP implementation does not support sign out, this method should call the `next()` function.
|
|
17
|
+
*/
|
|
18
|
+
signOutMiddleware(req: Request, res: Response, next: NextFunction): Promise<void | Response>;
|
|
19
|
+
/**
|
|
20
|
+
* Access token middleware. This method is used to handle the access token request and use response to send the access token response.
|
|
21
|
+
* <br/>
|
|
22
|
+
* If the IDP implementation does not support access token, this method should call the `next()` function.
|
|
23
|
+
*/
|
|
24
|
+
accessTokenMiddleware(req: Request, res: Response, next: NextFunction): Promise<void | Response>;
|
|
25
|
+
/**
|
|
26
|
+
* User api middleware. This method is used to handle the user request and use response to send the user response.
|
|
27
|
+
* <br/>
|
|
28
|
+
* If the IDP implementation does not support user, this method should call the `next()` function.
|
|
29
|
+
*/
|
|
30
|
+
userApiMiddleware(req: Request, res: Response, next: NextFunction): Promise<Response<Payload>>;
|
|
31
|
+
/**
|
|
32
|
+
* Introspect a token
|
|
33
|
+
* @param token - The token to introspect
|
|
34
|
+
* @returns The token payload
|
|
35
|
+
*/
|
|
36
|
+
introspectToken(token: string): Promise<Payload | null>;
|
|
37
|
+
/**
|
|
38
|
+
* Parse a token
|
|
39
|
+
* @param token - The token to parse
|
|
40
|
+
* @returns The token payload
|
|
41
|
+
*/
|
|
42
|
+
parseToken(token: string): Promise<Payload | null>;
|
|
43
|
+
/**
|
|
44
|
+
* Refresh a token
|
|
45
|
+
* @param refreshToken - The refresh token to use for the refresh
|
|
46
|
+
* @returns The authentication result
|
|
47
|
+
*/
|
|
48
|
+
refreshToken(refreshToken: string): Promise<AuthResult>;
|
|
49
|
+
/**
|
|
50
|
+
* Revoke a token. In different IDP implementations, this may have different token types.
|
|
51
|
+
* @param token - The token to revoke
|
|
52
|
+
*/
|
|
53
|
+
revokeToken(token: string): Promise<void>;
|
|
54
|
+
/**
|
|
55
|
+
* Initialize the IDP. Create resources, connect to databases, etc.
|
|
56
|
+
*/
|
|
57
|
+
initialize(): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Shutdown the IDP, close all connections and release resources
|
|
60
|
+
*/
|
|
61
|
+
shutdown(): Promise<void>;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/types/provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,gBAAgB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,GAAG,QAAQ,CAAC,CAAC;IAE5F;;;;OAIG;IACH,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,GAAG,QAAQ,CAAC,CAAC;IAE7F;;;;OAIG;IACH,qBAAqB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,GAAG,QAAQ,CAAC,CAAC;IAEjG;;;;OAIG;IACH,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;IAE/F;;;;OAIG;IACH,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IAExD;;;;OAIG;IACH,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IAEnD;;;;OAIG;IACH,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAExD;;;OAGG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE1C;;OAEG;IACH,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAE5B;;OAEG;IACH,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@owox/idp-protocol",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"author": "OWOX",
|
|
6
|
+
"license": "ELv2",
|
|
7
|
+
"description": "Identity Provider protocol contracts and interfaces for OWOX Data Marts",
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"access": "public"
|
|
10
|
+
},
|
|
11
|
+
"engines": {
|
|
12
|
+
"node": ">=22.16.0"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"clean": "rimraf dist",
|
|
17
|
+
"test": "jest",
|
|
18
|
+
"lint": "eslint . --config ./eslint.config.js",
|
|
19
|
+
"lint:fix": "eslint . --fix --config ./eslint.config.js",
|
|
20
|
+
"format": "prettier --write \"**/*.{ts,js,json,md}\"",
|
|
21
|
+
"format:check": "prettier --check \"**/*.{ts,js,json,md}\"",
|
|
22
|
+
"typecheck": "tsc --noEmit",
|
|
23
|
+
"prepack": "npm run build",
|
|
24
|
+
"prepublishOnly": "npm audit && npm run lint && npm run typecheck"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"idp",
|
|
28
|
+
"authentication",
|
|
29
|
+
"owox"
|
|
30
|
+
],
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"express": "^5"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/express": "^5.0.0",
|
|
36
|
+
"@types/node": "^22.10.7",
|
|
37
|
+
"typescript": "^5.7.3"
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"dist",
|
|
41
|
+
"src"
|
|
42
|
+
],
|
|
43
|
+
"exports": {
|
|
44
|
+
".": {
|
|
45
|
+
"import": "./dist/index.js",
|
|
46
|
+
"require": "./dist/index.js",
|
|
47
|
+
"types": "./dist/index.d.ts"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"main": "./dist/index.js",
|
|
51
|
+
"types": "./dist/index.d.ts"
|
|
52
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './protocol-middleware.js';
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { IdpProvider } from '../types/provider.js';
|
|
2
|
+
import { Express, Request, Response, NextFunction } from 'express';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The routes that are supported by the protocol middleware.
|
|
6
|
+
*/
|
|
7
|
+
export enum ProtocolRoute {
|
|
8
|
+
SIGN_IN = '/sign-in',
|
|
9
|
+
SIGN_OUT = '/sign-out',
|
|
10
|
+
ACCESS_TOKEN = '/access-token',
|
|
11
|
+
USER = '/api/user',
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The options for the protocol middleware.
|
|
16
|
+
* @property basePath - The base path for the protoc ol middleware.
|
|
17
|
+
* @property routes - The routes that are supported by the protocol middleware.
|
|
18
|
+
* @example
|
|
19
|
+
* {
|
|
20
|
+
* basePath: '/',
|
|
21
|
+
* routes: {
|
|
22
|
+
* signIn: '/signin', // override the default sign in route
|
|
23
|
+
* signOut: '/signout', // override the default sign out route
|
|
24
|
+
* accessToken: '/accesstoken', // override the default access token route
|
|
25
|
+
* user: '/api/userinfo', // override the default user route
|
|
26
|
+
* },
|
|
27
|
+
* }
|
|
28
|
+
*/
|
|
29
|
+
export interface IdpProtocolMiddlewareOptions {
|
|
30
|
+
basePath?: string;
|
|
31
|
+
routes?: {
|
|
32
|
+
signIn?: string;
|
|
33
|
+
signOut?: string;
|
|
34
|
+
accessToken?: string;
|
|
35
|
+
user?: string;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
type MiddlewareHandler = (
|
|
40
|
+
req: Request,
|
|
41
|
+
res: Response,
|
|
42
|
+
next: NextFunction
|
|
43
|
+
) => Promise<void | Response>;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The protocol middleware for the IDP.
|
|
47
|
+
* @param provider - The provider to use for the middleware.
|
|
48
|
+
* @param options - The options for the middleware.
|
|
49
|
+
* @example // Register the middleware with the default routes
|
|
50
|
+
* const idpProtocolMiddleware = new IdpProtocolMiddleware(provider);
|
|
51
|
+
* idpProtocolMiddleware.register(app);
|
|
52
|
+
*
|
|
53
|
+
* @example // Override the default routes
|
|
54
|
+
* const app = express();
|
|
55
|
+
* const middlewareOptions: IdpProtocolMiddlewareOptions = {
|
|
56
|
+
* basePath: '/',
|
|
57
|
+
* routes: {
|
|
58
|
+
* signIn: '/signin',
|
|
59
|
+
* signOut: '/signout',
|
|
60
|
+
* accessToken: '/accesstoken',
|
|
61
|
+
* user: '/api/userinfo',
|
|
62
|
+
* }
|
|
63
|
+
* };
|
|
64
|
+
* const idpProtocolMiddleware = new IdpProtocolMiddleware(provider, middlewareOptions);
|
|
65
|
+
* idpProtocolMiddleware.register(app);
|
|
66
|
+
*/
|
|
67
|
+
export class IdpProtocolMiddleware {
|
|
68
|
+
/**
|
|
69
|
+
* The default base path for the protocol middleware.
|
|
70
|
+
*/
|
|
71
|
+
public readonly DEFAULT_BASE_PATH = '/auth';
|
|
72
|
+
private readonly basePath: string;
|
|
73
|
+
private readonly routes: Required<NonNullable<IdpProtocolMiddlewareOptions['routes']>>;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* The constructor for the protocol middleware.
|
|
77
|
+
* @param provider - The provider to use for the middleware.
|
|
78
|
+
* @param options - The options for the middleware.
|
|
79
|
+
*/
|
|
80
|
+
constructor(
|
|
81
|
+
private readonly provider: IdpProvider,
|
|
82
|
+
options: IdpProtocolMiddlewareOptions = {}
|
|
83
|
+
) {
|
|
84
|
+
this.basePath = this.normalizeBasePath(options.basePath ?? this.DEFAULT_BASE_PATH);
|
|
85
|
+
this.routes = {
|
|
86
|
+
signIn: options.routes?.signIn ?? ProtocolRoute.SIGN_IN,
|
|
87
|
+
signOut: options.routes?.signOut ?? ProtocolRoute.SIGN_OUT,
|
|
88
|
+
accessToken: options.routes?.accessToken ?? ProtocolRoute.ACCESS_TOKEN,
|
|
89
|
+
user: options.routes?.user ?? ProtocolRoute.USER,
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
this.validateConfiguration();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Normalize the base path.
|
|
97
|
+
* @param path - The path to normalize.
|
|
98
|
+
* @returns The normalized path.
|
|
99
|
+
*/
|
|
100
|
+
private normalizeBasePath(path: string): string {
|
|
101
|
+
if (!path.startsWith('/')) {
|
|
102
|
+
throw new Error(`Base path must start with '/': ${path}`);
|
|
103
|
+
}
|
|
104
|
+
return path.endsWith('/') && path !== '/' ? path.slice(0, -1) : path;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Validate the configuration.
|
|
109
|
+
*/
|
|
110
|
+
private validateConfiguration(): void {
|
|
111
|
+
const routePaths = Object.values(this.routes);
|
|
112
|
+
const duplicates = routePaths.filter((path, index) => routePaths.indexOf(path) !== index);
|
|
113
|
+
|
|
114
|
+
if (duplicates.length > 0) {
|
|
115
|
+
throw new Error(`Duplicate route paths detected: ${duplicates.join(', ')}`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
routePaths.forEach(path => {
|
|
119
|
+
if (!path.startsWith('/')) {
|
|
120
|
+
throw new Error(`Route path must start with '/': ${path}`);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Create a route handler.
|
|
127
|
+
* @param handler - The handler to create.
|
|
128
|
+
* @returns The created handler.
|
|
129
|
+
*/
|
|
130
|
+
private createRouteHandler(handler: MiddlewareHandler): MiddlewareHandler {
|
|
131
|
+
return async (req: Request, res: Response, next: NextFunction) => {
|
|
132
|
+
try {
|
|
133
|
+
await handler(req, res, next);
|
|
134
|
+
} catch (error) {
|
|
135
|
+
next(error);
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Register the protocol middleware routes with the express app.
|
|
142
|
+
* @param app - The express app to register the routes with.
|
|
143
|
+
* @example
|
|
144
|
+
* const app = express();
|
|
145
|
+
* const idpProtocolMiddleware = new IdpProtocolMiddleware(provider);
|
|
146
|
+
* idpProtocolMiddleware.register(app);
|
|
147
|
+
* app.listen(3000);
|
|
148
|
+
*/
|
|
149
|
+
register(app: Express): void {
|
|
150
|
+
const routeConfigs = [
|
|
151
|
+
{
|
|
152
|
+
path: this.routes.signIn,
|
|
153
|
+
handler: (req: Request, res: Response, next: NextFunction) =>
|
|
154
|
+
this.provider.signInMiddleware(req, res, next),
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
path: this.routes.signOut,
|
|
158
|
+
handler: (req: Request, res: Response, next: NextFunction) =>
|
|
159
|
+
this.provider.signOutMiddleware(req, res, next),
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
path: this.routes.accessToken,
|
|
163
|
+
handler: (req: Request, res: Response, next: NextFunction) =>
|
|
164
|
+
this.provider.accessTokenMiddleware(req, res, next),
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
path: this.routes.user,
|
|
168
|
+
handler: (req: Request, res: Response, next: NextFunction) =>
|
|
169
|
+
this.provider.userApiMiddleware(req, res, next),
|
|
170
|
+
},
|
|
171
|
+
];
|
|
172
|
+
|
|
173
|
+
routeConfigs.forEach(({ path, handler }) => {
|
|
174
|
+
const fullPath = `${this.basePath}${path}`;
|
|
175
|
+
app.all(fullPath, this.createRouteHandler(handler));
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { NullIdpProvider } from './null-provider.js';
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { IdpProvider } from '../types/provider.js';
|
|
2
|
+
import { AuthResult, Payload } from '../types/models.js';
|
|
3
|
+
import { Request, Response, NextFunction } from 'express';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* NULL IDP Provider - single user, single project
|
|
7
|
+
* Used for deployments without user management and development
|
|
8
|
+
*/
|
|
9
|
+
export class NullIdpProvider implements IdpProvider {
|
|
10
|
+
private defaultPayload: Payload;
|
|
11
|
+
private defaultAccessToken: string;
|
|
12
|
+
private defaultRefreshToken: string;
|
|
13
|
+
|
|
14
|
+
constructor() {
|
|
15
|
+
this.defaultPayload = {
|
|
16
|
+
userId: '0',
|
|
17
|
+
email: 'admin@localhost',
|
|
18
|
+
roles: ['admin'],
|
|
19
|
+
fullName: 'Admin',
|
|
20
|
+
projectId: '0',
|
|
21
|
+
};
|
|
22
|
+
this.defaultAccessToken = 'accessToken';
|
|
23
|
+
this.defaultRefreshToken = 'refreshToken';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async refreshToken(_refreshToken: string): Promise<AuthResult> {
|
|
27
|
+
return {
|
|
28
|
+
accessToken: this.defaultAccessToken,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
signInMiddleware(_req: Request, _res: Response, _next: NextFunction): Promise<void> {
|
|
33
|
+
_res.cookie('refreshToken', this.defaultRefreshToken, {
|
|
34
|
+
httpOnly: true,
|
|
35
|
+
secure: true,
|
|
36
|
+
maxAge: 3600000,
|
|
37
|
+
});
|
|
38
|
+
return Promise.resolve(_res.redirect('/'));
|
|
39
|
+
}
|
|
40
|
+
signOutMiddleware(_req: Request, _res: Response, _next: NextFunction): Promise<void> {
|
|
41
|
+
_res.clearCookie('refreshToken');
|
|
42
|
+
return Promise.resolve(_res.redirect('/'));
|
|
43
|
+
}
|
|
44
|
+
accessTokenMiddleware(_req: Request, res: Response, _next: NextFunction): Promise<Response> {
|
|
45
|
+
return Promise.resolve(res.json({ accessToken: this.defaultAccessToken }));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
userApiMiddleware(_req: Request, res: Response, _next: NextFunction): Promise<Response<Payload>> {
|
|
49
|
+
return Promise.resolve(res.json(this.defaultPayload));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async initialize(): Promise<void> {
|
|
53
|
+
// Nothing to initialize
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async shutdown(): Promise<void> {
|
|
57
|
+
// Nothing to cleanup
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async introspectToken(_token: string): Promise<Payload | null> {
|
|
61
|
+
return this.defaultPayload;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async parseToken(_token: string): Promise<Payload | null> {
|
|
65
|
+
return this.defaultPayload;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async revokeToken(_token: string): Promise<void> {
|
|
69
|
+
// No-op for NULL provider
|
|
70
|
+
}
|
|
71
|
+
}
|
package/src/types/cli.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Payload } from './models.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Commands for adding a user to the IDP by app cli.
|
|
5
|
+
*/
|
|
6
|
+
export interface IdpProviderAddUserCommand {
|
|
7
|
+
addUser(username: string, password?: string): Promise<AddUserCommandResponse>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Commands for listing users from the IDP
|
|
12
|
+
*/
|
|
13
|
+
export interface IdpProviderListUsersCommand {
|
|
14
|
+
listUsers(): Promise<Payload[]>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Commands for removing a user from the IDP
|
|
19
|
+
*/
|
|
20
|
+
export interface IdpProviderRemoveUserCommand {
|
|
21
|
+
removeUser(userId: string): Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Response for adding a user to the IDP
|
|
26
|
+
*/
|
|
27
|
+
export interface AddUserCommandResponse {
|
|
28
|
+
username: string;
|
|
29
|
+
magicLink?: string;
|
|
30
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base error class for the IDP protocol
|
|
3
|
+
*/
|
|
4
|
+
export class IdpError extends Error {
|
|
5
|
+
constructor(
|
|
6
|
+
message: string,
|
|
7
|
+
public code: string,
|
|
8
|
+
public statusCode: number = 400
|
|
9
|
+
) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = 'IdpError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Authentication error
|
|
17
|
+
*/
|
|
18
|
+
export class AuthenticationError extends IdpError {
|
|
19
|
+
constructor(message: string = 'Authentication failed') {
|
|
20
|
+
super(message, 'AUTHENTICATION_ERROR', 401);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Authorization error
|
|
26
|
+
*/
|
|
27
|
+
export class AuthorizationError extends IdpError {
|
|
28
|
+
constructor(message: string = 'Insufficient permissions') {
|
|
29
|
+
super(message, 'AUTHORIZATION_ERROR', 403);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Token expired error
|
|
35
|
+
*/
|
|
36
|
+
export class TokenExpiredError extends IdpError {
|
|
37
|
+
constructor(message: string = 'Token has expired') {
|
|
38
|
+
super(message, 'TOKEN_EXPIRED', 401);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Invalid token error
|
|
44
|
+
*/
|
|
45
|
+
export class InvalidTokenError extends IdpError {
|
|
46
|
+
constructor(message: string = 'Invalid token') {
|
|
47
|
+
super(message, 'INVALID_TOKEN', 401);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main types export - organized by category
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Core provider interface
|
|
6
|
+
export * from './provider.js';
|
|
7
|
+
|
|
8
|
+
// Domain models
|
|
9
|
+
export * from './models.js';
|
|
10
|
+
|
|
11
|
+
// Configuration
|
|
12
|
+
export * from './config.js';
|
|
13
|
+
|
|
14
|
+
// Error classes
|
|
15
|
+
export * from './errors.js';
|
|
16
|
+
|
|
17
|
+
// CLI commands
|
|
18
|
+
export * from './cli.js';
|