@azure/identity 1.0.0-preview.1 → 1.0.0-preview.2

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.

Potentially problematic release.


This version of @azure/identity might be problematic. Click here for more details.

Files changed (88) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +50 -23
  3. package/browser/identity.js +9828 -0
  4. package/browser/identity.js.map +1 -0
  5. package/browser/identity.min.js +2 -0
  6. package/browser/identity.min.js.map +1 -0
  7. package/dist/index.js +475 -205
  8. package/dist/index.js.map +1 -1
  9. package/dist-esm/src/client/errors.d.ts +1 -1
  10. package/dist-esm/src/client/errors.d.ts.map +1 -1
  11. package/dist-esm/src/client/errors.js +9 -1
  12. package/dist-esm/src/client/errors.js.map +1 -1
  13. package/dist-esm/src/client/identityClient.d.ts +20 -17
  14. package/dist-esm/src/client/identityClient.d.ts.map +1 -1
  15. package/dist-esm/src/client/identityClient.js +42 -206
  16. package/dist-esm/src/client/identityClient.js.map +1 -1
  17. package/dist-esm/src/credentials/clientCertificateCredential.browser.d.ts +7 -0
  18. package/dist-esm/src/credentials/clientCertificateCredential.browser.d.ts.map +1 -0
  19. package/dist-esm/src/credentials/clientCertificateCredential.browser.js +12 -0
  20. package/dist-esm/src/credentials/clientCertificateCredential.browser.js.map +1 -0
  21. package/dist-esm/src/credentials/clientCertificateCredential.d.ts +5 -5
  22. package/dist-esm/src/credentials/clientCertificateCredential.d.ts.map +1 -1
  23. package/dist-esm/src/credentials/clientCertificateCredential.js +59 -5
  24. package/dist-esm/src/credentials/clientCertificateCredential.js.map +1 -1
  25. package/dist-esm/src/credentials/clientSecretCredential.d.ts +3 -3
  26. package/dist-esm/src/credentials/clientSecretCredential.d.ts.map +1 -1
  27. package/dist-esm/src/credentials/clientSecretCredential.js +27 -4
  28. package/dist-esm/src/credentials/clientSecretCredential.js.map +1 -1
  29. package/dist-esm/src/credentials/deviceCodeCredential.browser.d.ts +7 -0
  30. package/dist-esm/src/credentials/deviceCodeCredential.browser.d.ts.map +1 -0
  31. package/dist-esm/src/credentials/deviceCodeCredential.browser.js +12 -0
  32. package/dist-esm/src/credentials/deviceCodeCredential.browser.js.map +1 -0
  33. package/dist-esm/src/credentials/deviceCodeCredential.d.ts +67 -0
  34. package/dist-esm/src/credentials/deviceCodeCredential.d.ts.map +1 -0
  35. package/dist-esm/src/credentials/deviceCodeCredential.js +139 -0
  36. package/dist-esm/src/credentials/deviceCodeCredential.js.map +1 -0
  37. package/dist-esm/src/credentials/environmentCredential.browser.d.ts +7 -0
  38. package/dist-esm/src/credentials/environmentCredential.browser.d.ts.map +1 -0
  39. package/dist-esm/src/credentials/environmentCredential.browser.js +12 -0
  40. package/dist-esm/src/credentials/environmentCredential.browser.js.map +1 -0
  41. package/dist-esm/src/credentials/environmentCredential.d.ts.map +1 -1
  42. package/dist-esm/src/credentials/environmentCredential.js +0 -4
  43. package/dist-esm/src/credentials/environmentCredential.js.map +1 -1
  44. package/dist-esm/src/credentials/interactiveBrowserCredential.browser.d.ts +32 -0
  45. package/dist-esm/src/credentials/interactiveBrowserCredential.browser.d.ts.map +1 -0
  46. package/dist-esm/src/credentials/interactiveBrowserCredential.browser.js +112 -0
  47. package/dist-esm/src/credentials/interactiveBrowserCredential.browser.js.map +1 -0
  48. package/dist-esm/src/credentials/interactiveBrowserCredential.d.ts +12 -0
  49. package/dist-esm/src/credentials/interactiveBrowserCredential.d.ts.map +1 -0
  50. package/dist-esm/src/credentials/interactiveBrowserCredential.js +17 -0
  51. package/dist-esm/src/credentials/interactiveBrowserCredential.js.map +1 -0
  52. package/dist-esm/src/credentials/interactiveBrowserCredentialOptions.d.ts +24 -0
  53. package/dist-esm/src/credentials/interactiveBrowserCredentialOptions.d.ts.map +1 -0
  54. package/dist-esm/src/credentials/interactiveBrowserCredentialOptions.js +3 -0
  55. package/dist-esm/src/credentials/interactiveBrowserCredentialOptions.js.map +1 -0
  56. package/dist-esm/src/credentials/managedIdentityCredential.browser.d.ts +7 -0
  57. package/dist-esm/src/credentials/managedIdentityCredential.browser.d.ts.map +1 -0
  58. package/dist-esm/src/credentials/managedIdentityCredential.browser.js +15 -0
  59. package/dist-esm/src/credentials/managedIdentityCredential.browser.js.map +1 -0
  60. package/dist-esm/src/credentials/managedIdentityCredential.d.ts +10 -1
  61. package/dist-esm/src/credentials/managedIdentityCredential.d.ts.map +1 -1
  62. package/dist-esm/src/credentials/managedIdentityCredential.js +144 -2
  63. package/dist-esm/src/credentials/managedIdentityCredential.js.map +1 -1
  64. package/dist-esm/src/credentials/usernamePasswordCredential.d.ts +39 -0
  65. package/dist-esm/src/credentials/usernamePasswordCredential.d.ts.map +1 -0
  66. package/dist-esm/src/credentials/usernamePasswordCredential.js +67 -0
  67. package/dist-esm/src/credentials/usernamePasswordCredential.js.map +1 -0
  68. package/dist-esm/src/index.d.ts +4 -0
  69. package/dist-esm/src/index.d.ts.map +1 -1
  70. package/dist-esm/src/index.js +3 -0
  71. package/dist-esm/src/index.js.map +1 -1
  72. package/package.json +32 -14
  73. package/src/client/errors.ts +11 -3
  74. package/src/client/identityClient.ts +64 -246
  75. package/src/credentials/clientCertificateCredential.browser.ts +27 -0
  76. package/src/credentials/clientCertificateCredential.ts +72 -22
  77. package/src/credentials/clientSecretCredential.ts +32 -17
  78. package/src/credentials/deviceCodeCredential.browser.ts +27 -0
  79. package/src/credentials/deviceCodeCredential.ts +203 -0
  80. package/src/credentials/environmentCredential.browser.ts +19 -0
  81. package/src/credentials/environmentCredential.ts +5 -9
  82. package/src/credentials/interactiveBrowserCredential.browser.ts +134 -0
  83. package/src/credentials/interactiveBrowserCredential.ts +31 -0
  84. package/src/credentials/interactiveBrowserCredentialOptions.ts +30 -0
  85. package/src/credentials/managedIdentityCredential.browser.ts +22 -0
  86. package/src/credentials/managedIdentityCredential.ts +179 -8
  87. package/src/credentials/usernamePasswordCredential.ts +83 -0
  88. package/src/index.ts +4 -0
