@takodotid/azure-rest 0.1.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/.github/renovate.json +4 -0
- package/.github/workflows/publish.yaml +47 -0
- package/.github/workflows/test.yaml +32 -0
- package/.node-version +1 -0
- package/.vscode/settings.json +12 -0
- package/LICENSE +21 -0
- package/biome.json +56 -0
- package/dist/index.cjs +439 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +368 -0
- package/dist/index.d.ts +368 -0
- package/dist/index.js +396 -0
- package/dist/index.js.map +1 -0
- package/lefthook.yaml +11 -0
- package/package.json +56 -0
- package/pnpm-workspace.yaml +3 -0
- package/src/AzureClient.ts +147 -0
- package/src/index.ts +7 -0
- package/src/lib/AzureCliCredential.ts +135 -0
- package/src/lib/AzureCredential.ts +17 -0
- package/src/lib/DefaultChainedCredential.ts +34 -0
- package/src/lib/ManagedIdentityCredential.ts +75 -0
- package/src/lib/ServicePrincipalCredential.ts +132 -0
- package/src/lib/WorkloadIdentityCredential.ts +71 -0
- package/tsconfig.json +15 -0
- package/tsup.config.ts +10 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from "./AzureClient.js";
|
|
2
|
+
export * from "./lib/AzureCliCredential.js";
|
|
3
|
+
export * from "./lib/AzureCredential.js";
|
|
4
|
+
export * from "./lib/DefaultChainedCredential.js";
|
|
5
|
+
export * from "./lib/ManagedIdentityCredential.js";
|
|
6
|
+
export * from "./lib/ServicePrincipalCredential.js";
|
|
7
|
+
export * from "./lib/WorkloadIdentityCredential.js";
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import process from "node:process";
|
|
3
|
+
import type { AzureCredential } from "./AzureCredential.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The raw response returned by Azure CLI when requesting an access token.
|
|
7
|
+
*
|
|
8
|
+
* @property accessToken - The access token string
|
|
9
|
+
* @property expiresOn - Expiry date in RFC3339 format (legacy)
|
|
10
|
+
* @property expires_on - Expiry as seconds since epoch (preferred)
|
|
11
|
+
* @property subscription - (Optional) Subscription ID, may not be present
|
|
12
|
+
* @property tenant - Tenant ID
|
|
13
|
+
* @property tokenType - Token type (usually 'Bearer')
|
|
14
|
+
*/
|
|
15
|
+
export type CLITokenResponse = {
|
|
16
|
+
accessToken: string;
|
|
17
|
+
expiresOn: string;
|
|
18
|
+
expires_on: string;
|
|
19
|
+
subscription?: string;
|
|
20
|
+
tenant: string;
|
|
21
|
+
tokenType: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Options for AzureCliCredential.
|
|
26
|
+
*
|
|
27
|
+
* @property tenantId - The Azure tenant ID to use for authentication
|
|
28
|
+
*/
|
|
29
|
+
export type AzureCLICredentialOptions = {
|
|
30
|
+
tenantId: string;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export class AzureCliCredential implements AzureCredential {
|
|
34
|
+
public constructor(public options: AzureCLICredentialOptions) {}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Gets an Azure access token using the Azure CLI.
|
|
38
|
+
* @param scope The resource scope for the token
|
|
39
|
+
* @returns An object with token and expiresAt
|
|
40
|
+
* @throws If CLI is not installed or not logged in
|
|
41
|
+
*/
|
|
42
|
+
public async getToken(scope: string) {
|
|
43
|
+
try {
|
|
44
|
+
const result = await this.getCliToken(scope);
|
|
45
|
+
|
|
46
|
+
const specificScope = result.stderr.match("(.*)az login --scope(.*)");
|
|
47
|
+
const isLoginError = result.stderr.match("(.*)az login(.*)") && !specificScope;
|
|
48
|
+
const isNotInstallError = result.stderr.match("az:(.*)not found") ?? result.stderr.startsWith("'az' is not recognized");
|
|
49
|
+
|
|
50
|
+
if (isNotInstallError) {
|
|
51
|
+
throw new Error("Azure CLI not found. Please install");
|
|
52
|
+
}
|
|
53
|
+
if (isLoginError) {
|
|
54
|
+
throw new Error("Please login to Azure CLI");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return this.parseRawOutput(result.stdout);
|
|
58
|
+
} catch (error) {
|
|
59
|
+
throw new Error(`Failed to get token: ${(error as Error).stack}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Runs the Azure CLI to get an access token for the given scope.
|
|
65
|
+
* @param scope The resource scope
|
|
66
|
+
* @returns Promise resolving to CLI stdout and stderr
|
|
67
|
+
* @private
|
|
68
|
+
*/
|
|
69
|
+
private async getCliToken(scope: string) {
|
|
70
|
+
return new Promise<{ stderr: string; stdout: string }>((resolve, reject) => {
|
|
71
|
+
try {
|
|
72
|
+
execFile(
|
|
73
|
+
"az",
|
|
74
|
+
["account", "get-access-token", "--output", "json", "--resource", scope.replace(".default", ""), "--tenant", this.options.tenantId],
|
|
75
|
+
{ cwd: process.cwd(), shell: true, timeout: 30_000 },
|
|
76
|
+
(error, stdout, stderr) => {
|
|
77
|
+
if (error) {
|
|
78
|
+
reject(new Error(`Failed to get token: ${error.stack}`));
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
resolve({ stdout, stderr });
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
} catch (error) {
|
|
86
|
+
reject(error as Error);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Parses the raw CLI output and returns a token + expiry object.
|
|
93
|
+
* @param output The stdout from Azure CLI
|
|
94
|
+
* @returns An object with token and expiresAt
|
|
95
|
+
* @private
|
|
96
|
+
*/
|
|
97
|
+
private parseRawOutput(output: string) {
|
|
98
|
+
const response = JSON.parse(output) as CLITokenResponse;
|
|
99
|
+
const token = response.accessToken;
|
|
100
|
+
|
|
101
|
+
// if available, expires_on will be a number representing seconds since epoch.
|
|
102
|
+
// ensure it's a number or NaN
|
|
103
|
+
const expiresOnTimestamp = Number.parseInt(response.expires_on, 10) * 1_000;
|
|
104
|
+
if (!Number.isNaN(expiresOnTimestamp)) {
|
|
105
|
+
return {
|
|
106
|
+
accessToken: token,
|
|
107
|
+
expiresAt: new Date(expiresOnTimestamp),
|
|
108
|
+
tokenType: response.tokenType
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// fallback to the older expiresOn - an RFC3339 date string
|
|
113
|
+
return {
|
|
114
|
+
accessToken: token,
|
|
115
|
+
expiresAt: new Date(response.expiresOn),
|
|
116
|
+
tokenType: response.tokenType
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Instantiates AzureCliCredential using the AZURE_TENANT_ID environment variable.
|
|
122
|
+
* @returns AzureCliCredential instance
|
|
123
|
+
*/
|
|
124
|
+
public static fromEnv() {
|
|
125
|
+
return new AzureCliCredential({ tenantId: process.env.AZURE_TENANT_ID });
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
declare global {
|
|
130
|
+
namespace NodeJS {
|
|
131
|
+
interface ProcessEnv {
|
|
132
|
+
AZURE_TENANT_ID: string;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents an Azure access token and its expiration.
|
|
3
|
+
*/
|
|
4
|
+
export type Credential = { accessToken: string; clientId?: string; expiresAt: Date; tokenType: string };
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Abstract credential class for acquiring Azure tokens.
|
|
8
|
+
* Implement this to provide custom authentication logic.
|
|
9
|
+
*/
|
|
10
|
+
export abstract class AzureCredential {
|
|
11
|
+
/**
|
|
12
|
+
* Gets an Azure access token for the given scope.
|
|
13
|
+
* @param scope The resource or scope for which the token is requested
|
|
14
|
+
* @returns A promise resolving to an AzureToken
|
|
15
|
+
*/
|
|
16
|
+
public abstract getToken(scope: string): Promise<Credential>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { AzureCliCredential } from "./AzureCliCredential.js";
|
|
2
|
+
import type { AzureCredential } from "./AzureCredential.js";
|
|
3
|
+
import { ManagedIdentityCredential } from "./ManagedIdentityCredential.js";
|
|
4
|
+
import { ServicePrincipalCredential } from "./ServicePrincipalCredential.js";
|
|
5
|
+
import { WorkloadIdentityCredential } from "./WorkloadIdentityCredential.js";
|
|
6
|
+
|
|
7
|
+
const credentialChain = [WorkloadIdentityCredential, ManagedIdentityCredential, ServicePrincipalCredential, AzureCliCredential];
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* DefaultChainedCredential tries multiple credential providers in order until one succeeds.
|
|
11
|
+
*
|
|
12
|
+
* The chain is: WorkloadIdentityCredential → ManagedIdentityCredential → ServicePrincipalCredential → AzureCliCredential.
|
|
13
|
+
* Useful for local dev, CI, and cloud environments with minimal config.
|
|
14
|
+
*/
|
|
15
|
+
export class DefaultChainedCredential implements AzureCredential {
|
|
16
|
+
/**
|
|
17
|
+
* Attempts to get an Azure access token using the first available credential in the chain.
|
|
18
|
+
* @param scope The resource scope for the token
|
|
19
|
+
* @returns An object with token and expiresAt
|
|
20
|
+
* @throws If all credential providers fail
|
|
21
|
+
*/
|
|
22
|
+
public async getToken(scope: string) {
|
|
23
|
+
const errors: { name: string; error: Error }[] = [];
|
|
24
|
+
for (const Credential of credentialChain) {
|
|
25
|
+
try {
|
|
26
|
+
return await Credential.fromEnv().getToken(scope);
|
|
27
|
+
} catch (error) {
|
|
28
|
+
errors.push({ error: error as Error, name: Credential.name });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
throw new Error(`Failed to get token, errors:\n${errors.map(err => `[${err.name}] ${err.error.message}`).join("\n")}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { AzureCredential } from "./AzureCredential.js";
|
|
2
|
+
import type { OAuth2TokenResponse } from "./ServicePrincipalCredential.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Options for configuring ManagedIdentityCredential.
|
|
6
|
+
*
|
|
7
|
+
* @property clientId - (Optional) The user-assigned managed identity client ID
|
|
8
|
+
*/
|
|
9
|
+
export type ManagedIdentityCredentialOptions = {
|
|
10
|
+
clientId?: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* AzureCredential implementation for Azure Managed Identity (MSI).
|
|
15
|
+
*
|
|
16
|
+
* Supports both system-assigned and user-assigned managed identities.
|
|
17
|
+
* Works on Azure VM, App Service, Container Apps, etc.
|
|
18
|
+
*/
|
|
19
|
+
export class ManagedIdentityCredential implements AzureCredential {
|
|
20
|
+
/**
|
|
21
|
+
* @param options Managed identity credential options
|
|
22
|
+
*/
|
|
23
|
+
public constructor(public options: ManagedIdentityCredentialOptions = {}) {}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Gets an Azure access token using the managed identity endpoint.
|
|
27
|
+
* @param scope The resource scope for the token
|
|
28
|
+
* @returns An object with token and expiresAt
|
|
29
|
+
* @throws If the endpoint is unavailable or token request fails
|
|
30
|
+
*/
|
|
31
|
+
public async getToken(scope: string) {
|
|
32
|
+
const endpoint = process.env.AZURE_MANAGED_IDENTITY_ENDPOINT || process.env.IDENTITY_ENDPOINT || "http://169.254.169.254/metadata/identity/oauth2/token";
|
|
33
|
+
const apiVersion = "2018-02-01";
|
|
34
|
+
const params = new URLSearchParams({
|
|
35
|
+
resource: scope.replace(".default", ""),
|
|
36
|
+
apiVersion
|
|
37
|
+
});
|
|
38
|
+
if (this.options.clientId) {
|
|
39
|
+
params.set("client_id", this.options.clientId);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const url = `${endpoint}?${params.toString()}`;
|
|
43
|
+
const headers = { Metadata: "true" };
|
|
44
|
+
|
|
45
|
+
const response = await fetch(url, { headers });
|
|
46
|
+
if (!response.ok) {
|
|
47
|
+
throw new Error(`ManagedIdentityCredential: Failed to get token: ${await response.text()}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const data = (await response.json()) as OAuth2TokenResponse;
|
|
51
|
+
return {
|
|
52
|
+
accessToken: data.access_token,
|
|
53
|
+
clientId: data.client_id,
|
|
54
|
+
expiresAt: new Date(data.expires_on ? Number(data.expires_on) * 1000 : Date.now() + 60 * 60 * 1000), // fallback 1h
|
|
55
|
+
tokenType: data.token_type
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Instantiates ManagedIdentityCredential using environment variables.
|
|
61
|
+
* @returns ManagedIdentityCredential instance
|
|
62
|
+
*/
|
|
63
|
+
public static fromEnv() {
|
|
64
|
+
return new ManagedIdentityCredential({ clientId: process.env.AZURE_CLIENT_ID });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
declare global {
|
|
69
|
+
namespace NodeJS {
|
|
70
|
+
interface ProcessEnv {
|
|
71
|
+
AZURE_MANAGED_IDENTITY_ENDPOINT?: string;
|
|
72
|
+
IDENTITY_ENDPOINT?: string;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { URLSearchParams } from "node:url";
|
|
2
|
+
import type { AzureCredential } from "./AzureCredential.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The OAuth2 token response returned by Azure AD and Managed Identity endpoints.
|
|
6
|
+
*
|
|
7
|
+
* @property access_token - The access token string
|
|
8
|
+
* @property client_id - The client/application ID (optional, present in MSI)
|
|
9
|
+
* @property expires_in - Seconds until token expiry
|
|
10
|
+
* @property expires_on - Expiry time (epoch seconds, as string)
|
|
11
|
+
* @property ext_expires_in - Extended expiry in seconds
|
|
12
|
+
* @property not_before - Not before time (epoch seconds, as string)
|
|
13
|
+
* @property resource - The resource for which the token is issued
|
|
14
|
+
* @property token_type - The type of token (usually 'Bearer')
|
|
15
|
+
*/
|
|
16
|
+
export type OAuth2TokenResponse = {
|
|
17
|
+
access_token: string;
|
|
18
|
+
client_id?: string;
|
|
19
|
+
expires_in: number;
|
|
20
|
+
expires_on: string;
|
|
21
|
+
ext_expires_in: number;
|
|
22
|
+
not_before: string;
|
|
23
|
+
resource: string;
|
|
24
|
+
token_type: string;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Options for configuring ServicePrincipalCredential.
|
|
29
|
+
*
|
|
30
|
+
* @property clientId - The Azure AD application (client) ID
|
|
31
|
+
* @property clientSecret - The client secret or JWT assertion (for federated)
|
|
32
|
+
* @property tenantId - The Azure AD tenant ID
|
|
33
|
+
* @property authorityHost - (Optional) The Azure AD authority host
|
|
34
|
+
* @property federated - Whether to use federated (JWT) auth
|
|
35
|
+
*/
|
|
36
|
+
export type ServicePrincipalCredentialOption = {
|
|
37
|
+
clientId: string;
|
|
38
|
+
clientSecret?: string;
|
|
39
|
+
tenantId: string;
|
|
40
|
+
authorityHost?: string;
|
|
41
|
+
federated: boolean;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* AzureCredential implementation for authenticating with a Service Principal (client secret or federated/JWT).
|
|
46
|
+
*/
|
|
47
|
+
export class ServicePrincipalCredential implements AzureCredential {
|
|
48
|
+
/**
|
|
49
|
+
* @param options Service principal credential options
|
|
50
|
+
*/
|
|
51
|
+
public constructor(public options: ServicePrincipalCredentialOption) {}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Gets an Azure access token using the service principal credentials.
|
|
55
|
+
* @param scope The resource scope for the token
|
|
56
|
+
* @returns An object with token and expiresAt
|
|
57
|
+
* @throws If client secret is missing or token request fails
|
|
58
|
+
*/
|
|
59
|
+
public async getToken(scope: string) {
|
|
60
|
+
if (!this.options.clientSecret) throw new Error("ServicePrincipalCredential: The client secret is not provided.");
|
|
61
|
+
|
|
62
|
+
// Remove trailing slash from identityAuthorityHost
|
|
63
|
+
const url = [this.options.authorityHost?.replace(/\/$/, "") ?? "https://login.microsoftonline.com", `${this.options.tenantId}/oauth2/v2.0/token`].join("/");
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
const searchParams = {
|
|
67
|
+
client_id: this.options.clientId,
|
|
68
|
+
grant_type: "client_credentials",
|
|
69
|
+
scope
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// If federated, use client_assertion and client_assertion_type
|
|
73
|
+
// Otherwise, use client_secret
|
|
74
|
+
if (this.options.federated) {
|
|
75
|
+
Object.assign(searchParams, {
|
|
76
|
+
client_assertion: this.options.clientSecret,
|
|
77
|
+
client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
|
|
78
|
+
});
|
|
79
|
+
} else {
|
|
80
|
+
Object.assign(searchParams, {
|
|
81
|
+
client_secret: this.options.clientSecret
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
const response = await fetch(url, {
|
|
85
|
+
method: "POST",
|
|
86
|
+
headers: {
|
|
87
|
+
Accept: "application/json",
|
|
88
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
89
|
+
},
|
|
90
|
+
body: new URLSearchParams(searchParams)
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
if (!response.ok) {
|
|
94
|
+
throw new Error(`Failed to get token: ${await response.text()}`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const data = (await response.json()) as OAuth2TokenResponse;
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
accessToken: data.access_token,
|
|
101
|
+
clientId: data.client_id ?? this.options.clientId,
|
|
102
|
+
expiresAt: new Date(Date.now() + data.expires_in * 1_000),
|
|
103
|
+
tokenType: data.token_type
|
|
104
|
+
};
|
|
105
|
+
} catch (error) {
|
|
106
|
+
throw new Error(`Failed to get token: ${(error as Error).stack}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Instantiates ServicePrincipalCredential using environment variables.
|
|
112
|
+
* @returns ServicePrincipalCredential instance
|
|
113
|
+
*/
|
|
114
|
+
public static fromEnv() {
|
|
115
|
+
return new ServicePrincipalCredential({
|
|
116
|
+
clientId: process.env.AZURE_CLIENT_ID,
|
|
117
|
+
clientSecret: process.env.AZURE_CLIENT_SECRET,
|
|
118
|
+
tenantId: process.env.AZURE_TENANT_ID,
|
|
119
|
+
federated: false
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
declare global {
|
|
125
|
+
namespace NodeJS {
|
|
126
|
+
interface ProcessEnv {
|
|
127
|
+
AZURE_CLIENT_ID: string;
|
|
128
|
+
AZURE_CLIENT_SECRET: string;
|
|
129
|
+
AZURE_TENANT_ID: string;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import type { AzureCredential } from "./AzureCredential.js";
|
|
3
|
+
import { ServicePrincipalCredential } from "./ServicePrincipalCredential.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Options for configuring WorkloadIdentityCredential.
|
|
7
|
+
*
|
|
8
|
+
* @property clientId - The Azure AD application (client) ID
|
|
9
|
+
* @property federatedTokenFile - Path to the federated token file (OIDC/JWT)
|
|
10
|
+
* @property tenantId - The Azure AD tenant ID
|
|
11
|
+
* @property authorityHost - (Optional) The Azure AD authority host
|
|
12
|
+
*/
|
|
13
|
+
export type WorkloadIdentityCredentialOption = {
|
|
14
|
+
clientId: string;
|
|
15
|
+
federatedTokenFile: string;
|
|
16
|
+
tenantId: string;
|
|
17
|
+
authorityHost?: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* AzureCredential implementation for Azure Workload Identity (OIDC federated token).
|
|
22
|
+
*
|
|
23
|
+
* Reads a federated token from file and authenticates as a service principal using JWT assertion.
|
|
24
|
+
*/
|
|
25
|
+
export class WorkloadIdentityCredential implements AzureCredential {
|
|
26
|
+
/**
|
|
27
|
+
* @param options Workload identity credential options
|
|
28
|
+
*/
|
|
29
|
+
public constructor(public options: WorkloadIdentityCredentialOption) {}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Gets an Azure access token using the federated token file.
|
|
33
|
+
* @param scope The resource scope for the token
|
|
34
|
+
* @returns An object with token and expiresAt
|
|
35
|
+
* @throws If the federated token file does not exist
|
|
36
|
+
*/
|
|
37
|
+
public async getToken(scope: string) {
|
|
38
|
+
if (!existsSync(this.options.federatedTokenFile)) {
|
|
39
|
+
throw new Error("WorkloadIdentityCredential: The federated token file does not exist.");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const token = readFileSync(this.options.federatedTokenFile, "utf-8");
|
|
43
|
+
const servicePrincipal = new ServicePrincipalCredential({ ...this.options, clientSecret: token, federated: true });
|
|
44
|
+
|
|
45
|
+
return servicePrincipal.getToken(scope);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Instantiates WorkloadIdentityCredential using environment variables.
|
|
50
|
+
* @returns WorkloadIdentityCredential instance
|
|
51
|
+
*/
|
|
52
|
+
public static fromEnv() {
|
|
53
|
+
return new WorkloadIdentityCredential({
|
|
54
|
+
authorityHost: process.env.AZURE_AUTHORITY_HOST,
|
|
55
|
+
clientId: process.env.AZURE_CLIENT_ID,
|
|
56
|
+
federatedTokenFile: process.env.AZURE_FEDERATED_TOKEN_FILE,
|
|
57
|
+
tenantId: process.env.AZURE_TENANT_ID
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
declare global {
|
|
63
|
+
namespace NodeJS {
|
|
64
|
+
interface ProcessEnv {
|
|
65
|
+
AZURE_AUTHORITY_HOST: string;
|
|
66
|
+
AZURE_CLIENT_ID: string;
|
|
67
|
+
AZURE_FEDERATED_TOKEN_FILE: string;
|
|
68
|
+
AZURE_TENANT_ID: string;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"baseUrl": ".",
|
|
4
|
+
"lib": ["ES2022"],
|
|
5
|
+
"module": "node18",
|
|
6
|
+
"moduleResolution": "nodenext",
|
|
7
|
+
"target": "es2022",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"declaration": true,
|
|
12
|
+
"noEmit": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["src"]
|
|
15
|
+
}
|