@dvsa/appdev-api-common 0.3.3 → 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/auth/auth-checker.d.ts +2 -1
- package/auth/auth-checker.js +2 -1
- package/auth/client-credentials.d.ts +24 -1
- package/auth/client-credentials.js +25 -2
- package/auth/verify-jwt.d.ts +5 -0
- package/auth/verify-jwt.js +5 -0
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/jest.config.ts +8 -0
- package/package.json +4 -1
- package/utils/date-time.d.ts +24 -0
- package/utils/date-time.js +85 -0
package/auth/auth-checker.d.ts
CHANGED
|
@@ -8,7 +8,8 @@ export declare class JWTAuthChecker {
|
|
|
8
8
|
/**
|
|
9
9
|
* Perform a JWT token verification and role check
|
|
10
10
|
* @param {RoutingControllersRequest} request
|
|
11
|
-
* @param roles
|
|
11
|
+
* @param {string | string[]} roles
|
|
12
|
+
* @returns {Promise<boolean>}
|
|
12
13
|
*/
|
|
13
14
|
static execute({ request }: Action, roles?: string | string[]): Promise<boolean>;
|
|
14
15
|
}
|
package/auth/auth-checker.js
CHANGED
|
@@ -9,7 +9,8 @@ class JWTAuthChecker {
|
|
|
9
9
|
/**
|
|
10
10
|
* Perform a JWT token verification and role check
|
|
11
11
|
* @param {RoutingControllersRequest} request
|
|
12
|
-
* @param roles
|
|
12
|
+
* @param {string | string[]} roles
|
|
13
|
+
* @returns {Promise<boolean>}
|
|
13
14
|
*/
|
|
14
15
|
static async execute({ request }, roles = []) {
|
|
15
16
|
// if running locally, skip the token auth and role check
|
|
@@ -11,9 +11,32 @@ export declare class ClientCredentials {
|
|
|
11
11
|
private readonly scope;
|
|
12
12
|
private readonly debugMode;
|
|
13
13
|
private static accessToken;
|
|
14
|
-
private static readonly
|
|
14
|
+
private static readonly grantType;
|
|
15
|
+
/**
|
|
16
|
+
* Create a new instance of the ClientCredentials class
|
|
17
|
+
* @param tokenUrl - The URL to fetch the access token from
|
|
18
|
+
* @param clientId - The client id
|
|
19
|
+
* @param clientSecret - The client secret
|
|
20
|
+
* @param scope - The scope of the access token
|
|
21
|
+
* @param debugMode - Whether to log debug messages
|
|
22
|
+
*/
|
|
15
23
|
constructor(tokenUrl: string, clientId: string, clientSecret: string, scope: string, debugMode?: boolean);
|
|
24
|
+
/**
|
|
25
|
+
* Helper method to perform the client credentials flow and return the access token
|
|
26
|
+
* This method will check for the existence of the token and if it is expired, it will fetch a new one
|
|
27
|
+
* @returns {Promise<string>} - The access token
|
|
28
|
+
*/
|
|
16
29
|
getAccessToken(): Promise<string>;
|
|
30
|
+
/**
|
|
31
|
+
* Fetch the client credentials from the token URL
|
|
32
|
+
* @returns {Promise<ClientCredentialsResponse>} - The response from the token URL
|
|
33
|
+
* @private
|
|
34
|
+
*/
|
|
17
35
|
private fetchClientCredentials;
|
|
36
|
+
/**
|
|
37
|
+
* Check if the access token is expired
|
|
38
|
+
* @returns {boolean} - Whether the access token is expired
|
|
39
|
+
* @private
|
|
40
|
+
*/
|
|
18
41
|
private isAccessTokenExpired;
|
|
19
42
|
}
|
|
@@ -10,7 +10,15 @@ class ClientCredentials {
|
|
|
10
10
|
scope;
|
|
11
11
|
debugMode;
|
|
12
12
|
static accessToken;
|
|
13
|
-
static
|
|
13
|
+
static grantType = "client_credentials";
|
|
14
|
+
/**
|
|
15
|
+
* Create a new instance of the ClientCredentials class
|
|
16
|
+
* @param tokenUrl - The URL to fetch the access token from
|
|
17
|
+
* @param clientId - The client id
|
|
18
|
+
* @param clientSecret - The client secret
|
|
19
|
+
* @param scope - The scope of the access token
|
|
20
|
+
* @param debugMode - Whether to log debug messages
|
|
21
|
+
*/
|
|
14
22
|
constructor(tokenUrl, clientId, clientSecret, scope, debugMode = false) {
|
|
15
23
|
this.tokenUrl = tokenUrl;
|
|
16
24
|
this.clientId = clientId;
|
|
@@ -18,6 +26,11 @@ class ClientCredentials {
|
|
|
18
26
|
this.scope = scope;
|
|
19
27
|
this.debugMode = debugMode;
|
|
20
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Helper method to perform the client credentials flow and return the access token
|
|
31
|
+
* This method will check for the existence of the token and if it is expired, it will fetch a new one
|
|
32
|
+
* @returns {Promise<string>} - The access token
|
|
33
|
+
*/
|
|
21
34
|
async getAccessToken() {
|
|
22
35
|
if (!ClientCredentials.accessToken || this.isAccessTokenExpired()) {
|
|
23
36
|
const { access_token } = await this.fetchClientCredentials();
|
|
@@ -30,12 +43,17 @@ class ClientCredentials {
|
|
|
30
43
|
}
|
|
31
44
|
return ClientCredentials.accessToken;
|
|
32
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Fetch the client credentials from the token URL
|
|
48
|
+
* @returns {Promise<ClientCredentialsResponse>} - The response from the token URL
|
|
49
|
+
* @private
|
|
50
|
+
*/
|
|
33
51
|
async fetchClientCredentials() {
|
|
34
52
|
const response = await fetch(this.tokenUrl, {
|
|
35
53
|
method: "POST",
|
|
36
54
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
37
55
|
body: (0, node_querystring_1.stringify)({
|
|
38
|
-
grant_type: ClientCredentials.
|
|
56
|
+
grant_type: ClientCredentials.grantType,
|
|
39
57
|
client_id: this.clientId,
|
|
40
58
|
client_secret: this.clientSecret,
|
|
41
59
|
scope: this.scope,
|
|
@@ -47,6 +65,11 @@ class ClientCredentials {
|
|
|
47
65
|
}
|
|
48
66
|
return (await response.json());
|
|
49
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Check if the access token is expired
|
|
70
|
+
* @returns {boolean} - Whether the access token is expired
|
|
71
|
+
* @private
|
|
72
|
+
*/
|
|
50
73
|
isAccessTokenExpired() {
|
|
51
74
|
try {
|
|
52
75
|
const decodedAccessToken = (0, jose_1.decodeJwt)(ClientCredentials.accessToken);
|
package/auth/verify-jwt.d.ts
CHANGED
|
@@ -5,9 +5,14 @@ export declare class JwtAuthoriser {
|
|
|
5
5
|
private static readonly tokenExpiryEnvExclusionList;
|
|
6
6
|
private static readonly JWKS_URI;
|
|
7
7
|
private static JWKS;
|
|
8
|
+
/**
|
|
9
|
+
* Create a new instance of the JwtAuthoriser class
|
|
10
|
+
* @param clientId - the client id to validate the token against
|
|
11
|
+
*/
|
|
8
12
|
constructor(clientId?: string | null);
|
|
9
13
|
/**
|
|
10
14
|
* Validate a JWT and return the decoded payload
|
|
15
|
+
* @param {string} token - the JWT token to validate
|
|
11
16
|
* @returns {Promise<JWTPayload>}
|
|
12
17
|
*/
|
|
13
18
|
verify(token: string): Promise<JWTPayload>;
|
package/auth/verify-jwt.js
CHANGED
|
@@ -13,11 +13,16 @@ class JwtAuthoriser {
|
|
|
13
13
|
];
|
|
14
14
|
static JWKS_URI = new URL("https://login.microsoftonline.com/common/discovery/keys");
|
|
15
15
|
static JWKS = (0, jose_1.createRemoteJWKSet)(JwtAuthoriser.JWKS_URI);
|
|
16
|
+
/**
|
|
17
|
+
* Create a new instance of the JwtAuthoriser class
|
|
18
|
+
* @param clientId - the client id to validate the token against
|
|
19
|
+
*/
|
|
16
20
|
constructor(clientId = null) {
|
|
17
21
|
this.clientId = clientId;
|
|
18
22
|
}
|
|
19
23
|
/**
|
|
20
24
|
* Validate a JWT and return the decoded payload
|
|
25
|
+
* @param {string} token - the JWT token to validate
|
|
21
26
|
* @returns {Promise<JWTPayload>}
|
|
22
27
|
*/
|
|
23
28
|
async verify(token) {
|
package/index.d.ts
CHANGED
package/index.js
CHANGED
|
@@ -19,4 +19,5 @@ __exportStar(require("./auth/auth-checker"), exports);
|
|
|
19
19
|
__exportStar(require("./auth/auth-errors"), exports);
|
|
20
20
|
__exportStar(require("./auth/client-credentials"), exports);
|
|
21
21
|
__exportStar(require("./auth/verify-jwt"), exports);
|
|
22
|
+
__exportStar(require("./utils/date-time"), exports);
|
|
22
23
|
__exportStar(require("./validation/request-body"), exports);
|
package/jest.config.ts
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dvsa/appdev-api-common",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"dvsa",
|
|
6
6
|
"nodejs",
|
|
@@ -20,12 +20,15 @@
|
|
|
20
20
|
"lint:fix": "npm run lint -- --write",
|
|
21
21
|
"build": "npm run clean && tsc",
|
|
22
22
|
"build:package": "npm run build",
|
|
23
|
+
"test": "jest",
|
|
24
|
+
"test:coverage": "jest --coverage",
|
|
23
25
|
"prepublishOnly": "npm run build:package && cp -r ./dist/* . && rm -rf ./dist",
|
|
24
26
|
"postpublish": "git clean -fd && npm run clean:temp",
|
|
25
27
|
"gitSecrets": "git secrets --scan . && git log -p -- . | scanrepo"
|
|
26
28
|
},
|
|
27
29
|
"dependencies": {
|
|
28
30
|
"ajv": "^8.17.1",
|
|
31
|
+
"dayjs": "^1.11.13",
|
|
29
32
|
"jose": "^5.9.6"
|
|
30
33
|
},
|
|
31
34
|
"devDependencies": {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import dayjs from "dayjs";
|
|
2
|
+
type AcceptableDate = DateTime | string | Date | null;
|
|
3
|
+
export declare class DateTime {
|
|
4
|
+
private instance;
|
|
5
|
+
private static readonly UKLocalDateTimeFormat;
|
|
6
|
+
private static readonly UKLocalDateFormat;
|
|
7
|
+
constructor(sourceDateTime?: AcceptableDate, format?: string | undefined);
|
|
8
|
+
static at(sourceDateTime: AcceptableDate, format?: string | undefined): DateTime | null;
|
|
9
|
+
static StandardUkLocalDateTimeAdapter(sourceDateTime: AcceptableDate): string | null;
|
|
10
|
+
static StandardUkLocalDateAdapter(sourceDateTime: AcceptableDate): string | null;
|
|
11
|
+
add(amount: number, unit: dayjs.ManipulateType): DateTime;
|
|
12
|
+
subtract(amount: number, unit: dayjs.ManipulateType): DateTime;
|
|
13
|
+
format(formatString: string): string;
|
|
14
|
+
day(): number;
|
|
15
|
+
toString(): string;
|
|
16
|
+
toISOString(): string;
|
|
17
|
+
isAfter(targetDate: AcceptableDate): boolean;
|
|
18
|
+
diff(targetDate: AcceptableDate, unit: dayjs.QUnitType, precise?: boolean): number;
|
|
19
|
+
daysDiff(targetDate: AcceptableDate): number;
|
|
20
|
+
compareDuration(targetDate: AcceptableDate, unit: dayjs.QUnitType): number;
|
|
21
|
+
isBefore(targetDate: AcceptableDate): boolean;
|
|
22
|
+
static today(): Date;
|
|
23
|
+
}
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DateTime = void 0;
|
|
7
|
+
const dayjs_1 = __importDefault(require("dayjs"));
|
|
8
|
+
const customParseFormat_1 = __importDefault(require("dayjs/plugin/customParseFormat"));
|
|
9
|
+
class DateTime {
|
|
10
|
+
instance;
|
|
11
|
+
static UKLocalDateTimeFormat = "DD/MM/YYYY HH:mm:ss";
|
|
12
|
+
static UKLocalDateFormat = "DD/MM/YYYY";
|
|
13
|
+
constructor(sourceDateTime, format = undefined) {
|
|
14
|
+
dayjs_1.default.extend(customParseFormat_1.default);
|
|
15
|
+
if (sourceDateTime === undefined || sourceDateTime === null) {
|
|
16
|
+
this.instance = (0, dayjs_1.default)();
|
|
17
|
+
}
|
|
18
|
+
else if (typeof sourceDateTime === "string" ||
|
|
19
|
+
sourceDateTime instanceof Date) {
|
|
20
|
+
this.instance = (0, dayjs_1.default)(sourceDateTime, format);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
this.instance = (0, dayjs_1.default)(sourceDateTime.instance, format);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
static at(sourceDateTime, format = undefined) {
|
|
27
|
+
if (!sourceDateTime) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
return new DateTime(sourceDateTime, format);
|
|
31
|
+
}
|
|
32
|
+
static StandardUkLocalDateTimeAdapter(sourceDateTime) {
|
|
33
|
+
return (DateTime.at(sourceDateTime)?.format(DateTime.UKLocalDateTimeFormat) ||
|
|
34
|
+
null);
|
|
35
|
+
}
|
|
36
|
+
static StandardUkLocalDateAdapter(sourceDateTime) {
|
|
37
|
+
return (DateTime.at(sourceDateTime)?.format(DateTime.UKLocalDateFormat) || null);
|
|
38
|
+
}
|
|
39
|
+
add(amount, unit) {
|
|
40
|
+
this.instance = this.instance.add(amount, unit);
|
|
41
|
+
return this;
|
|
42
|
+
}
|
|
43
|
+
subtract(amount, unit) {
|
|
44
|
+
this.instance = this.instance.subtract(amount, unit);
|
|
45
|
+
return this;
|
|
46
|
+
}
|
|
47
|
+
format(formatString) {
|
|
48
|
+
return this.instance.format(formatString);
|
|
49
|
+
}
|
|
50
|
+
day() {
|
|
51
|
+
return this.instance.day();
|
|
52
|
+
}
|
|
53
|
+
toString() {
|
|
54
|
+
return this.instance.toString();
|
|
55
|
+
}
|
|
56
|
+
toISOString() {
|
|
57
|
+
return this.instance.toISOString();
|
|
58
|
+
}
|
|
59
|
+
isAfter(targetDate) {
|
|
60
|
+
const date = new DateTime(targetDate);
|
|
61
|
+
return this.instance.isAfter(date.instance);
|
|
62
|
+
}
|
|
63
|
+
diff(targetDate, unit, precise) {
|
|
64
|
+
const date = new DateTime(targetDate);
|
|
65
|
+
return this.instance.diff(date.instance, unit, precise);
|
|
66
|
+
}
|
|
67
|
+
daysDiff(targetDate) {
|
|
68
|
+
const date = new DateTime(targetDate);
|
|
69
|
+
return this.instance
|
|
70
|
+
.startOf("day")
|
|
71
|
+
.diff(date.instance.startOf("day"), "day");
|
|
72
|
+
}
|
|
73
|
+
compareDuration(targetDate, unit) {
|
|
74
|
+
const date = new DateTime(targetDate);
|
|
75
|
+
return date.instance.diff(this.instance, unit);
|
|
76
|
+
}
|
|
77
|
+
isBefore(targetDate) {
|
|
78
|
+
const date = new DateTime(targetDate);
|
|
79
|
+
return this.instance.isBefore(date.instance);
|
|
80
|
+
}
|
|
81
|
+
static today() {
|
|
82
|
+
return (0, dayjs_1.default)().toDate();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
exports.DateTime = DateTime;
|