@@ -2,44 +2,58 @@
2
2
  // Licensed under the MIT License.
3
3
 
4
4
  import qs from "qs";
5
- import jws from "jws";
6
- import uuid from "uuid";
7
5
  import {
8
6
  AccessToken,
9
7
  ServiceClient,
10
8
  ServiceClientOptions,
11
- GetTokenOptions,
12
9
  WebResource,
13
10
  RequestPrepareOptions,
14
- RestError
11
+ GetTokenOptions
15
12
  } from "@azure/core-http";
16
13
  import { AuthenticationError } from "./errors";
17
14
 
18
- const SelfSignedJwtLifetimeMins = 10;
19
15
  const DefaultAuthorityHost = "https://login.microsoftonline.com";
20
- const DefaultScopeSuffix = "/.default";
21
- export const ImdsEndpoint = "http://169.254.169.254/metadata/identity/oauth2/token";
22
- export const ImdsApiVersion = "2018-02-01";
23
- export const AppServiceMsiApiVersion = "2017-09-01";
16
+
17
+ /**
18
+ * An internal type used to communicate details of a token request's
19
+ * response that should not be sent back as part of the AccessToken.
20
+ */
21
+ export interface TokenResponse {
22
+ /**
23
+ * The AccessToken to be returned from getToken.
24
+ */
25
+ accessToken: AccessToken,
26
+
27
+ /**
28
+ * The refresh token if the 'offline_access' scope was used.
29
+ */
30
+ refreshToken?: string
31
+ }
24
32
 
