@twin.org/api-auth-entity-storage-service 0.0.1-next.2 → 0.0.1-next.21
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/dist/cjs/index.cjs +24 -18
- package/dist/esm/index.mjs +25 -19
- package/dist/types/processors/authHeaderProcessor.d.ts +8 -4
- package/dist/types/services/entityStorageAuthenticationService.d.ts +4 -0
- package/dist/types/utils/tokenHelper.d.ts +3 -3
- package/docs/changelog.md +1 -1
- package/docs/reference/classes/AuthHeaderProcessor.md +15 -7
- package/docs/reference/classes/EntityStorageAuthenticationService.md +8 -0
- package/docs/reference/classes/TokenHelper.md +2 -10
- package/package.json +4 -31
package/dist/cjs/index.cjs
CHANGED
@@ -120,12 +120,13 @@ class TokenHelper {
|
|
120
120
|
* @returns The token if found.
|
121
121
|
*/
|
122
122
|
static extractTokenFromHeaders(headers, cookieName) {
|
123
|
-
const
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
123
|
+
const authHeader = headers?.[web.HeaderTypes.Authorization];
|
124
|
+
const cookiesHeader = headers?.[web.HeaderTypes.Cookie];
|
125
|
+
if (core.Is.string(authHeader) && authHeader.startsWith("Bearer ")) {
|
126
|
+
return {
|
127
|
+
token: authHeader.slice(7).trim(),
|
128
|
+
location: "authorization"
|
129
|
+
};
|
129
130
|
}
|
130
131
|
else if (core.Is.notEmpty(cookiesHeader) && core.Is.stringValue(cookieName)) {
|
131
132
|
const cookies = core.Is.arrayValue(cookiesHeader) ? cookiesHeader : [cookiesHeader];
|
@@ -136,17 +137,14 @@ class TokenHelper {
|
|
136
137
|
.map(c => c.trim())
|
137
138
|
.find(c => c.startsWith(cookieName));
|
138
139
|
if (core.Is.stringValue(accessTokenCookie)) {
|
139
|
-
|
140
|
-
|
141
|
-
|
140
|
+
return {
|
141
|
+
token: accessTokenCookie.slice(cookieName.length + 1).trim(),
|
142
|
+
location: "cookie"
|
143
|
+
};
|
142
144
|
}
|
143
145
|
}
|
144
146
|
}
|
145
147
|
}
|
146
|
-
return {
|
147
|
-
token,
|
148
|
-
location
|
149
|
-
};
|
150
148
|
}
|
151
149
|
}
|
152
150
|
|
@@ -156,6 +154,10 @@ class TokenHelper {
|
|
156
154
|
* Handle a JWT token in the authorization header or cookies and validate it to populate request context identity.
|
157
155
|
*/
|
158
156
|
class AuthHeaderProcessor {
|
157
|
+
/**
|
158
|
+
* The namespace supported by the processor.
|
159
|
+
*/
|
160
|
+
static NAMESPACE = "auth-header";
|
159
161
|
/**
|
160
162
|
* The default name for the access token as a cookie.
|
161
163
|
* @internal
|
@@ -218,10 +220,10 @@ class AuthHeaderProcessor {
|
|
218
220
|
if (!core.Is.empty(route) && !(route.skipAuth ?? false)) {
|
219
221
|
try {
|
220
222
|
const tokenAndLocation = TokenHelper.extractTokenFromHeaders(request.headers, this._cookieName);
|
221
|
-
const headerAndPayload = await TokenHelper.verify(this._vaultConnector, `${this._nodeIdentity}/${this._signingKeyName}`, tokenAndLocation
|
223
|
+
const headerAndPayload = await TokenHelper.verify(this._vaultConnector, `${this._nodeIdentity}/${this._signingKeyName}`, tokenAndLocation?.token);
|
222
224
|
requestIdentity.userIdentity = headerAndPayload.payload?.sub;
|
223
|
-
processorState.authToken = tokenAndLocation
|
224
|
-
processorState.authTokenLocation = tokenAndLocation
|
225
|
+
processorState.authToken = tokenAndLocation?.token;
|
226
|
+
processorState.authTokenLocation = tokenAndLocation?.location;
|
225
227
|
}
|
226
228
|
catch (err) {
|
227
229
|
const error = core.BaseError.fromError(err);
|
@@ -246,13 +248,13 @@ class AuthHeaderProcessor {
|
|
246
248
|
if ((responseAuthOperation === "login" || responseAuthOperation === "refresh") &&
|
247
249
|
core.Is.stringValue(response.body?.token)) {
|
248
250
|
response.headers ??= {};
|
249
|
-
response.headers[
|
251
|
+
response.headers[web.HeaderTypes.SetCookie] =
|
250
252
|
`${this._cookieName}=${response.body.token}; Secure; HttpOnly; SameSite=None; Path=/`;
|
251
253
|
delete response.body.token;
|
252
254
|
}
|
253
255
|
else if (responseAuthOperation === "logout") {
|
254
256
|
response.headers ??= {};
|
255
|
-
response.headers[
|
257
|
+
response.headers[web.HeaderTypes.SetCookie] =
|
256
258
|
`${this._cookieName}=; Max-Age=0; Secure; HttpOnly; SameSite=None; Path=/`;
|
257
259
|
}
|
258
260
|
}
|
@@ -504,6 +506,10 @@ class PasswordHelper {
|
|
504
506
|
* Implementation of the authentication component using entity storage.
|
505
507
|
*/
|
506
508
|
class EntityStorageAuthenticationService {
|
509
|
+
/**
|
510
|
+
* The namespace supported by the authentication service.
|
511
|
+
*/
|
512
|
+
static NAMESPACE = "authentication-entity-storage";
|
507
513
|
/**
|
508
514
|
* Default TTL in minutes.
|
509
515
|
* @internal
|
package/dist/esm/index.mjs
CHANGED
@@ -2,7 +2,7 @@ import { property, entity, EntitySchemaFactory, EntitySchemaHelper } from '@twin
|
|
2
2
|
import { HttpErrorHelper } from '@twin.org/api-models';
|
3
3
|
import { Is, UnauthorizedError, Guards, BaseError, ComponentFactory, Converter, GeneralError } from '@twin.org/core';
|
4
4
|
import { VaultConnectorFactory } from '@twin.org/vault-models';
|
5
|
-
import { Jwt, JwtAlgorithms, HttpStatusCode } from '@twin.org/web';
|
5
|
+
import { Jwt, JwtAlgorithms, HeaderTypes, HttpStatusCode } from '@twin.org/web';
|
6
6
|
import { EntityStorageConnectorFactory } from '@twin.org/entity-storage-models';
|
7
7
|
import { Blake2b } from '@twin.org/crypto';
|
8
8
|
|
@@ -118,12 +118,13 @@ class TokenHelper {
|
|
118
118
|
* @returns The token if found.
|
119
119
|
*/
|
120
120
|
static extractTokenFromHeaders(headers, cookieName) {
|
121
|
-
const
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
121
|
+
const authHeader = headers?.[HeaderTypes.Authorization];
|
122
|
+
const cookiesHeader = headers?.[HeaderTypes.Cookie];
|
123
|
+
if (Is.string(authHeader) && authHeader.startsWith("Bearer ")) {
|
124
|
+
return {
|
125
|
+
token: authHeader.slice(7).trim(),
|
126
|
+
location: "authorization"
|
127
|
+
};
|
127
128
|
}
|
128
129
|
else if (Is.notEmpty(cookiesHeader) && Is.stringValue(cookieName)) {
|
129
130
|
const cookies = Is.arrayValue(cookiesHeader) ? cookiesHeader : [cookiesHeader];
|
@@ -134,17 +135,14 @@ class TokenHelper {
|
|
134
135
|
.map(c => c.trim())
|
135
136
|
.find(c => c.startsWith(cookieName));
|
136
137
|
if (Is.stringValue(accessTokenCookie)) {
|
137
|
-
|
138
|
-
|
139
|
-
|
138
|
+
return {
|
139
|
+
token: accessTokenCookie.slice(cookieName.length + 1).trim(),
|
140
|
+
location: "cookie"
|
141
|
+
};
|
140
142
|
}
|
141
143
|
}
|
142
144
|
}
|
143
145
|
}
|
144
|
-
return {
|
145
|
-
token,
|
146
|
-
location
|
147
|
-
};
|
148
146
|
}
|
149
147
|
}
|
150
148
|
|
@@ -154,6 +152,10 @@ class TokenHelper {
|
|
154
152
|
* Handle a JWT token in the authorization header or cookies and validate it to populate request context identity.
|
155
153
|
*/
|
156
154
|
class AuthHeaderProcessor {
|
155
|
+
/**
|
156
|
+
* The namespace supported by the processor.
|
157
|
+
*/
|
158
|
+
static NAMESPACE = "auth-header";
|
157
159
|
/**
|
158
160
|
* The default name for the access token as a cookie.
|
159
161
|
* @internal
|
@@ -216,10 +218,10 @@ class AuthHeaderProcessor {
|
|
216
218
|
if (!Is.empty(route) && !(route.skipAuth ?? false)) {
|
217
219
|
try {
|
218
220
|
const tokenAndLocation = TokenHelper.extractTokenFromHeaders(request.headers, this._cookieName);
|
219
|
-
const headerAndPayload = await TokenHelper.verify(this._vaultConnector, `${this._nodeIdentity}/${this._signingKeyName}`, tokenAndLocation
|
221
|
+
const headerAndPayload = await TokenHelper.verify(this._vaultConnector, `${this._nodeIdentity}/${this._signingKeyName}`, tokenAndLocation?.token);
|
220
222
|
requestIdentity.userIdentity = headerAndPayload.payload?.sub;
|
221
|
-
processorState.authToken = tokenAndLocation
|
222
|
-
processorState.authTokenLocation = tokenAndLocation
|
223
|
+
processorState.authToken = tokenAndLocation?.token;
|
224
|
+
processorState.authTokenLocation = tokenAndLocation?.location;
|
223
225
|
}
|
224
226
|
catch (err) {
|
225
227
|
const error = BaseError.fromError(err);
|
@@ -244,13 +246,13 @@ class AuthHeaderProcessor {
|
|
244
246
|
if ((responseAuthOperation === "login" || responseAuthOperation === "refresh") &&
|
245
247
|
Is.stringValue(response.body?.token)) {
|
246
248
|
response.headers ??= {};
|
247
|
-
response.headers[
|
249
|
+
response.headers[HeaderTypes.SetCookie] =
|
248
250
|
`${this._cookieName}=${response.body.token}; Secure; HttpOnly; SameSite=None; Path=/`;
|
249
251
|
delete response.body.token;
|
250
252
|
}
|
251
253
|
else if (responseAuthOperation === "logout") {
|
252
254
|
response.headers ??= {};
|
253
|
-
response.headers[
|
255
|
+
response.headers[HeaderTypes.SetCookie] =
|
254
256
|
`${this._cookieName}=; Max-Age=0; Secure; HttpOnly; SameSite=None; Path=/`;
|
255
257
|
}
|
256
258
|
}
|
@@ -502,6 +504,10 @@ class PasswordHelper {
|
|
502
504
|
* Implementation of the authentication component using entity storage.
|
503
505
|
*/
|
504
506
|
class EntityStorageAuthenticationService {
|
507
|
+
/**
|
508
|
+
* The namespace supported by the authentication service.
|
509
|
+
*/
|
510
|
+
static NAMESPACE = "authentication-entity-storage";
|
505
511
|
/**
|
506
512
|
* Default TTL in minutes.
|
507
513
|
* @internal
|
@@ -1,9 +1,13 @@
|
|
1
|
-
import { type
|
1
|
+
import { type IBaseRoute, type IBaseRouteProcessor, type IHttpRequestIdentity, type IHttpResponse, type IHttpServerRequest } from "@twin.org/api-models";
|
2
2
|
import type { IAuthHeaderProcessorConfig } from "../models/IAuthHeaderProcessorConfig";
|
3
3
|
/**
|
4
4
|
* Handle a JWT token in the authorization header or cookies and validate it to populate request context identity.
|
5
5
|
*/
|
6
|
-
export declare class AuthHeaderProcessor implements
|
6
|
+
export declare class AuthHeaderProcessor implements IBaseRouteProcessor {
|
7
|
+
/**
|
8
|
+
* The namespace supported by the processor.
|
9
|
+
*/
|
10
|
+
static readonly NAMESPACE: string;
|
7
11
|
/**
|
8
12
|
* Runtime name for the class.
|
9
13
|
*/
|
@@ -33,7 +37,7 @@ export declare class AuthHeaderProcessor implements IHttpRestRouteProcessor {
|
|
33
37
|
* @param requestIdentity The identity context for the request.
|
34
38
|
* @param processorState The state handed through the processors.
|
35
39
|
*/
|
36
|
-
pre(request: IHttpServerRequest, response: IHttpResponse, route:
|
40
|
+
pre(request: IHttpServerRequest, response: IHttpResponse, route: IBaseRoute | undefined, requestIdentity: IHttpRequestIdentity, processorState: {
|
37
41
|
[id: string]: unknown;
|
38
42
|
}): Promise<void>;
|
39
43
|
/**
|
@@ -44,7 +48,7 @@ export declare class AuthHeaderProcessor implements IHttpRestRouteProcessor {
|
|
44
48
|
* @param requestIdentity The identity context for the request.
|
45
49
|
* @param processorState The state handed through the processors.
|
46
50
|
*/
|
47
|
-
post(request: IHttpServerRequest, response: IHttpResponse, route:
|
51
|
+
post(request: IHttpServerRequest, response: IHttpResponse, route: IBaseRoute | undefined, requestIdentity: IHttpRequestIdentity, processorState: {
|
48
52
|
[id: string]: unknown;
|
49
53
|
}): Promise<void>;
|
50
54
|
}
|
@@ -4,6 +4,10 @@ import type { IEntityStorageAuthenticationServiceConfig } from "../models/IEntit
|
|
4
4
|
* Implementation of the authentication component using entity storage.
|
5
5
|
*/
|
6
6
|
export declare class EntityStorageAuthenticationService implements IAuthenticationComponent {
|
7
|
+
/**
|
8
|
+
* The namespace supported by the authentication service.
|
9
|
+
*/
|
10
|
+
static readonly NAMESPACE: string;
|
7
11
|
/**
|
8
12
|
* Runtime name for the class.
|
9
13
|
*/
|
@@ -35,7 +35,7 @@ export declare class TokenHelper {
|
|
35
35
|
* @returns The token if found.
|
36
36
|
*/
|
37
37
|
static extractTokenFromHeaders(headers?: IHttpHeaders, cookieName?: string): {
|
38
|
-
token: string
|
39
|
-
location: "authorization" | "cookie"
|
40
|
-
};
|
38
|
+
token: string;
|
39
|
+
location: "authorization" | "cookie";
|
40
|
+
} | undefined;
|
41
41
|
}
|
package/docs/changelog.md
CHANGED
@@ -4,7 +4,7 @@ Handle a JWT token in the authorization header or cookies and validate it to pop
|
|
4
4
|
|
5
5
|
## Implements
|
6
6
|
|
7
|
-
- `
|
7
|
+
- `IBaseRouteProcessor`
|
8
8
|
|
9
9
|
## Constructors
|
10
10
|
|
@@ -34,6 +34,14 @@ The configuration for the processor.
|
|
34
34
|
|
35
35
|
## Properties
|
36
36
|
|
37
|
+
### NAMESPACE
|
38
|
+
|
39
|
+
> `readonly` `static` **NAMESPACE**: `string` = `"auth-header"`
|
40
|
+
|
41
|
+
The namespace supported by the processor.
|
42
|
+
|
43
|
+
***
|
44
|
+
|
37
45
|
### CLASS\_NAME
|
38
46
|
|
39
47
|
> `readonly` **CLASS\_NAME**: `string`
|
@@ -42,7 +50,7 @@ Runtime name for the class.
|
|
42
50
|
|
43
51
|
#### Implementation of
|
44
52
|
|
45
|
-
`
|
53
|
+
`IBaseRouteProcessor.CLASS_NAME`
|
46
54
|
|
47
55
|
## Methods
|
48
56
|
|
@@ -70,7 +78,7 @@ Nothing.
|
|
70
78
|
|
71
79
|
#### Implementation of
|
72
80
|
|
73
|
-
`
|
81
|
+
`IBaseRouteProcessor.start`
|
74
82
|
|
75
83
|
***
|
76
84
|
|
@@ -90,7 +98,7 @@ The incoming request.
|
|
90
98
|
|
91
99
|
The outgoing response.
|
92
100
|
|
93
|
-
• **route**: `undefined` \| `
|
101
|
+
• **route**: `undefined` \| `IBaseRoute`
|
94
102
|
|
95
103
|
The route to process.
|
96
104
|
|
@@ -108,7 +116,7 @@ The state handed through the processors.
|
|
108
116
|
|
109
117
|
#### Implementation of
|
110
118
|
|
111
|
-
`
|
119
|
+
`IBaseRouteProcessor.pre`
|
112
120
|
|
113
121
|
***
|
114
122
|
|
@@ -128,7 +136,7 @@ The incoming request.
|
|
128
136
|
|
129
137
|
The outgoing response.
|
130
138
|
|
131
|
-
• **route**: `undefined` \| `
|
139
|
+
• **route**: `undefined` \| `IBaseRoute`
|
132
140
|
|
133
141
|
The route to process.
|
134
142
|
|
@@ -146,4 +154,4 @@ The state handed through the processors.
|
|
146
154
|
|
147
155
|
#### Implementation of
|
148
156
|
|
149
|
-
`
|
157
|
+
`IBaseRouteProcessor.post`
|
@@ -38,6 +38,14 @@ The configuration for the authentication.
|
|
38
38
|
|
39
39
|
## Properties
|
40
40
|
|
41
|
+
### NAMESPACE
|
42
|
+
|
43
|
+
> `readonly` `static` **NAMESPACE**: `string` = `"authentication-entity-storage"`
|
44
|
+
|
45
|
+
The namespace supported by the authentication service.
|
46
|
+
|
47
|
+
***
|
48
|
+
|
41
49
|
### CLASS\_NAME
|
42
50
|
|
43
51
|
> `readonly` **CLASS\_NAME**: `string`
|
@@ -96,7 +96,7 @@ UnauthorizedError if the token is missing, invalid or expired.
|
|
96
96
|
|
97
97
|
### extractTokenFromHeaders()
|
98
98
|
|
99
|
-
> `static` **extractTokenFromHeaders**(`headers`?, `cookieName`?): `object`
|
99
|
+
> `static` **extractTokenFromHeaders**(`headers`?, `cookieName`?): `undefined` \| `object`
|
100
100
|
|
101
101
|
Extract the auth token from the headers, either from the authorization header or the cookie header.
|
102
102
|
|
@@ -112,14 +112,6 @@ The name of the cookie to extract the token from.
|
|
112
112
|
|
113
113
|
#### Returns
|
114
114
|
|
115
|
-
`object`
|
115
|
+
`undefined` \| `object`
|
116
116
|
|
117
117
|
The token if found.
|
118
|
-
|
119
|
-
##### token
|
120
|
-
|
121
|
-
> **token**: `undefined` \| `string`
|
122
|
-
|
123
|
-
##### location
|
124
|
-
|
125
|
-
> **location**: `undefined` \| `"authorization"` \| `"cookie"`
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@twin.org/api-auth-entity-storage-service",
|
3
|
-
"version": "0.0.1-next.
|
3
|
+
"version": "0.0.1-next.21",
|
4
4
|
"description": "Auth Entity Storage contract implementation and REST endpoint definitions",
|
5
5
|
"repository": {
|
6
6
|
"type": "git",
|
@@ -13,23 +13,10 @@
|
|
13
13
|
"engines": {
|
14
14
|
"node": ">=20.0.0"
|
15
15
|
},
|
16
|
-
"scripts": {
|
17
|
-
"clean": "rimraf dist coverage",
|
18
|
-
"build": "tspc",
|
19
|
-
"test": "vitest --run --config ./vitest.config.ts --no-cache",
|
20
|
-
"coverage": "vitest --run --coverage --config ./vitest.config.ts --no-cache",
|
21
|
-
"bundle:esm": "rollup --config rollup.config.mjs --environment MODULE:esm",
|
22
|
-
"bundle:cjs": "rollup --config rollup.config.mjs --environment MODULE:cjs",
|
23
|
-
"bundle": "npm run bundle:esm && npm run bundle:cjs",
|
24
|
-
"docs:clean": "rimraf docs/reference",
|
25
|
-
"docs:generate": "typedoc",
|
26
|
-
"docs": "npm run docs:clean && npm run docs:generate",
|
27
|
-
"dist": "npm run clean && npm run build && npm run test && npm run bundle && npm run docs"
|
28
|
-
},
|
29
16
|
"dependencies": {
|
30
|
-
"@twin.org/api-auth-entity-storage-models": "0.0.1-next.
|
31
|
-
"@twin.org/api-core": "0.0.1-next.
|
32
|
-
"@twin.org/api-models": "0.0.1-next.
|
17
|
+
"@twin.org/api-auth-entity-storage-models": "0.0.1-next.21",
|
18
|
+
"@twin.org/api-core": "0.0.1-next.21",
|
19
|
+
"@twin.org/api-models": "0.0.1-next.21",
|
33
20
|
"@twin.org/core": "next",
|
34
21
|
"@twin.org/crypto": "next",
|
35
22
|
"@twin.org/entity": "next",
|
@@ -39,20 +26,6 @@
|
|
39
26
|
"@twin.org/vault-models": "next",
|
40
27
|
"@twin.org/web": "next"
|
41
28
|
},
|
42
|
-
"devDependencies": {
|
43
|
-
"@twin.org/nameof-transformer": "next",
|
44
|
-
"@vitest/coverage-v8": "2.1.1",
|
45
|
-
"@types/node": "22.5.5",
|
46
|
-
"copyfiles": "2.4.1",
|
47
|
-
"rimraf": "6.0.1",
|
48
|
-
"rollup": "4.21.3",
|
49
|
-
"rollup-plugin-typescript2": "0.36.0",
|
50
|
-
"ts-patch": "3.2.1",
|
51
|
-
"typedoc": "0.26.7",
|
52
|
-
"typedoc-plugin-markdown": "4.2.7",
|
53
|
-
"typescript": "5.6.2",
|
54
|
-
"vitest": "2.1.1"
|
55
|
-
},
|
56
29
|
"main": "./dist/cjs/index.cjs",
|
57
30
|
"module": "./dist/esm/index.mjs",
|
58
31
|
"types": "./dist/types/index.d.ts",
|