@dvsa/appdev-api-common 1.2.0 → 2.0.0-canary.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/oidc.d.ts +39 -0
- package/auth/oidc.js +98 -0
- package/package.json +7 -2
package/auth/oidc.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export interface AzureTokenResponse {
|
|
2
|
+
token_type: string;
|
|
3
|
+
expires_in: number;
|
|
4
|
+
ext_expires_in?: number;
|
|
5
|
+
access_token: string;
|
|
6
|
+
}
|
|
7
|
+
export declare class AwsToAzureFederatedCredentials {
|
|
8
|
+
private readonly tenantId;
|
|
9
|
+
private readonly clientID;
|
|
10
|
+
private readonly tokenDurationSeconds;
|
|
11
|
+
private readonly debugMode;
|
|
12
|
+
private static accessToken;
|
|
13
|
+
private static readonly stsClient;
|
|
14
|
+
/**
|
|
15
|
+
* Create a new instance of the AwsToAzureFederatedCredentials class
|
|
16
|
+
* @param tenantId - The Azure AD tenant ID
|
|
17
|
+
* @param clientID - The Azure AD application (client) ID
|
|
18
|
+
* @param tokenDurationSeconds - Duration for the AWS token (default: 300)
|
|
19
|
+
* @param debugMode - Whether to log debug messages
|
|
20
|
+
*/
|
|
21
|
+
constructor(tenantId: string, clientID: string, tokenDurationSeconds?: number, debugMode?: boolean);
|
|
22
|
+
/**
|
|
23
|
+
* Get the Azure access token, fetching a new one if needed
|
|
24
|
+
* @returns {Promise<string>} - The Azure access token
|
|
25
|
+
*/
|
|
26
|
+
getAccessToken(): Promise<string>;
|
|
27
|
+
/**
|
|
28
|
+
* Fetch the AWS JWT and exchange it for an Azure token
|
|
29
|
+
* @returns {Promise<AzureTokenResponse>} - The Azure token response
|
|
30
|
+
* @private
|
|
31
|
+
*/
|
|
32
|
+
private fetchFederatedCredentials;
|
|
33
|
+
/**
|
|
34
|
+
* Check if the access token is expired
|
|
35
|
+
* @returns {boolean} - Whether the access token is expired
|
|
36
|
+
* @private
|
|
37
|
+
*/
|
|
38
|
+
private isAccessTokenExpired;
|
|
39
|
+
}
|
package/auth/oidc.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AwsToAzureFederatedCredentials = void 0;
|
|
4
|
+
const client_sts_1 = require("@aws-sdk/client-sts");
|
|
5
|
+
const jose_1 = require("jose");
|
|
6
|
+
class AwsToAzureFederatedCredentials {
|
|
7
|
+
tenantId;
|
|
8
|
+
clientID;
|
|
9
|
+
tokenDurationSeconds;
|
|
10
|
+
debugMode;
|
|
11
|
+
static accessToken;
|
|
12
|
+
static stsClient = new client_sts_1.STSClient();
|
|
13
|
+
/**
|
|
14
|
+
* Create a new instance of the AwsToAzureFederatedCredentials class
|
|
15
|
+
* @param tenantId - The Azure AD tenant ID
|
|
16
|
+
* @param clientID - The Azure AD application (client) ID
|
|
17
|
+
* @param tokenDurationSeconds - Duration for the AWS token (default: 300)
|
|
18
|
+
* @param debugMode - Whether to log debug messages
|
|
19
|
+
*/
|
|
20
|
+
constructor(tenantId, clientID, tokenDurationSeconds = 300, debugMode = false) {
|
|
21
|
+
this.tenantId = tenantId;
|
|
22
|
+
this.clientID = clientID;
|
|
23
|
+
this.tokenDurationSeconds = tokenDurationSeconds;
|
|
24
|
+
this.debugMode = debugMode;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Get the Azure access token, fetching a new one if needed
|
|
28
|
+
* @returns {Promise<string>} - The Azure access token
|
|
29
|
+
*/
|
|
30
|
+
async getAccessToken() {
|
|
31
|
+
if (!AwsToAzureFederatedCredentials.accessToken || this.isAccessTokenExpired()) {
|
|
32
|
+
const { access_token } = await this.fetchFederatedCredentials();
|
|
33
|
+
if (this.debugMode) {
|
|
34
|
+
console.log('[DEBUG] New Azure access token fetched:', access_token);
|
|
35
|
+
}
|
|
36
|
+
AwsToAzureFederatedCredentials.accessToken = access_token;
|
|
37
|
+
}
|
|
38
|
+
else if (this.debugMode) {
|
|
39
|
+
console.log("[DEBUG] Using existing Azure access token:", AwsToAzureFederatedCredentials.accessToken);
|
|
40
|
+
}
|
|
41
|
+
return AwsToAzureFederatedCredentials.accessToken;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Fetch the AWS JWT and exchange it for an Azure token
|
|
45
|
+
* @returns {Promise<AzureTokenResponse>} - The Azure token response
|
|
46
|
+
* @private
|
|
47
|
+
*/
|
|
48
|
+
async fetchFederatedCredentials() {
|
|
49
|
+
const tokenResp = await AwsToAzureFederatedCredentials.stsClient.send(new client_sts_1.GetWebIdentityTokenCommand({
|
|
50
|
+
Audience: [this.clientID],
|
|
51
|
+
SigningAlgorithm: "RS256",
|
|
52
|
+
DurationSeconds: this.tokenDurationSeconds,
|
|
53
|
+
}));
|
|
54
|
+
const awsJwt = tokenResp?.WebIdentityToken;
|
|
55
|
+
if (!awsJwt) {
|
|
56
|
+
throw new Error("STS did not return a WebIdentityToken");
|
|
57
|
+
}
|
|
58
|
+
if (this.debugMode)
|
|
59
|
+
console.log("[DEBUG] AWS JWT obtained");
|
|
60
|
+
const response = await fetch(`https://login.microsoftonline.com/${this.tenantId}/oauth2/v2.0/token`, {
|
|
61
|
+
method: "POST",
|
|
62
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
63
|
+
body: new URLSearchParams({
|
|
64
|
+
grant_type: "client_credentials",
|
|
65
|
+
client_id: this.clientID,
|
|
66
|
+
client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
|
|
67
|
+
client_assertion: awsJwt,
|
|
68
|
+
scope: `api://${this.clientID}/.default`,
|
|
69
|
+
}).toString(),
|
|
70
|
+
});
|
|
71
|
+
if (!response.ok) {
|
|
72
|
+
const errorBody = await response.text();
|
|
73
|
+
console.error("Error exchanging AWS JWT for Azure token:", response.status, errorBody);
|
|
74
|
+
throw new Error(`Azure token endpoint error: HTTP ${response.status} - ${errorBody}`);
|
|
75
|
+
}
|
|
76
|
+
return (await response.json());
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Check if the access token is expired
|
|
80
|
+
* @returns {boolean} - Whether the access token is expired
|
|
81
|
+
* @private
|
|
82
|
+
*/
|
|
83
|
+
isAccessTokenExpired() {
|
|
84
|
+
try {
|
|
85
|
+
const decodedAccessToken = (0, jose_1.decodeJwt)(AwsToAzureFederatedCredentials.accessToken);
|
|
86
|
+
const currentTime = Date.now() / 1000;
|
|
87
|
+
if (!decodedAccessToken?.exp) {
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
return currentTime > decodedAccessToken.exp;
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
console.error("Error decoding access token:", err);
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
exports.AwsToAzureFederatedCredentials = AwsToAzureFederatedCredentials;
|
package/package.json
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dvsa/appdev-api-common",
|
|
3
|
-
"version": "
|
|
4
|
-
"keywords": [
|
|
3
|
+
"version": "2.0.0-canary.0",
|
|
4
|
+
"keywords": [
|
|
5
|
+
"dvsa",
|
|
6
|
+
"nodejs",
|
|
7
|
+
"typescript"
|
|
8
|
+
],
|
|
5
9
|
"author": "DVSA",
|
|
6
10
|
"repository": {
|
|
7
11
|
"type": "git",
|
|
@@ -28,6 +32,7 @@
|
|
|
28
32
|
"gitSecrets": "git secrets --scan . && git log -p -- . | scanrepo"
|
|
29
33
|
},
|
|
30
34
|
"dependencies": {
|
|
35
|
+
"@aws-sdk/client-sts": "^3.989.0",
|
|
31
36
|
"ajv": "^8.17.1",
|
|
32
37
|
"ajv-formats": "^3.0.1",
|
|
33
38
|
"dayjs": "^1.11.13",
|