25
33
  export class IdentityClient extends ServiceClient {
34
+ public authorityHost: string;
35
+
26
36
  constructor(options?: IdentityClientOptions) {
27
37
  options = options || IdentityClient.getDefaultOptions();
28
38
  super(undefined, options);
29
39
 
30
- this.baseUri = options.authorityHost;
40
+ this.baseUri = this.authorityHost = options.authorityHost || DefaultAuthorityHost;
41
+
42
+ if (!this.baseUri.startsWith("https:")) {
43
+ throw new Error("The authorityHost address must use the 'https' protocol.");
44
+ }
31
45
  }
32
46
 
33
- private createWebResource(requestOptions: RequestPrepareOptions): WebResource {
47
+ createWebResource(requestOptions: RequestPrepareOptions): WebResource {
34
48
  const webResource = new WebResource();
35
49
  webResource.prepare(requestOptions);
36
50
  return webResource;
37
51
  }
38
52
 
39
- private async sendTokenRequest(
53
+ async sendTokenRequest(
40
54
  webResource: WebResource,
41
55
  expiresOnParser?: (responseBody: any) => number,
42
- ): Promise<AccessToken | null> {
56
+ ): Promise<TokenResponse | null> {
43
57
  const response = await this.sendRequest(webResource);
44
58
 
45
59
  expiresOnParser = expiresOnParser || ((responseBody: any) => {
@@ -48,262 +62,66 @@ export class IdentityClient extends ServiceClient {
48
62
 
49
63
  if (response.status === 200 || response.status === 201) {
50
64
  return {
51
- token: response.parsedBody.access_token,
52
- expiresOnTimestamp: expiresOnParser(response.parsedBody)
65
+ accessToken: {
66
+ token: response.parsedBody.access_token,
67
+ expiresOnTimestamp: expiresOnParser(response.parsedBody)
68
+ },
69
+ refreshToken: response.parsedBody.refresh_token,
53
70
  };
54
71
  } else {
55
- throw new AuthenticationError(response.status, response.bodyAsText);
72
+ throw new AuthenticationError(response.status, response.parsedBody || response.bodyAsText);
56
73
  }
57
74
  }
58
75
 
59
- private mapScopesToResource(scopes: string | string[]): string {
60
- let scope = "";
61
- if (Array.isArray(scopes)) {
62
- if (scopes.length !== 1) {
63
- throw "To convert to a resource string the specified array must be exactly length 1";
64
- }
65
-
66
- scope = scopes[0];
67
- } else if (typeof scopes === "string") {
68
- scope = scopes;
69
- }
70
-
71
- if (!scope.endsWith(DefaultScopeSuffix)) {
72
- return scope;
73
- }
74
-
75
- return scope.substr(0, scope.lastIndexOf(DefaultScopeSuffix));
76
- }
77
-
78
- private dateInSeconds(date: Date): number {
79
- return Math.floor(date.getTime() / 1000);
80
- }
81
-
82
- private addMinutes(date: Date, minutes: number): Date {
83
- date.setMinutes(date.getMinutes() + minutes);
84
- return date;
85
- }
86
-
87
- private createImdsAuthRequest(resource: string, clientId?: string): RequestPrepareOptions {
88
- const queryParameters: any = {
89
- resource,
90
- "api-version": ImdsApiVersion
91
- };
92
-
93
- if (clientId) {
94
- queryParameters.client_id = clientId;
95
- }
96
-
97
- return {
98
- url: ImdsEndpoint,
99
- method: "GET",
100
- queryParameters,
101
- headers: {
102
- Accept: "application/json",
103
- Metadata: true
104
- }
105
- };
106
- }
107
-
108
- private createAppServiceMsiAuthRequest(resource: string, clientId?: string): RequestPrepareOptions {
109
- const queryParameters: any = {
110
- resource,
111
- "api-version": AppServiceMsiApiVersion,
112
- };
113
-
114
- if (clientId) {
115
- queryParameters.client_id = clientId;
116
- }
117
-
118
- return {
119
- url: process.env.MSI_ENDPOINT,
120
- method: "GET",
121
- queryParameters,
122
- headers: {
123
- Accept: "application/json",
124
- secret: process.env.MSI_SECRET
125
- }
126
- };
127
- }
128
-
129
- private createCloudShellMsiAuthRequest(resource: string, clientId?: string): RequestPrepareOptions {
130
- const body: any = {
131
- resource
132
- };
133
-
134
- if (clientId) {
135
- body.client_id = clientId;
76
+ async refreshAccessToken(
77
+ tenantId: string,
78
+ clientId: string,
79
+ scopes: string,
80
+ refreshToken: string | undefined,
81
+ clientSecret: string | undefined,
82
+ expiresOnParser?: (responseBody: any) => number,
83
+ options?: GetTokenOptions
84
+ ): Promise<TokenResponse | null> {
85
+ if (refreshToken === undefined) {
86
+ return null;
136
87
  }
137
88
 
138
- return {
139
- url: process.env.MSI_ENDPOINT,
140
- method: "POST",
141
- body: qs.stringify(body),
142
- headers: {
143
- Accept: "application/json",
144
- Metadata: true,
145
- "Content-Type": "application/x-www-form-urlencoded"
146
- }
89
+ const refreshParams = {
90
+ grant_type: "refresh_token",
91
+ client_id: clientId,
92
+ refresh_token: refreshToken,
93
+ scope: scopes
147
94
  };
148
- }
149
95
 
150
- private async pingImdsEndpoint(resource: string, clientId?: string): Promise<boolean> {
151
- const request = this.createImdsAuthRequest(resource, clientId);
152
-
153
- // This will always be populated, but let's make TypeScript happy
154
- if (request.headers) {
155
- // Remove the Metadata header to invoke a request error from
156
- // IMDS endpoint
157
- delete request.headers.Metadata;
96
+ if (clientSecret !== undefined) {
97
+ (refreshParams as any).client_secret = clientSecret;
158
98
  }
159
99
 
160
- // Create a request with a 500 msec timeout since we expect that
161
- // not having a "Metadata" header should cause an error to be
162
- // returned quickly from the endpoint, proving its availability.
163
- const webResource = this.createWebResource(request);
164
- webResource.timeout = 500;
165
-
166
- try {
167
- await this.sendRequest(webResource);
168
- } catch (err) {
169
- if (err instanceof RestError && err.code === RestError.REQUEST_SEND_ERROR) {
170
- // Either request failed or IMDS endpoint isn't available
171
- return false;
172
- }
173
- }
174
-
175
- // If we received any response, the endpoint is available
176
- return true;
177
- }
178
-
179
- authenticateClientSecret(
180
- tenantId: string,
181
- clientId: string,
182
- clientSecret: string,
183
- scopes: string | string[],
184
- getTokenOptions?: GetTokenOptions
185
- ): Promise<AccessToken | null> {
186
100
  const webResource = this.createWebResource({
187
- url: `${this.baseUri}/${tenantId}/oauth2/v2.0/token`,
101
+ url: `${this.authorityHost}/${tenantId}/oauth2/v2.0/token`,
188
102
  method: "POST",
189
103
  disableJsonStringifyOnBody: true,
190
104
  deserializationMapper: undefined,
191
- body: qs.stringify({
192
- response_type: "token",
193
- grant_type: "client_credentials",
194
- client_id: clientId,
195
- client_secret: clientSecret,
196
- scope: typeof scopes === "string" ? scopes : scopes.join(" ")
197
- }),
105
+ body: qs.stringify(refreshParams),
198
106
  headers: {
199
107
  Accept: "application/json",
200
108
  "Content-Type": "application/x-www-form-urlencoded"
201
109
  },
202
- abortSignal: getTokenOptions && getTokenOptions.abortSignal
110
+ abortSignal: options && options.abortSignal
203
111
  });
204
112
 
205
- return this.sendTokenRequest(webResource);
206
- }
207
-
208
- async authenticateManagedIdentity(
209
- scopes: string | string[],
210
- checkIfImdsEndpointAvailable: boolean,
211
- clientId?: string,
212
- getTokenOptions?: GetTokenOptions
213
- ): Promise<AccessToken | null> {
214
- let authRequestOptions: RequestPrepareOptions;
215
- const resource = this.mapScopesToResource(scopes);
216
- let expiresInParser: ((requestBody: any) => number) | undefined;
217
-
218
- // Detect which type of environment we are running in
219
- if (process.env.MSI_ENDPOINT) {
220
- if (process.env.MSI_SECRET) {
221
- // Running in App Service
222
- authRequestOptions = this.createAppServiceMsiAuthRequest(resource, clientId);
223
- expiresInParser = (requestBody: any) => {
224
- // Parse a date format like "06/20/2019 02:57:58 +00:00" and
225
- // convert it into a JavaScript-formatted date
226
- const m = requestBody.expires_on.match(/(\d\d)\/(\d\d)\/(\d\d\d\d) (\d\d):(\d\d):(\d\d) (\+|-)(\d\d):(\d\d)/)
227
- return Date.parse(`${m[3]}-${m[1]}-${m[2]}T${m[4]}:${m[5]}:${m[6]}${m[7]}${m[8]}:${m[9]}`)
228
- };
229
- } else {
230
- // Running in Cloud Shell
231
- authRequestOptions = this.createCloudShellMsiAuthRequest(resource, clientId);
232
- }
233
- } else {
234
- // Ping the IMDS endpoint to see if it's available
235
- if (!checkIfImdsEndpointAvailable || await this.pingImdsEndpoint(resource, clientId)) {
236
- // Running in an Azure VM
237
- authRequestOptions = this.createImdsAuthRequest(resource, clientId);
238
- } else {
239
- // Returning null tells the ManagedIdentityCredential that
240
- // no MSI authentication endpoints are available
113
+ try {
114
+ return await this.sendTokenRequest(webResource, expiresOnParser);
115
+ } catch (err) {
116
+ if (err instanceof AuthenticationError && err.errorResponse.error === "interaction_required") {
117
+ // It's likely that the refresh token has expired, so
118
+ // return null so that the credential implementation will
119
+ // initiate the authentication flow again.
241
120
  return null;
121
+ } else {
122
+ throw err;
242
123
  }
243
124
  }
244
-
245
- const webResource = this.createWebResource({
246
- disableJsonStringifyOnBody: true,
247
- deserializationMapper: undefined,
248
- abortSignal: getTokenOptions && getTokenOptions.abortSignal,
249
- ...authRequestOptions
250
- });
251
-
252
- return this.sendTokenRequest(webResource, expiresInParser);
253
- }
254
-
255
- authenticateClientCertificate(
256
- tenantId: string,
257
- clientId: string,
258
- certificateString: string,
259
- certificateX5t: string,
260
- scopes: string | string[],
261
- getTokenOptions?: GetTokenOptions
262
- ): Promise<AccessToken | null> {
263
- const tokenId = uuid.v4();
264
- const audienceUrl = `${this.baseUri}/${tenantId}/oauth2/v2.0/token`;
265
- const header: jws.Header = {
266
- typ: "JWT",
267
- alg: "RS256",
268
- x5t: certificateX5t
269
- };
270
-
271
- const payload = {
272
- iss: clientId,
273
- sub: clientId,
274
- aud: audienceUrl,
275
- jti: tokenId,
276
- nbf: this.dateInSeconds(new Date()),
277
- exp: this.dateInSeconds(this.addMinutes(new Date(), SelfSignedJwtLifetimeMins))
278
- };
279
-
280
- const clientAssertion = jws.sign({
281
- header,
282
- payload,
283
- secret: certificateString
284
- });
285
-
286
- const webResource = this.createWebResource({
287
- url: audienceUrl,
288
- method: "POST",
289
- disableJsonStringifyOnBody: true,
290
- deserializationMapper: undefined,
291
- body: qs.stringify({
292
- response_type: "token",
293
- grant_type: "client_credentials",
294
- client_id: clientId,
295
- client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
296
- client_assertion: clientAssertion,
297
- scope: typeof scopes === "string" ? scopes : scopes.join(" ")
298
- }),
299
- headers: {
300
- Accept: "application/json",
301
- "Content-Type": "application/x-www-form-urlencoded"
302
- },
303
- abortSignal: getTokenOptions && getTokenOptions.abortSignal
304
- });
305
-
306
- return this.sendTokenRequest(webResource);
307
125
  }
308
126
 
309
127
  static getDefaultOptions(): IdentityClientOptions {
@@ -314,5 +132,5 @@ export class IdentityClient extends ServiceClient {
314
132
  }
315
133
 
316
134
  export interface IdentityClientOptions extends ServiceClientOptions {
317
- authorityHost: string;
135
+ authorityHost?: string;
318
136
  }
@@ -0,0 +1,27 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+
4
+ /* eslint-disable @typescript-eslint/no-unused-vars */
5
+
6
+ import { TokenCredential, GetTokenOptions, AccessToken } from "@azure/core-http";
7
+ import { IdentityClientOptions } from "../client/identityClient";
8
+
9
+ const BrowserNotSupportedError = new Error("ClientCertificateCredential is not supported in the browser.");
10
+
11
+ export class ClientCertificateCredential implements TokenCredential {
12
+ constructor(
13
+ tenantId: string,
14
+ clientId: string,
15
+ certificatePath: string,
16
+ options?: IdentityClientOptions
17
+ ) {
18
+ throw BrowserNotSupportedError;
19
+ }
20
+
21
+ public getToken(
22
+ scopes: string | string[],
23
+ options?: GetTokenOptions
24
+ ): Promise<AccessToken | null> {
25
+ throw BrowserNotSupportedError;
26
+ }
27
+ }
@@ -1,32 +1,45 @@
1
1
  // Copyright (c) Microsoft Corporation.
2
2
  // Licensed under the MIT License.
3
3
 
4
+ import qs from "qs";
5
+ import jws from "jws";
6
+ import uuid from "uuid";
4
7
  import { readFileSync } from "fs";
5
8
  import { createHash } from "crypto";
6
9
  import { TokenCredential, GetTokenOptions, AccessToken } from "@azure/core-http";
7
10
  import { IdentityClientOptions, IdentityClient } from "../client/identityClient";
8
11
 
12
+ const SelfSignedJwtLifetimeMins = 10;
13
+
14
+ function timestampInSeconds(date: Date): number {
15
+ return Math.floor(date.getTime() / 1000);
16
+ }
17
+
18
+ function addMinutes(date: Date, minutes: number): Date {
19
+ date.setMinutes(date.getMinutes() + minutes);
20
+ return date;
21
+ }
22
+
9
23
  /**
10
24
  * Enables authentication to Azure Active Directory using a PEM-encoded
11
25
  * certificate that is assigned to an App Registration. More information
12
26
  * on how to configure certificate authentication can be found here:
13
- *
27
+ *
14
28
  * https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-certificate-credentials#register-your-certificate-with-azure-ad
15
29
  *
16
30
  */
17
31
  export class ClientCertificateCredential implements TokenCredential {
18
32
  private identityClient: IdentityClient;
19
- private _tenantId: string;
20
- private _clientId: string;
21
- private _certificateString: string;
22
-
23
- public certificateThumbprint: string;
24
- public certificateX5t: string;
33
+ private tenantId: string;
34
+ private clientId: string;
35
+ private certificateString: string;
36
+ private certificateThumbprint: string;
37
+ private certificateX5t: string;
25
38
 
26
39
  /**
27
40
  * Creates an instance of the ClientCertificateCredential with the details
28
41
  * needed to authenticate against Azure Active Directory with a certificate.
29
- *
42
+ *
30
43
  * @param tenantId The Azure Active Directory tenant (directory) ID.
31
44
  * @param clientId The client (application) ID of an App Registration in the tenant.
32
45
  * @param certificatePath The path to a PEM-encoded public/private key certificate on the filesystem.
@@ -39,13 +52,13 @@ export class ClientCertificateCredential implements TokenCredential {
39
52
  options?: IdentityClientOptions
40
53
  ) {
41
54
  this.identityClient = new IdentityClient(options);
42
- this._tenantId = tenantId;
43
- this._clientId = clientId;
55
+ this.tenantId = tenantId;
56
+ this.clientId = clientId;
44
57
 
45
- this._certificateString = readFileSync(certificatePath, "utf8");
58
+ this.certificateString = readFileSync(certificatePath, "utf8");
46
59
 
47
60
  const certificatePattern = /(-+BEGIN CERTIFICATE-+)(\n\r?|\r\n?)([A-Za-z0-9+/\n\r]+=*)(\n\r?|\r\n?)(-+END CERTIFICATE-+)/;
48
- const matchCert = this._certificateString.match(certificatePattern);
61
+ const matchCert = this.certificateString.match(certificatePattern);
49
62
  const publicKey = matchCert ? matchCert[3] : "";
50
63
  if (!publicKey) {
51
64
  throw new Error(
@@ -66,22 +79,59 @@ export class ClientCertificateCredential implements TokenCredential {
66
79
  * successful. If authentication cannot be performed at this time, this method may
67
80
  * return null. If an error occurs during authentication, an {@link AuthenticationError}
68
81
  * containing failure details will be thrown.
69
- *
82
+ *
70
83
  * @param scopes The list of scopes for which the token will have access.
71
84
  * @param options The options used to configure any requests this
72
85
  * TokenCredential implementation might make.
73
86
  */
74
- public getToken(
87
+ public async getToken(
75
88
  scopes: string | string[],
76
89
  options?: GetTokenOptions
77
90
  ): Promise<AccessToken | null> {
78
- return this.identityClient.authenticateClientCertificate(
79
- this._tenantId,
80
- this._clientId,
81
- this._certificateString,
82
- this.certificateX5t,
83
- scopes,
84
- options
85
- );
91
+ const tokenId = uuid.v4();
92
+ const audienceUrl = `${this.identityClient.authorityHost}/${this.tenantId}/oauth2/v2.0/token`;
93
+ const header: jws.Header = {
94
+ typ: "JWT",
95
+ alg: "RS256",
96
+ x5t: this.certificateX5t
97
+ };
98
+
99
+ const payload = {
100
+ iss: this.clientId,
101
+ sub: this.clientId,
102
+ aud: audienceUrl,
103
+ jti: tokenId,
104
+ nbf: timestampInSeconds(new Date()),
105
+ exp: timestampInSeconds(addMinutes(new Date(), SelfSignedJwtLifetimeMins))
106
+ };
107
+
108
+ const clientAssertion = jws.sign({
109
+ header,
110
+ payload,
111
+ secret: this.certificateString
112
+ });
113
+
114
+ const webResource = this.identityClient.createWebResource({
115
+ url: audienceUrl,
116
+ method: "POST",
117
+ disableJsonStringifyOnBody: true,
118
+ deserializationMapper: undefined,
119
+ body: qs.stringify({
120
+ response_type: "token",
121
+ grant_type: "client_credentials",
122
+ client_id: this.clientId,
123
+ client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
124
+ client_assertion: clientAssertion,
125
+ scope: typeof scopes === "string" ? scopes : scopes.join(" ")
126
+ }),
127
+ headers: {
128
+ Accept: "application/json",
129
+ "Content-Type": "application/x-www-form-urlencoded"
130
+ },
131
+ abortSignal: options && options.abortSignal
132
+ });
133
+
134
+ const tokenResponse = await this.identityClient.sendTokenRequest(webResource);
135
+ return (tokenResponse && tokenResponse.accessToken) || null;
86
136
  }
87
137
  }
@@ -1,6 +1,7 @@
1
1
  // Copyright (c) Microsoft Corporation.
2
2
  // Licensed under the MIT License.
3
3
 
4
+ import qs from "qs";
4
5
  import { TokenCredential, GetTokenOptions, AccessToken } from "@azure/core-http";
5
6
  import { IdentityClientOptions, IdentityClient } from "../client/identityClient";
6
7
 
@@ -8,21 +9,21 @@ import { IdentityClientOptions, IdentityClient } from "../client/identityClient"
8
9
  * Enables authentication to Azure Active Directory using a client secret
9
10
  * that was generated for an App Registration. More information on how
10
11
  * to configure a client secret can be found here:
11
- *
12
+ *
12
13
  * https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-configure-app-access-web-apis#add-credentials-to-your-web-application
13
14
  *
14
15
  */
15
16
  export class ClientSecretCredential implements TokenCredential {
16
17
  private identityClient: IdentityClient;
17
- private _tenantId: string;
18
- private _clientId: string;
19
- private _clientSecret: string;
18
+ private tenantId: string;
19
+ private clientId: string;
20
+ private clientSecret: string;
20
21
 
21
22
  /**
22
23
  * Creates an instance of the ClientSecretCredential with the details
23
24
  * needed to authenticate against Azure Active Directory with a client
24
25
  * secret.
25
- *
26
+ *
26
27
  * @param tenantId The Azure Active Directory tenant (directory) ID.
27
28
  * @param clientId The client (application) ID of an App Registration in the tenant.
28
29
  * @param clientSecret A client secret that was generated for the App Registration.
@@ -35,9 +36,9 @@ export class ClientSecretCredential implements TokenCredential {
35
36
  options?: IdentityClientOptions
36
37
  ) {
37
38
  this.identityClient = new IdentityClient(options);
38
- this._tenantId = tenantId;
39
- this._clientId = clientId;
40
- this._clientSecret = clientSecret;
39
+ this.tenantId = tenantId;
40
+ this.clientId = clientId;
41
+ this.clientSecret = clientSecret;
41
42
  }
42
43
 
43
44
  /**
@@ -45,21 +46,35 @@ export class ClientSecretCredential implements TokenCredential {
45
46
  * successful. If authentication cannot be performed at this time, this method may
46
47
  * return null. If an error occurs during authentication, an {@link AuthenticationError}
47
48
  * containing failure details will be thrown.
48
- *
49
+ *
49
50
  * @param scopes The list of scopes for which the token will have access.
50
51
  * @param options The options used to configure any requests this
51
52
  * TokenCredential implementation might make.
52
53
  */
53
- public getToken(
54
+ public async getToken(
54
55
  scopes: string | string[],
55
56
  options?: GetTokenOptions
56
57
  ): Promise<AccessToken | null> {
57
- return this.identityClient.authenticateClientSecret(
58
- this._tenantId,
59
- this._clientId,
60
- this._clientSecret,
61
- scopes,
62
- options
63
- );
58
+ const webResource = this.identityClient.createWebResource({
59
+ url: `${this.identityClient.authorityHost}/${this.tenantId}/oauth2/v2.0/token`,
60
+ method: "POST",
61
+ disableJsonStringifyOnBody: true,
62
+ deserializationMapper: undefined,
63
+ body: qs.stringify({
64
+ response_type: "token",
65
+ grant_type: "client_credentials",
66
+ client_id: this.clientId,
67
+ client_secret: this.clientSecret,
68
+ scope: typeof scopes === "string" ? scopes : scopes.join(" ")
69
+ }),
70
+ headers: {
71
+ Accept: "application/json",
72
+ "Content-Type": "application/x-www-form-urlencoded"
73
+ },
74
+ abortSignal: options && options.abortSignal
75
+ });
76
+
77
+ const tokenResponse = await this.identityClient.sendTokenRequest(webResource);
78
+ return (tokenResponse && tokenResponse.accessToken) || null;
64
79
  }
65
80
  }
@@ -0,0 +1,27 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+
4
+ /* eslint-disable @typescript-eslint/no-unused-vars */
5
+
6
+ import { TokenCredential, GetTokenOptions, AccessToken } from "@azure/core-http";
7
+ import { IdentityClientOptions } from "../client/identityClient";
8
+
9
+ const BrowserNotSupportedError = new Error("DeviceCodeCredential is not supported in the browser.");
10
+
11
+ export class DeviceCodeCredential implements TokenCredential {
12
+ constructor(
13
+ tenantId: string,
14
+ clientId: string,
15
+ userPromptCallback: (details: any) => void,
16
+ options?: IdentityClientOptions
17
+ ) {
18
+ throw BrowserNotSupportedError;
19
+ }
20
+
21
+ public getToken(
22
+ scopes: string | string[],
23
+ options?: GetTokenOptions
24
+ ): Promise<AccessToken | null> {
25
+ throw BrowserNotSupportedError;
26
+ }
27
+ }