@takodotid/azure-rest 0.1.6 → 1.0.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 +74 -0
- package/dist/index.cjs +114 -67
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +43 -22
- package/dist/index.d.ts +43 -22
- package/dist/index.js +113 -66
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -48,14 +48,6 @@ declare class AzureClient {
|
|
|
48
48
|
* @param options Azure client configuration (baseUrl, credential, etc)
|
|
49
49
|
*/
|
|
50
50
|
constructor(options: AzureClientOptions);
|
|
51
|
-
/**
|
|
52
|
-
* Sends a request to the Azure REST API, handling token refresh and retries.
|
|
53
|
-
* @param path The API path (relative to baseUrl)
|
|
54
|
-
* @param options Optional fetch options
|
|
55
|
-
* @returns The fetch Response object
|
|
56
|
-
* @throws If token refresh fails after max retries
|
|
57
|
-
*/
|
|
58
|
-
sendRequest(path: string, options?: RequestInit): Promise<Response>;
|
|
59
51
|
/**
|
|
60
52
|
* Sends a GET request to the Azure REST API.
|
|
61
53
|
* @param path The API path
|
|
@@ -91,6 +83,14 @@ declare class AzureClient {
|
|
|
91
83
|
* @returns The fetch Response object
|
|
92
84
|
*/
|
|
93
85
|
delete(path: string, options?: Exclude<RequestInit, "method">): Promise<Response>;
|
|
86
|
+
/**
|
|
87
|
+
* Sends a request to the Azure REST API, handling token refresh and retries.
|
|
88
|
+
* @param path The API path (relative to baseUrl)
|
|
89
|
+
* @param options Optional fetch options
|
|
90
|
+
* @returns The fetch Response object
|
|
91
|
+
* @throws If token refresh fails after max retries
|
|
92
|
+
*/
|
|
93
|
+
request(path: string, options?: RequestInit): Promise<Response>;
|
|
94
94
|
/**
|
|
95
95
|
* Refreshes the Azure access token using the provided credential helper.
|
|
96
96
|
* @private
|
|
@@ -119,18 +119,32 @@ type CLITokenResponse = {
|
|
|
119
119
|
/**
|
|
120
120
|
* Options for AzureCliCredential.
|
|
121
121
|
*
|
|
122
|
-
* @property tenantId - The Azure tenant ID to use for authentication.
|
|
122
|
+
* @property tenantId - (Optional) The Azure tenant ID to use for authentication. If not provided, will use the current Azure CLI context.
|
|
123
123
|
*/
|
|
124
124
|
type AzureCLICredentialOptions = {
|
|
125
|
-
tenantId
|
|
125
|
+
tenantId?: string;
|
|
126
126
|
};
|
|
127
|
+
/**
|
|
128
|
+
* AzureCliCredential authenticates using the Azure CLI (`az`).
|
|
129
|
+
*
|
|
130
|
+
* - If `tenantId` is provided, uses it for token requests.
|
|
131
|
+
* - If not, will try `AZURE_TENANT_ID` env var, then fall back to the current Azure CLI context.
|
|
132
|
+
*
|
|
133
|
+
* Throws clear errors if the CLI is not installed or not logged in.
|
|
134
|
+
*/
|
|
127
135
|
declare class AzureCliCredential implements AzureCredential {
|
|
128
136
|
options: AzureCLICredentialOptions;
|
|
129
137
|
constructor(options: AzureCLICredentialOptions);
|
|
138
|
+
/**
|
|
139
|
+
* Instantiates AzureCliCredential using the AZURE_TENANT_ID environment variable (if set),
|
|
140
|
+
* or falls back to the current Azure CLI context.
|
|
141
|
+
* @returns AzureCliCredential instance
|
|
142
|
+
*/
|
|
143
|
+
static fromEnv(): AzureCliCredential;
|
|
130
144
|
/**
|
|
131
145
|
* Gets an Azure access token using the Azure CLI.
|
|
132
|
-
* @param scope The resource scope for the token
|
|
133
|
-
* @returns An object with
|
|
146
|
+
* @param scope The resource scope for the token (e.g. 'https://management.azure.com/.default')
|
|
147
|
+
* @returns An object with accessToken, expiresAt, and tokenType
|
|
134
148
|
* @throws If CLI is not installed or not logged in
|
|
135
149
|
*/
|
|
136
150
|
getToken(scope: string): Promise<{
|
|
@@ -139,26 +153,33 @@ declare class AzureCliCredential implements AzureCredential {
|
|
|
139
153
|
tokenType: string;
|
|
140
154
|
}>;
|
|
141
155
|
/**
|
|
142
|
-
* Runs the Azure CLI to get an access token for the given scope.
|
|
156
|
+
* Runs the Azure CLI to get an access token for the given scope and tenant.
|
|
143
157
|
* @param scope The resource scope
|
|
158
|
+
* @param tenantId The Azure tenant ID
|
|
144
159
|
* @returns Promise resolving to CLI stdout and stderr
|
|
145
160
|
* @private
|
|
146
161
|
*/
|
|
147
162
|
private getCliToken;
|
|
163
|
+
/**
|
|
164
|
+
* Gets the current tenant ID from Azure CLI context.
|
|
165
|
+
* @returns Promise resolving to the current tenant ID string
|
|
166
|
+
* @throws If CLI is not installed or not logged in
|
|
167
|
+
*/
|
|
168
|
+
private static getCurrentTenantId;
|
|
148
169
|
/**
|
|
149
170
|
* Parses the raw CLI output and returns a token + expiry object.
|
|
150
171
|
* @param output The stdout from Azure CLI
|
|
151
|
-
* @returns An object with
|
|
172
|
+
* @returns An object with accessToken, expiresAt, and tokenType
|
|
152
173
|
* @private
|
|
153
174
|
*/
|
|
154
175
|
private parseRawOutput;
|
|
155
176
|
/**
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
* @
|
|
177
|
+
* Checks stderr for common Azure CLI login errors.
|
|
178
|
+
* @param stderr The stderr string from CLI
|
|
179
|
+
* @returns Object with isLoginError and isNotInstallError booleans
|
|
180
|
+
* @private
|
|
160
181
|
*/
|
|
161
|
-
static
|
|
182
|
+
private static parseCliLoginError;
|
|
162
183
|
}
|
|
163
184
|
declare global {
|
|
164
185
|
namespace NodeJS {
|
|
@@ -169,12 +190,12 @@ declare global {
|
|
|
169
190
|
}
|
|
170
191
|
|
|
171
192
|
/**
|
|
172
|
-
*
|
|
193
|
+
* ChainedCredential tries multiple credential providers in order until one succeeds.
|
|
173
194
|
*
|
|
174
195
|
* The chain is: WorkloadIdentityCredential → ManagedIdentityCredential → ServicePrincipalCredential → AzureCliCredential.
|
|
175
196
|
* Useful for local dev, CI, and cloud environments with minimal config.
|
|
176
197
|
*/
|
|
177
|
-
declare class
|
|
198
|
+
declare class ChainedCredential implements AzureCredential {
|
|
178
199
|
/**
|
|
179
200
|
* Attempts to get an Azure access token using the first available credential in the chain.
|
|
180
201
|
* @param scope The resource scope for the token
|
|
@@ -383,4 +404,4 @@ declare global {
|
|
|
383
404
|
}
|
|
384
405
|
}
|
|
385
406
|
|
|
386
|
-
export { type AzureCLICredentialOptions, AzureCliCredential, AzureClient, type AzureClientOptions, AzureCredential, type CLITokenResponse, type Credential,
|
|
407
|
+
export { type AzureCLICredentialOptions, AzureCliCredential, AzureClient, type AzureClientOptions, AzureCredential, type CLITokenResponse, ChainedCredential, type Credential, ManagedIdentityCredential, type ManagedIdentityCredentialOptions, type OAuth2TokenResponse, ServicePrincipalCredential, type ServicePrincipalCredentialOption, WorkloadIdentityCredential, type WorkloadIdentityCredentialOption };
|
package/dist/index.d.ts
CHANGED
|
@@ -48,14 +48,6 @@ declare class AzureClient {
|
|
|
48
48
|
* @param options Azure client configuration (baseUrl, credential, etc)
|
|
49
49
|
*/
|
|
50
50
|
constructor(options: AzureClientOptions);
|
|
51
|
-
/**
|
|
52
|
-
* Sends a request to the Azure REST API, handling token refresh and retries.
|
|
53
|
-
* @param path The API path (relative to baseUrl)
|
|
54
|
-
* @param options Optional fetch options
|
|
55
|
-
* @returns The fetch Response object
|
|
56
|
-
* @throws If token refresh fails after max retries
|
|
57
|
-
*/
|
|
58
|
-
sendRequest(path: string, options?: RequestInit): Promise<Response>;
|
|
59
51
|
/**
|
|
60
52
|
* Sends a GET request to the Azure REST API.
|
|
61
53
|
* @param path The API path
|
|
@@ -91,6 +83,14 @@ declare class AzureClient {
|
|
|
91
83
|
* @returns The fetch Response object
|
|
92
84
|
*/
|
|
93
85
|
delete(path: string, options?: Exclude<RequestInit, "method">): Promise<Response>;
|
|
86
|
+
/**
|
|
87
|
+
* Sends a request to the Azure REST API, handling token refresh and retries.
|
|
88
|
+
* @param path The API path (relative to baseUrl)
|
|
89
|
+
* @param options Optional fetch options
|
|
90
|
+
* @returns The fetch Response object
|
|
91
|
+
* @throws If token refresh fails after max retries
|
|
92
|
+
*/
|
|
93
|
+
request(path: string, options?: RequestInit): Promise<Response>;
|
|
94
94
|
/**
|
|
95
95
|
* Refreshes the Azure access token using the provided credential helper.
|
|
96
96
|
* @private
|
|
@@ -119,18 +119,32 @@ type CLITokenResponse = {
|
|
|
119
119
|
/**
|
|
120
120
|
* Options for AzureCliCredential.
|
|
121
121
|
*
|
|
122
|
-
* @property tenantId - The Azure tenant ID to use for authentication.
|
|
122
|
+
* @property tenantId - (Optional) The Azure tenant ID to use for authentication. If not provided, will use the current Azure CLI context.
|
|
123
123
|
*/
|
|
124
124
|
type AzureCLICredentialOptions = {
|
|
125
|
-
tenantId
|
|
125
|
+
tenantId?: string;
|
|
126
126
|
};
|
|
127
|
+
/**
|
|
128
|
+
* AzureCliCredential authenticates using the Azure CLI (`az`).
|
|
129
|
+
*
|
|
130
|
+
* - If `tenantId` is provided, uses it for token requests.
|
|
131
|
+
* - If not, will try `AZURE_TENANT_ID` env var, then fall back to the current Azure CLI context.
|
|
132
|
+
*
|
|
133
|
+
* Throws clear errors if the CLI is not installed or not logged in.
|
|
134
|
+
*/
|
|
127
135
|
declare class AzureCliCredential implements AzureCredential {
|
|
128
136
|
options: AzureCLICredentialOptions;
|
|
129
137
|
constructor(options: AzureCLICredentialOptions);
|
|
138
|
+
/**
|
|
139
|
+
* Instantiates AzureCliCredential using the AZURE_TENANT_ID environment variable (if set),
|
|
140
|
+
* or falls back to the current Azure CLI context.
|
|
141
|
+
* @returns AzureCliCredential instance
|
|
142
|
+
*/
|
|
143
|
+
static fromEnv(): AzureCliCredential;
|
|
130
144
|
/**
|
|
131
145
|
* Gets an Azure access token using the Azure CLI.
|
|
132
|
-
* @param scope The resource scope for the token
|
|
133
|
-
* @returns An object with
|
|
146
|
+
* @param scope The resource scope for the token (e.g. 'https://management.azure.com/.default')
|
|
147
|
+
* @returns An object with accessToken, expiresAt, and tokenType
|
|
134
148
|
* @throws If CLI is not installed or not logged in
|
|
135
149
|
*/
|
|
136
150
|
getToken(scope: string): Promise<{
|
|
@@ -139,26 +153,33 @@ declare class AzureCliCredential implements AzureCredential {
|
|
|
139
153
|
tokenType: string;
|
|
140
154
|
}>;
|
|
141
155
|
/**
|
|
142
|
-
* Runs the Azure CLI to get an access token for the given scope.
|
|
156
|
+
* Runs the Azure CLI to get an access token for the given scope and tenant.
|
|
143
157
|
* @param scope The resource scope
|
|
158
|
+
* @param tenantId The Azure tenant ID
|
|
144
159
|
* @returns Promise resolving to CLI stdout and stderr
|
|
145
160
|
* @private
|
|
146
161
|
*/
|
|
147
162
|
private getCliToken;
|
|
163
|
+
/**
|
|
164
|
+
* Gets the current tenant ID from Azure CLI context.
|
|
165
|
+
* @returns Promise resolving to the current tenant ID string
|
|
166
|
+
* @throws If CLI is not installed or not logged in
|
|
167
|
+
*/
|
|
168
|
+
private static getCurrentTenantId;
|
|
148
169
|
/**
|
|
149
170
|
* Parses the raw CLI output and returns a token + expiry object.
|
|
150
171
|
* @param output The stdout from Azure CLI
|
|
151
|
-
* @returns An object with
|
|
172
|
+
* @returns An object with accessToken, expiresAt, and tokenType
|
|
152
173
|
* @private
|
|
153
174
|
*/
|
|
154
175
|
private parseRawOutput;
|
|
155
176
|
/**
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
* @
|
|
177
|
+
* Checks stderr for common Azure CLI login errors.
|
|
178
|
+
* @param stderr The stderr string from CLI
|
|
179
|
+
* @returns Object with isLoginError and isNotInstallError booleans
|
|
180
|
+
* @private
|
|
160
181
|
*/
|
|
161
|
-
static
|
|
182
|
+
private static parseCliLoginError;
|
|
162
183
|
}
|
|
163
184
|
declare global {
|
|
164
185
|
namespace NodeJS {
|
|
@@ -169,12 +190,12 @@ declare global {
|
|
|
169
190
|
}
|
|
170
191
|
|
|
171
192
|
/**
|
|
172
|
-
*
|
|
193
|
+
* ChainedCredential tries multiple credential providers in order until one succeeds.
|
|
173
194
|
*
|
|
174
195
|
* The chain is: WorkloadIdentityCredential → ManagedIdentityCredential → ServicePrincipalCredential → AzureCliCredential.
|
|
175
196
|
* Useful for local dev, CI, and cloud environments with minimal config.
|
|
176
197
|
*/
|
|
177
|
-
declare class
|
|
198
|
+
declare class ChainedCredential implements AzureCredential {
|
|
178
199
|
/**
|
|
179
200
|
* Attempts to get an Azure access token using the first available credential in the chain.
|
|
180
201
|
* @param scope The resource scope for the token
|
|
@@ -383,4 +404,4 @@ declare global {
|
|
|
383
404
|
}
|
|
384
405
|
}
|
|
385
406
|
|
|
386
|
-
export { type AzureCLICredentialOptions, AzureCliCredential, AzureClient, type AzureClientOptions, AzureCredential, type CLITokenResponse, type Credential,
|
|
407
|
+
export { type AzureCLICredentialOptions, AzureCliCredential, AzureClient, type AzureClientOptions, AzureCredential, type CLITokenResponse, ChainedCredential, type Credential, ManagedIdentityCredential, type ManagedIdentityCredentialOptions, type OAuth2TokenResponse, ServicePrincipalCredential, type ServicePrincipalCredentialOption, WorkloadIdentityCredential, type WorkloadIdentityCredentialOption };
|
package/dist/index.js
CHANGED
|
@@ -9,34 +9,6 @@ var AzureClient = class _AzureClient {
|
|
|
9
9
|
}
|
|
10
10
|
static MAX_TOKEN_RETRIES = 3;
|
|
11
11
|
token = null;
|
|
12
|
-
/**
|
|
13
|
-
* Sends a request to the Azure REST API, handling token refresh and retries.
|
|
14
|
-
* @param path The API path (relative to baseUrl)
|
|
15
|
-
* @param options Optional fetch options
|
|
16
|
-
* @returns The fetch Response object
|
|
17
|
-
* @throws If token refresh fails after max retries
|
|
18
|
-
*/
|
|
19
|
-
async sendRequest(path, options) {
|
|
20
|
-
for (let i = 0; i <= _AzureClient.MAX_TOKEN_RETRIES; i++) {
|
|
21
|
-
if (this.token && this.token.expiresAt > /* @__PURE__ */ new Date()) break;
|
|
22
|
-
if (i === _AzureClient.MAX_TOKEN_RETRIES) {
|
|
23
|
-
throw new Error("Failed to refresh token after multiple attempts");
|
|
24
|
-
}
|
|
25
|
-
await this.refreshToken();
|
|
26
|
-
if (i > 0) await new Promise((res) => setTimeout(res, 100 * i));
|
|
27
|
-
}
|
|
28
|
-
if (!this.token) throw new Error("Token is unexpectedly null after refresh attempts");
|
|
29
|
-
const baseUrl = this.options.baseUrl.replace(/\/+$/, "");
|
|
30
|
-
const relPath = path.replace(/^\/+/, "");
|
|
31
|
-
const url = `${baseUrl}/${relPath}`;
|
|
32
|
-
return fetch(url, {
|
|
33
|
-
...options,
|
|
34
|
-
headers: {
|
|
35
|
-
...this.options.credential.builder ? this.options.credential.builder(this.token) : { Authorization: `Bearer ${this.token.accessToken}` },
|
|
36
|
-
...options?.headers
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
12
|
/**
|
|
41
13
|
* Sends a GET request to the Azure REST API.
|
|
42
14
|
* @param path The API path
|
|
@@ -44,7 +16,7 @@ var AzureClient = class _AzureClient {
|
|
|
44
16
|
* @returns The fetch Response object
|
|
45
17
|
*/
|
|
46
18
|
get(path, options) {
|
|
47
|
-
return this.
|
|
19
|
+
return this.request(path, { ...options, method: "GET" });
|
|
48
20
|
}
|
|
49
21
|
/**
|
|
50
22
|
* Sends a POST request with a JSON body to the Azure REST API.
|
|
@@ -53,7 +25,7 @@ var AzureClient = class _AzureClient {
|
|
|
53
25
|
* @returns The fetch Response object
|
|
54
26
|
*/
|
|
55
27
|
post(path, options) {
|
|
56
|
-
return this.
|
|
28
|
+
return this.request(path, {
|
|
57
29
|
...options,
|
|
58
30
|
method: "POST"
|
|
59
31
|
});
|
|
@@ -65,7 +37,7 @@ var AzureClient = class _AzureClient {
|
|
|
65
37
|
* @returns The fetch Response object
|
|
66
38
|
*/
|
|
67
39
|
put(path, options) {
|
|
68
|
-
return this.
|
|
40
|
+
return this.request(path, {
|
|
69
41
|
...options,
|
|
70
42
|
method: "PUT"
|
|
71
43
|
});
|
|
@@ -77,7 +49,7 @@ var AzureClient = class _AzureClient {
|
|
|
77
49
|
* @returns The fetch Response object
|
|
78
50
|
*/
|
|
79
51
|
patch(path, options) {
|
|
80
|
-
return this.
|
|
52
|
+
return this.request(path, {
|
|
81
53
|
...options,
|
|
82
54
|
method: "PATCH"
|
|
83
55
|
});
|
|
@@ -89,7 +61,35 @@ var AzureClient = class _AzureClient {
|
|
|
89
61
|
* @returns The fetch Response object
|
|
90
62
|
*/
|
|
91
63
|
delete(path, options) {
|
|
92
|
-
return this.
|
|
64
|
+
return this.request(path, { ...options, method: "DELETE" });
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Sends a request to the Azure REST API, handling token refresh and retries.
|
|
68
|
+
* @param path The API path (relative to baseUrl)
|
|
69
|
+
* @param options Optional fetch options
|
|
70
|
+
* @returns The fetch Response object
|
|
71
|
+
* @throws If token refresh fails after max retries
|
|
72
|
+
*/
|
|
73
|
+
async request(path, options) {
|
|
74
|
+
for (let i = 0; i <= _AzureClient.MAX_TOKEN_RETRIES; i++) {
|
|
75
|
+
if (this.token && this.token.expiresAt > /* @__PURE__ */ new Date()) break;
|
|
76
|
+
if (i === _AzureClient.MAX_TOKEN_RETRIES) {
|
|
77
|
+
throw new Error("Failed to refresh token after multiple attempts");
|
|
78
|
+
}
|
|
79
|
+
await this.refreshToken();
|
|
80
|
+
if (i > 0) await new Promise((res) => setTimeout(res, 100 * i));
|
|
81
|
+
}
|
|
82
|
+
if (!this.token) throw new Error("Token is unexpectedly null after refresh attempts");
|
|
83
|
+
const baseUrl = this.options.baseUrl.replace(/\/+$/, "");
|
|
84
|
+
const relPath = path.replace(/^\/+/, "");
|
|
85
|
+
const url = `${baseUrl}/${relPath}`;
|
|
86
|
+
return fetch(url, {
|
|
87
|
+
...options,
|
|
88
|
+
headers: {
|
|
89
|
+
...this.options.credential.builder ? this.options.credential.builder(this.token) : { Authorization: `Bearer ${this.token.accessToken}` },
|
|
90
|
+
...options?.headers
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
93
|
}
|
|
94
94
|
/**
|
|
95
95
|
* Refreshes the Azure access token using the provided credential helper.
|
|
@@ -100,52 +100,63 @@ var AzureClient = class _AzureClient {
|
|
|
100
100
|
}
|
|
101
101
|
};
|
|
102
102
|
|
|
103
|
-
// src/
|
|
103
|
+
// src/credentials/AzureCliCredential.ts
|
|
104
104
|
import { execFile } from "child_process";
|
|
105
105
|
import process2 from "process";
|
|
106
106
|
var AzureCliCredential = class _AzureCliCredential {
|
|
107
107
|
constructor(options) {
|
|
108
108
|
this.options = options;
|
|
109
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Instantiates AzureCliCredential using the AZURE_TENANT_ID environment variable (if set),
|
|
112
|
+
* or falls back to the current Azure CLI context.
|
|
113
|
+
* @returns AzureCliCredential instance
|
|
114
|
+
*/
|
|
115
|
+
static fromEnv() {
|
|
116
|
+
const tenantId = process2.env.AZURE_TENANT_ID;
|
|
117
|
+
return new _AzureCliCredential(tenantId ? { tenantId } : {});
|
|
118
|
+
}
|
|
110
119
|
/**
|
|
111
120
|
* Gets an Azure access token using the Azure CLI.
|
|
112
|
-
* @param scope The resource scope for the token
|
|
113
|
-
* @returns An object with
|
|
121
|
+
* @param scope The resource scope for the token (e.g. 'https://management.azure.com/.default')
|
|
122
|
+
* @returns An object with accessToken, expiresAt, and tokenType
|
|
114
123
|
* @throws If CLI is not installed or not logged in
|
|
115
124
|
*/
|
|
116
125
|
async getToken(scope) {
|
|
117
126
|
try {
|
|
118
|
-
const
|
|
119
|
-
const
|
|
120
|
-
const isLoginError = result.stderr.match("(.*)az login(.*)") && !specificScope;
|
|
121
|
-
const isNotInstallError = result.stderr.match("az:(.*)not found") ?? result.stderr.startsWith("'az' is not recognized");
|
|
122
|
-
if (isNotInstallError) {
|
|
123
|
-
throw new Error("Azure CLI not found. Please install");
|
|
124
|
-
}
|
|
125
|
-
if (isLoginError) {
|
|
126
|
-
throw new Error("Please login to Azure CLI");
|
|
127
|
-
}
|
|
127
|
+
const tenantId = this.options.tenantId ?? await _AzureCliCredential.getCurrentTenantId();
|
|
128
|
+
const result = await this.getCliToken(scope, tenantId);
|
|
128
129
|
return this.parseRawOutput(result.stdout);
|
|
129
130
|
} catch (error) {
|
|
130
|
-
throw new Error(`Failed to get token: ${error.
|
|
131
|
+
throw new Error(`Failed to get token: ${error.message}`);
|
|
131
132
|
}
|
|
132
133
|
}
|
|
133
134
|
/**
|
|
134
|
-
* Runs the Azure CLI to get an access token for the given scope.
|
|
135
|
+
* Runs the Azure CLI to get an access token for the given scope and tenant.
|
|
135
136
|
* @param scope The resource scope
|
|
137
|
+
* @param tenantId The Azure tenant ID
|
|
136
138
|
* @returns Promise resolving to CLI stdout and stderr
|
|
137
139
|
* @private
|
|
138
140
|
*/
|
|
139
|
-
async getCliToken(scope) {
|
|
141
|
+
async getCliToken(scope, tenantId) {
|
|
140
142
|
return new Promise((resolve, reject) => {
|
|
141
143
|
try {
|
|
142
144
|
execFile(
|
|
143
145
|
"az",
|
|
144
|
-
["account", "get-access-token", "--output", "json", "--resource", scope.replace(".default", ""), "--tenant",
|
|
146
|
+
["account", "get-access-token", "--output", "json", "--resource", scope.replace(".default", ""), "--tenant", tenantId],
|
|
145
147
|
{ cwd: process2.cwd(), shell: true, timeout: 3e4 },
|
|
146
148
|
(error, stdout, stderr) => {
|
|
149
|
+
const { isLoginError, isNotInstallError } = _AzureCliCredential.parseCliLoginError(stderr);
|
|
150
|
+
if (isNotInstallError) {
|
|
151
|
+
reject(new Error("Azure CLI not found. Please install"));
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (isLoginError) {
|
|
155
|
+
reject(new Error("Please login to Azure CLI"));
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
147
158
|
if (error) {
|
|
148
|
-
reject(new Error(`Failed to get token: ${error.
|
|
159
|
+
reject(new Error(`Failed to get token: ${error.message}`));
|
|
149
160
|
return;
|
|
150
161
|
}
|
|
151
162
|
resolve({ stdout, stderr });
|
|
@@ -156,10 +167,40 @@ var AzureCliCredential = class _AzureCliCredential {
|
|
|
156
167
|
}
|
|
157
168
|
});
|
|
158
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* Gets the current tenant ID from Azure CLI context.
|
|
172
|
+
* @returns Promise resolving to the current tenant ID string
|
|
173
|
+
* @throws If CLI is not installed or not logged in
|
|
174
|
+
*/
|
|
175
|
+
static async getCurrentTenantId() {
|
|
176
|
+
return new Promise((resolve, reject) => {
|
|
177
|
+
execFile("az", ["account", "show", "--query", "tenantId", "--output", "tsv"], { cwd: process2.cwd(), shell: true, timeout: 1e4 }, (error, stdout, stderr) => {
|
|
178
|
+
const { isLoginError, isNotInstallError } = _AzureCliCredential.parseCliLoginError(stderr);
|
|
179
|
+
if (isNotInstallError) {
|
|
180
|
+
reject(new Error("Azure CLI not found. Please install"));
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (isLoginError) {
|
|
184
|
+
reject(new Error("Please login to Azure CLI"));
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
if (error) {
|
|
188
|
+
reject(new Error(`Failed to detect tenantId from Azure CLI context: ${stderr}`));
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const tenantId = stdout.trim();
|
|
192
|
+
if (!tenantId) {
|
|
193
|
+
reject(new Error("Could not detect tenantId from Azure CLI context."));
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
resolve(tenantId);
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
}
|
|
159
200
|
/**
|
|
160
201
|
* Parses the raw CLI output and returns a token + expiry object.
|
|
161
202
|
* @param output The stdout from Azure CLI
|
|
162
|
-
* @returns An object with
|
|
203
|
+
* @returns An object with accessToken, expiresAt, and tokenType
|
|
163
204
|
* @private
|
|
164
205
|
*/
|
|
165
206
|
parseRawOutput(output) {
|
|
@@ -180,21 +221,27 @@ var AzureCliCredential = class _AzureCliCredential {
|
|
|
180
221
|
};
|
|
181
222
|
}
|
|
182
223
|
/**
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
* @
|
|
224
|
+
* Checks stderr for common Azure CLI login errors.
|
|
225
|
+
* @param stderr The stderr string from CLI
|
|
226
|
+
* @returns Object with isLoginError and isNotInstallError booleans
|
|
227
|
+
* @private
|
|
187
228
|
*/
|
|
188
|
-
static
|
|
189
|
-
|
|
229
|
+
static parseCliLoginError(stderr) {
|
|
230
|
+
const specificScope = stderr.match("(.*)az login --scope(.*)");
|
|
231
|
+
const isLoginError = stderr.match("(.*)az login(.*)") && !specificScope;
|
|
232
|
+
const isNotInstallError = stderr.match("az:(.*)not found") ?? stderr.startsWith("'az' is not recognized");
|
|
233
|
+
return {
|
|
234
|
+
isLoginError: Boolean(isLoginError),
|
|
235
|
+
isNotInstallError: Boolean(isNotInstallError)
|
|
236
|
+
};
|
|
190
237
|
}
|
|
191
238
|
};
|
|
192
239
|
|
|
193
|
-
// src/
|
|
240
|
+
// src/credentials/AzureCredential.ts
|
|
194
241
|
var AzureCredential = class {
|
|
195
242
|
};
|
|
196
243
|
|
|
197
|
-
// src/
|
|
244
|
+
// src/credentials/ManagedIdentityCredential.ts
|
|
198
245
|
var ManagedIdentityCredential = class _ManagedIdentityCredential {
|
|
199
246
|
/**
|
|
200
247
|
* @param options Managed identity credential options
|
|
@@ -265,7 +312,7 @@ var ManagedIdentityCredential = class _ManagedIdentityCredential {
|
|
|
265
312
|
}
|
|
266
313
|
};
|
|
267
314
|
|
|
268
|
-
// src/
|
|
315
|
+
// src/credentials/ServicePrincipalCredential.ts
|
|
269
316
|
import { URLSearchParams as URLSearchParams2 } from "url";
|
|
270
317
|
var ServicePrincipalCredential = class _ServicePrincipalCredential {
|
|
271
318
|
/**
|
|
@@ -341,7 +388,7 @@ var ServicePrincipalCredential = class _ServicePrincipalCredential {
|
|
|
341
388
|
}
|
|
342
389
|
};
|
|
343
390
|
|
|
344
|
-
// src/
|
|
391
|
+
// src/credentials/WorkloadIdentityCredential.ts
|
|
345
392
|
import { readFile } from "fs/promises";
|
|
346
393
|
var WorkloadIdentityCredential = class _WorkloadIdentityCredential {
|
|
347
394
|
/**
|
|
@@ -387,9 +434,9 @@ var WorkloadIdentityCredential = class _WorkloadIdentityCredential {
|
|
|
387
434
|
}
|
|
388
435
|
};
|
|
389
436
|
|
|
390
|
-
// src/
|
|
437
|
+
// src/credentials/ChainedCredential.ts
|
|
391
438
|
var credentialChain = [WorkloadIdentityCredential, ManagedIdentityCredential, ServicePrincipalCredential, AzureCliCredential];
|
|
392
|
-
var
|
|
439
|
+
var ChainedCredential = class {
|
|
393
440
|
/**
|
|
394
441
|
* Attempts to get an Azure access token using the first available credential in the chain.
|
|
395
442
|
* @param scope The resource scope for the token
|
|
@@ -400,7 +447,7 @@ var DefaultChainedCredential = class {
|
|
|
400
447
|
const errors = [];
|
|
401
448
|
const debug = process.env.DEBUG?.includes("tako-azure-rest:credentials") || process.env.DEBUG?.includes("tako-azure-rest:*");
|
|
402
449
|
for (const Credential of credentialChain) {
|
|
403
|
-
const label = `[
|
|
450
|
+
const label = `[ChainedCredential] ${Credential.name}`;
|
|
404
451
|
let start;
|
|
405
452
|
if (debug) {
|
|
406
453
|
start = Date.now();
|
|
@@ -429,7 +476,7 @@ export {
|
|
|
429
476
|
AzureCliCredential,
|
|
430
477
|
AzureClient,
|
|
431
478
|
AzureCredential,
|
|
432
|
-
|
|
479
|
+
ChainedCredential,
|
|
433
480
|
ManagedIdentityCredential,
|
|
434
481
|
ServicePrincipalCredential,
|
|
435
482
|
WorkloadIdentityCredential
|