From 32c780f73d1dcaf45ca118fc238553c17730306f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodr=C3=ADguez?= Date: Thu, 17 Jun 2021 21:16:17 -0400 Subject: [PATCH] [Identity] Adding regional STS support (#15778) Added regional STS support to client credential types. - Added the `RegionalAuthority` type, that allows specifying Azure regions. - Added `regionalAuthority` property to `ClientSecretCredentialOptions` and `ClientCertificateCredentialOptions`. - If instead of a region, `autoDiscoverRegion` is specified as the value for `regionalAuthority`, MSAL will be used to attempt to discover the region. - A region can also be specified through the `AZURE_REGIONAL_AUTHORITY_NAME` environment variable. Fixes #15762 Fixes #15714 --- sdk/identity/identity/CHANGELOG.md | 8 ++ sdk/identity/identity/package.json | 7 +- ...ports_specifying_the_regional_authority.js | 5 + ...ports_specifying_the_regional_authority.js | 111 +++++++++++++++++ ...ports_specifying_the_regional_authority.js | 5 + sdk/identity/identity/review/identity.api.md | 59 +++++++++ .../clientCertificateCredentialOptions.ts | 6 + .../clientSecretCredentialOptions.ts | 9 +- sdk/identity/identity/src/index.ts | 1 + .../msal/nodeFlows/msalClientCertificate.ts | 3 +- .../src/msal/nodeFlows/msalClientSecret.ts | 3 +- .../identity/src/msal/nodeFlows/nodeCommon.ts | 12 ++ .../identity/src/regionalAuthority.ts | 114 ++++++++++++++++++ .../node/clientCertificateCredential.spec.ts | 31 ++++- .../node/clientSecretCredential.spec.ts | 31 ++++- .../node/clientSecretCredential.spec.ts | 23 +++- 16 files changed, 416 insertions(+), 12 deletions(-) create mode 100644 sdk/identity/identity/recordings/node/clientcertificatecredential_internal/recording_supports_specifying_the_regional_authority.js create mode 100644 sdk/identity/identity/recordings/node/clientsecretcredential/recording_supports_specifying_the_regional_authority.js create mode 100644 sdk/identity/identity/recordings/node/clientsecretcredential_internal/recording_supports_specifying_the_regional_authority.js create mode 100644 sdk/identity/identity/src/regionalAuthority.ts diff --git a/sdk/identity/identity/CHANGELOG.md b/sdk/identity/identity/CHANGELOG.md index 07f68c7425d0..3fd454807739 100644 --- a/sdk/identity/identity/CHANGELOG.md +++ b/sdk/identity/identity/CHANGELOG.md @@ -11,6 +11,14 @@ - Removed the protected method `getAzureCliAccessToken` from the public API of the `AzureCliCredential`. While it will continue to be available as part of v1, we won't be supporting this method as part of v2's public API. +### New Features + +- Added regional STS support to client credential types. + - Added the `RegionalAuthority` type, that allows specifying Azure regions. + - Added `regionalAuthority` property to `ClientSecretCredentialOptions` and `ClientCertificateCredentialOptions`. + - If instead of a region, `AutoDiscoverRegion` is specified as the value for `regionalAuthority`, MSAL will be used to attempt to discover the region. + - A region can also be specified through the `AZURE_REGIONAL_AUTHORITY_NAME` environment variable. + ## 2.0.0-beta.3 (2021-05-12) ### New features diff --git a/sdk/identity/identity/package.json b/sdk/identity/identity/package.json index 1305800bed36..901f8b5252e9 100644 --- a/sdk/identity/identity/package.json +++ b/sdk/identity/identity/package.json @@ -43,7 +43,8 @@ "test:node": "npm run clean && npm run build:test && npm run unit-test:node && npm run integration-test:node", "test": "npm run clean && npm run build:test && npm run unit-test && npm run integration-test", "unit-test:browser": "karma start --single-run", - "unit-test:node": "mocha -r esm -r ts-node/register --reporter ../../../common/tools/mocha-multi-reporter.js --timeout 180000 --full-trace --exclude \"test/**/browser/**/*.spec.ts\" \"test/**/*.spec.ts\"", + "unit-test:node": "mocha -r esm -r ts-node/register --reporter ../../../common/tools/mocha-multi-reporter.js --timeout 300000 --full-trace --exclude \"test/**/browser/**/*.spec.ts\" \"test/**/*.spec.ts\"", + "unit-test:node:no-timeouts": "mocha -r esm -r ts-node/register --reporter ../../../common/tools/mocha-multi-reporter.js --no-timeouts --full-trace --exclude \"test/**/browser/**/*.spec.ts\" \"test/**/*.spec.ts\"", "unit-test": "npm run unit-test:node && npm run unit-test:browser", "docs": "typedoc --excludePrivate --excludeNotExported --excludeExternals --stripInternal --mode file --out ./dist/docs ./src" }, @@ -81,8 +82,8 @@ "@azure/core-tracing": "1.0.0-preview.11", "@azure/logger": "^1.0.0", "@azure/abort-controller": "^1.0.0", - "@azure/msal-common": "^4.0.0", - "@azure/msal-node": "^1.0.2", + "@azure/msal-common": "^4.3.0", + "@azure/msal-node": "^1.1.0", "@azure/msal-browser": "^2.0.0", "@types/stoppable": "^1.1.0", "events": "^3.0.0", diff --git a/sdk/identity/identity/recordings/node/clientcertificatecredential_internal/recording_supports_specifying_the_regional_authority.js b/sdk/identity/identity/recordings/node/clientcertificatecredential_internal/recording_supports_specifying_the_regional_authority.js new file mode 100644 index 000000000000..39bf9fd9d887 --- /dev/null +++ b/sdk/identity/identity/recordings/node/clientcertificatecredential_internal/recording_supports_specifying_the_regional_authority.js @@ -0,0 +1,5 @@ +let nock = require('nock'); + +module.exports.hash = "1c295e144eadd1075576c2a02e3ede6f"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} diff --git a/sdk/identity/identity/recordings/node/clientsecretcredential/recording_supports_specifying_the_regional_authority.js b/sdk/identity/identity/recordings/node/clientsecretcredential/recording_supports_specifying_the_regional_authority.js new file mode 100644 index 000000000000..04b1e5ca3450 --- /dev/null +++ b/sdk/identity/identity/recordings/node/clientsecretcredential/recording_supports_specifying_the_regional_authority.js @@ -0,0 +1,111 @@ +let nock = require('nock'); + +module.exports.hash = "fe292480b4563761fe80dd6d2717255c"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/common/discovery/instance') + .query(true) + .reply(200, {"tenant_discovery_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration","api-version":"1.1","metadata":[{"preferred_network":"login.microsoftonline.com","preferred_cache":"login.windows.net","aliases":["login.microsoftonline.com","login.windows.net","login.microsoft.com","sts.windows.net"]},{"preferred_network":"login.partner.microsoftonline.cn","preferred_cache":"login.partner.microsoftonline.cn","aliases":["login.partner.microsoftonline.cn","login.chinacloudapi.cn"]},{"preferred_network":"login.microsoftonline.de","preferred_cache":"login.microsoftonline.de","aliases":["login.microsoftonline.de"]},{"preferred_network":"login.microsoftonline.us","preferred_cache":"login.microsoftonline.us","aliases":["login.microsoftonline.us","login.usgovcloudapi.net"]},{"preferred_network":"login-us.microsoftonline.com","preferred_cache":"login-us.microsoftonline.com","aliases":["login-us.microsoftonline.com"]}]}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '0579a12d-488e-4943-b17c-48d1aa313900', + 'x-ms-ests-server', + '2.1.11829.4 - EUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Jul-2021 17:42:37 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 16 Jun 2021 17:42:37 GMT', + 'Content-Length', + '980' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, {"token_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","client_secret_basic"],"jwks_uri":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"response_types_supported":["code","id_token","code id_token","id_token token"],"scopes_supported":["openid","profile","email","offline_access"],"issuer":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0","request_uri_parameter_supported":false,"userinfo_endpoint":"https://graph.microsoft.com/oidc/userinfo","authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize","device_authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode","http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout","claims_supported":["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_username","name","tid","ver","at_hash","c_hash","email"],"kerberos_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/kerberos","tenant_region_scope":"WW","cloud_instance_name":"microsoftonline.com","cloud_graph_host_name":"graph.windows.net","msgraph_host":"graph.microsoft.com","rbac_url":"https://pas.windows.net"}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '1111d97f-702c-43ca-b72f-bbeae7b51c00', + 'x-ms-ests-server', + '2.1.11829.4 - WUS2 ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Jul-2021 17:42:37 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 16 Jun 2021 17:42:37 GMT', + 'Content-Length', + '1753' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .post('/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token', "client_id=azure_client_id&scope=https%3A%2F%2Fsanitized%2F&grant_type=client_credentials&x-client-SKU=msal.js.node&x-client-VER=1.1.0&x-client-OS=linux&x-client-CPU=x64&x-ms-lib-capability=retry-after, h429&x-client-current-telemetry=2|771,0|,&x-client-last-telemetry=2|0|||0,0&client-request-id=client-request-id&client_secret=azure_client_secret") + .reply(200, {"token_type":"Bearer","expires_in":86399,"ext_expires_in":86399,"access_token":"access_token"}, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '46df8cdf-7b2e-4604-88da-76b9cfe56700', + 'x-ms-ests-server', + '2.1.11829.4 - SCUS ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Jul-2021 17:47:01 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 16 Jun 2021 17:47:00 GMT', + 'Content-Length', + '1315' +]); diff --git a/sdk/identity/identity/recordings/node/clientsecretcredential_internal/recording_supports_specifying_the_regional_authority.js b/sdk/identity/identity/recordings/node/clientsecretcredential_internal/recording_supports_specifying_the_regional_authority.js new file mode 100644 index 000000000000..285bdd03c827 --- /dev/null +++ b/sdk/identity/identity/recordings/node/clientsecretcredential_internal/recording_supports_specifying_the_regional_authority.js @@ -0,0 +1,5 @@ +let nock = require('nock'); + +module.exports.hash = "08961c25d03418e2799182c2d0852917"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} diff --git a/sdk/identity/identity/review/identity.api.md b/sdk/identity/identity/review/identity.api.md index 4e92bcea784e..4f2152d01d2a 100644 --- a/sdk/identity/identity/review/identity.api.md +++ b/sdk/identity/identity/review/identity.api.md @@ -92,6 +92,7 @@ export class ClientCertificateCredential implements TokenCredential { // @public export interface ClientCertificateCredentialOptions extends TokenCredentialOptions { + regionalAuthority?: string; sendCertificateChain?: boolean; } @@ -103,6 +104,7 @@ export class ClientSecretCredential implements TokenCredential { // @public export interface ClientSecretCredentialOptions extends TokenCredentialOptions { + regionalAuthority?: string; } // @public @@ -210,6 +212,63 @@ export class ManagedIdentityCredential implements TokenCredential { getToken(scopes: string | string[], options?: GetTokenOptions): Promise; } +// @public +export enum RegionalAuthority { + AsiaEast = "eastasia", + AsiaSouthEast = "southeastasia", + AustraliaCentral = "australiacentral", + AustraliaCentral2 = "australiacentral2", + AustraliaEast = "australiaeast", + AustraliaSouthEast = "australiasoutheast", + AutoDiscoverRegion = "AutoDiscoverRegion", + BrazilSouth = "brazilsouth", + CanadaCentral = "canadacentral", + CanadaEast = "canadaeast", + ChinaEast = "chinaeast", + ChinaEast2 = "chinaeast2", + ChinaNorth = "chinanorth", + ChinaNorth2 = "chinanorth2", + EuropeNorth = "northeurope", + EuropeWest = "westeurope", + FranceCentral = "francecentral", + FranceSouth = "francesouth", + GermanyCentral = "germanycentral", + GermanyNorth = "germanynorth", + GermanyNorthEast = "germanynortheast", + GermanyWestCentral = "germanywestcentral", + GovernmentUSArizona = "usgovarizona", + GovernmentUSDodCentral = "usdodcentral", + GovernmentUSDodEast = "usdodeast", + GovernmentUSIowa = "usgoviowa", + GovernmentUSTexas = "usgovtexas", + GovernmentUSVirginia = "usgovvirginia", + IndiaCentral = "centralindia", + IndiaSouth = "southindia", + IndiaWest = "westindia", + JapanEast = "japaneast", + JapanWest = "japanwest", + KoreaCentral = "koreacentral", + KoreaSouth = "koreasouth", + NorwayEast = "norwayeast", + NorwayWest = "norwaywest", + SouthAfricaNorth = "southafricanorth", + SouthAfricaWest = "southafricawest", + SwitzerlandNorth = "switzerlandnorth", + SwitzerlandWest = "switzerlandwest", + UAECentral = "uaecentral", + UAENorth = "uaenorth", + UKSouth = "uksouth", + UKWest = "ukwest", + USCentral = "centralus", + USEast = "eastus", + USEast2 = "eastus2", + USNorthCentral = "northcentralus", + USSouthCentral = "southcentralus", + USWest = "westus", + USWest2 = "westus2", + USWestCentral = "westcentralus" +} + // @public export function serializeAuthenticationRecord(record: AuthenticationRecord): string; diff --git a/sdk/identity/identity/src/credentials/clientCertificateCredentialOptions.ts b/sdk/identity/identity/src/credentials/clientCertificateCredentialOptions.ts index 41f02fea246c..74c149a2f6dd 100644 --- a/sdk/identity/identity/src/credentials/clientCertificateCredentialOptions.ts +++ b/sdk/identity/identity/src/credentials/clientCertificateCredentialOptions.ts @@ -12,4 +12,10 @@ export interface ClientCertificateCredentialOptions extends TokenCredentialOptio * Set this option to send base64 encoded public certificate in the client assertion header as an x5c claim */ sendCertificateChain?: boolean; + /** + * Specifies a regional authority. Please refer to the {@link RegionalAuthority} type for the accepted values. + * If {@link RegionalAuthority.AutoDiscoverRegion} is specified, we will try to discover the regional authority endpoint. + * If the property is not specified, the credential uses the global authority endpoint. + */ + regionalAuthority?: string; } diff --git a/sdk/identity/identity/src/credentials/clientSecretCredentialOptions.ts b/sdk/identity/identity/src/credentials/clientSecretCredentialOptions.ts index 05c9f9542220..83a4694abb4b 100644 --- a/sdk/identity/identity/src/credentials/clientSecretCredentialOptions.ts +++ b/sdk/identity/identity/src/credentials/clientSecretCredentialOptions.ts @@ -6,4 +6,11 @@ import { TokenCredentialOptions } from "../client/identityClient"; /** * Optional parameters for the {@link ClientSecretCredential} class. */ -export interface ClientSecretCredentialOptions extends TokenCredentialOptions {} +export interface ClientSecretCredentialOptions extends TokenCredentialOptions { + /** + * Specifies a regional authority. Please refer to the {@link RegionalAuthority} type for the accepted values. + * If {@link RegionalAuthority.AutoDiscoverRegion} is specified, we will try to discover the regional authority endpoint. + * If the property is not specified, the credential uses the global authority endpoint. + */ + regionalAuthority?: string; +} diff --git a/sdk/identity/identity/src/index.ts b/sdk/identity/identity/src/index.ts index ec431a1a5d42..7e951d958cb3 100644 --- a/sdk/identity/identity/src/index.ts +++ b/sdk/identity/identity/src/index.ts @@ -8,6 +8,7 @@ export { AuthenticationRecord } from "./msal/types"; export { AuthenticationRequiredError } from "./msal/errors"; export { serializeAuthenticationRecord, deserializeAuthenticationRecord } from "./msal/utils"; export { TokenCredentialOptions } from "./client/identityClient"; +export { RegionalAuthority } from "./regionalAuthority"; export { InteractiveCredentialOptions } from "./credentials/interactiveCredentialOptions"; export { ChainedTokenCredential } from "./credentials/chainedTokenCredential"; diff --git a/sdk/identity/identity/src/msal/nodeFlows/msalClientCertificate.ts b/sdk/identity/identity/src/msal/nodeFlows/msalClientCertificate.ts index 7fe53880c9d9..b70aca7356e7 100644 --- a/sdk/identity/identity/src/msal/nodeFlows/msalClientCertificate.ts +++ b/sdk/identity/identity/src/msal/nodeFlows/msalClientCertificate.ts @@ -106,7 +106,8 @@ export class MsalClientCertificate extends MsalNode { try { const result = await this.confidentialApp!.acquireTokenByClientCredential({ scopes, - correlationId: options.correlationId + correlationId: options.correlationId, + azureRegion: this.azureRegion }); // Even though we're providing the same default in memory persistence cache that we use for DeviceCodeCredential, // The Client Credential flow does not return the account information from the authentication service, diff --git a/sdk/identity/identity/src/msal/nodeFlows/msalClientSecret.ts b/sdk/identity/identity/src/msal/nodeFlows/msalClientSecret.ts index b58c78fd53d4..e40ba09db500 100644 --- a/sdk/identity/identity/src/msal/nodeFlows/msalClientSecret.ts +++ b/sdk/identity/identity/src/msal/nodeFlows/msalClientSecret.ts @@ -31,7 +31,8 @@ export class MsalClientSecret extends MsalNode { try { const result = await this.confidentialApp!.acquireTokenByClientCredential({ scopes, - correlationId: options.correlationId + correlationId: options.correlationId, + azureRegion: this.azureRegion }); // The Client Credential flow does not return an account, // so each time getToken gets called, we will have to acquire a new token through the service. diff --git a/sdk/identity/identity/src/msal/nodeFlows/nodeCommon.ts b/sdk/identity/identity/src/msal/nodeFlows/nodeCommon.ts index 62705e1b34a3..d0f90b587c3b 100644 --- a/sdk/identity/identity/src/msal/nodeFlows/nodeCommon.ts +++ b/sdk/identity/identity/src/msal/nodeFlows/nodeCommon.ts @@ -20,6 +20,7 @@ import { msalToPublic, publicToMsal } from "../utils"; +import { RegionalAuthority } from "../../regionalAuthority"; /** * Union of the constructor parameters that all MSAL flow types for Node. @@ -27,6 +28,12 @@ import { */ export interface MsalNodeOptions extends MsalFlowOptions { tokenCredentialOptions: TokenCredentialOptions; + /** + * Specifies a regional authority. Please refer to the {@link RegionalAuthority} type for the accepted values. + * If {@link RegionalAuthority.AutoDiscoverRegion} is specified, we will try to discover the regional authority endpoint. + * If the property is not specified, uses a non-regional authority endpoint. + */ + regionalAuthority?: string; } /** @@ -45,11 +52,16 @@ export abstract class MsalNode extends MsalBaseUtilities implements MsalFlow { protected clientId: string; protected identityClient?: IdentityClient; protected requiresConfidential: boolean = false; + protected azureRegion?: string; constructor(options: MsalNodeOptions) { super(options); this.msalConfig = this.defaultNodeMsalConfig(options); this.clientId = this.msalConfig.auth.clientId; + this.azureRegion = options.regionalAuthority ?? process.env.AZURE_REGIONAL_AUTHORITY_NAME; + if (this.azureRegion === RegionalAuthority.AutoDiscoverRegion) { + this.azureRegion = "AUTO_DISCOVER"; + } } /** diff --git a/sdk/identity/identity/src/regionalAuthority.ts b/sdk/identity/identity/src/regionalAuthority.ts new file mode 100644 index 000000000000..e60e2fc7325a --- /dev/null +++ b/sdk/identity/identity/src/regionalAuthority.ts @@ -0,0 +1,114 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +/** + * Helps specify a regional authority, or "AutoDiscoverRegion" to auto-detect the region. + */ +export enum RegionalAuthority { + /** Instructs MSAL to attempt to discover the region */ + AutoDiscoverRegion = "AutoDiscoverRegion", + /** Uses the {@link RegionalAuthority} for the Azure 'westus' region. */ + USWest = "westus", + /** Uses the {@link RegionalAuthority} for the Azure 'westus2' region. */ + USWest2 = "westus2", + /** Uses the {@link RegionalAuthority} for the Azure 'centralus' region. */ + USCentral = "centralus", + /** Uses the {@link RegionalAuthority} for the Azure 'eastus' region. */ + USEast = "eastus", + /** Uses the {@link RegionalAuthority} for the Azure 'eastus2' region. */ + USEast2 = "eastus2", + /** Uses the {@link RegionalAuthority} for the Azure 'northcentralus' region. */ + USNorthCentral = "northcentralus", + /** Uses the {@link RegionalAuthority} for the Azure 'southcentralus' region. */ + USSouthCentral = "southcentralus", + /** Uses the {@link RegionalAuthority} for the Azure 'westcentralus' region. */ + USWestCentral = "westcentralus", + /** Uses the {@link RegionalAuthority} for the Azure 'canadacentral' region. */ + CanadaCentral = "canadacentral", + /** Uses the {@link RegionalAuthority} for the Azure 'canadaeast' region. */ + CanadaEast = "canadaeast", + /** Uses the {@link RegionalAuthority} for the Azure 'brazilsouth' region. */ + BrazilSouth = "brazilsouth", + /** Uses the {@link RegionalAuthority} for the Azure 'northeurope' region. */ + EuropeNorth = "northeurope", + /** Uses the {@link RegionalAuthority} for the Azure 'westeurope' region. */ + EuropeWest = "westeurope", + /** Uses the {@link RegionalAuthority} for the Azure 'uksouth' region. */ + UKSouth = "uksouth", + /** Uses the {@link RegionalAuthority} for the Azure 'ukwest' region. */ + UKWest = "ukwest", + /** Uses the {@link RegionalAuthority} for the Azure 'francecentral' region. */ + FranceCentral = "francecentral", + /** Uses the {@link RegionalAuthority} for the Azure 'francesouth' region. */ + FranceSouth = "francesouth", + /** Uses the {@link RegionalAuthority} for the Azure 'switzerlandnorth' region. */ + SwitzerlandNorth = "switzerlandnorth", + /** Uses the {@link RegionalAuthority} for the Azure 'switzerlandwest' region. */ + SwitzerlandWest = "switzerlandwest", + /** Uses the {@link RegionalAuthority} for the Azure 'germanynorth' region. */ + GermanyNorth = "germanynorth", + /** Uses the {@link RegionalAuthority} for the Azure 'germanywestcentral' region. */ + GermanyWestCentral = "germanywestcentral", + /** Uses the {@link RegionalAuthority} for the Azure 'norwaywest' region. */ + NorwayWest = "norwaywest", + /** Uses the {@link RegionalAuthority} for the Azure 'norwayeast' region. */ + NorwayEast = "norwayeast", + /** Uses the {@link RegionalAuthority} for the Azure 'eastasia' region. */ + AsiaEast = "eastasia", + /** Uses the {@link RegionalAuthority} for the Azure 'southeastasia' region. */ + AsiaSouthEast = "southeastasia", + /** Uses the {@link RegionalAuthority} for the Azure 'japaneast' region. */ + JapanEast = "japaneast", + /** Uses the {@link RegionalAuthority} for the Azure 'japanwest' region. */ + JapanWest = "japanwest", + /** Uses the {@link RegionalAuthority} for the Azure 'australiaeast' region. */ + AustraliaEast = "australiaeast", + /** Uses the {@link RegionalAuthority} for the Azure 'australiasoutheast' region. */ + AustraliaSouthEast = "australiasoutheast", + /** Uses the {@link RegionalAuthority} for the Azure 'australiacentral' region. */ + AustraliaCentral = "australiacentral", + /** Uses the {@link RegionalAuthority} for the Azure 'australiacentral2' region. */ + AustraliaCentral2 = "australiacentral2", + /** Uses the {@link RegionalAuthority} for the Azure 'centralindia' region. */ + IndiaCentral = "centralindia", + /** Uses the {@link RegionalAuthority} for the Azure 'southindia' region. */ + IndiaSouth = "southindia", + /** Uses the {@link RegionalAuthority} for the Azure 'westindia' region. */ + IndiaWest = "westindia", + /** Uses the {@link RegionalAuthority} for the Azure 'koreasouth' region. */ + KoreaSouth = "koreasouth", + /** Uses the {@link RegionalAuthority} for the Azure 'koreacentral' region. */ + KoreaCentral = "koreacentral", + /** Uses the {@link RegionalAuthority} for the Azure 'uaecentral' region. */ + UAECentral = "uaecentral", + /** Uses the {@link RegionalAuthority} for the Azure 'uaenorth' region. */ + UAENorth = "uaenorth", + /** Uses the {@link RegionalAuthority} for the Azure 'southafricanorth' region. */ + SouthAfricaNorth = "southafricanorth", + /** Uses the {@link RegionalAuthority} for the Azure 'southafricawest' region. */ + SouthAfricaWest = "southafricawest", + /** Uses the {@link RegionalAuthority} for the Azure 'chinanorth' region. */ + ChinaNorth = "chinanorth", + /** Uses the {@link RegionalAuthority} for the Azure 'chinaeast' region. */ + ChinaEast = "chinaeast", + /** Uses the {@link RegionalAuthority} for the Azure 'chinanorth2' region. */ + ChinaNorth2 = "chinanorth2", + /** Uses the {@link RegionalAuthority} for the Azure 'chinaeast2' region. */ + ChinaEast2 = "chinaeast2", + /** Uses the {@link RegionalAuthority} for the Azure 'germanycentral' region. */ + GermanyCentral = "germanycentral", + /** Uses the {@link RegionalAuthority} for the Azure 'germanynortheast' region. */ + GermanyNorthEast = "germanynortheast", + /** Uses the {@link RegionalAuthority} for the Azure 'usgovvirginia' region. */ + GovernmentUSVirginia = "usgovvirginia", + /** Uses the {@link RegionalAuthority} for the Azure 'usgoviowa' region. */ + GovernmentUSIowa = "usgoviowa", + /** Uses the {@link RegionalAuthority} for the Azure 'usgovarizona' region. */ + GovernmentUSArizona = "usgovarizona", + /** Uses the {@link RegionalAuthority} for the Azure 'usgovtexas' region. */ + GovernmentUSTexas = "usgovtexas", + /** Uses the {@link RegionalAuthority} for the Azure 'usdodeast' region. */ + GovernmentUSDodEast = "usdodeast", + /** Uses the {@link RegionalAuthority} for the Azure 'usdodcentral' region. */ + GovernmentUSDodCentral = "usdodcentral" +} diff --git a/sdk/identity/identity/test/internal/node/clientCertificateCredential.spec.ts b/sdk/identity/identity/test/internal/node/clientCertificateCredential.spec.ts index 83fa30a44264..7a9a1dc8d7ae 100644 --- a/sdk/identity/identity/test/internal/node/clientCertificateCredential.spec.ts +++ b/sdk/identity/identity/test/internal/node/clientCertificateCredential.spec.ts @@ -6,9 +6,10 @@ import Sinon from "sinon"; import assert from "assert"; import * as path from "path"; -import { env, isPlaybackMode } from "@azure/test-utils-recorder"; +import { AbortController } from "@azure/abort-controller"; +import { env, isPlaybackMode, delay } from "@azure/test-utils-recorder"; import { ConfidentialClientApplication } from "@azure/msal-node"; -import { ClientCertificateCredential } from "../../../src"; +import { ClientCertificateCredential, RegionalAuthority } from "../../../src"; import { MsalTestCleanup, msalNodeTestSetup } from "../../msalTestUtils"; import { MsalNode } from "../../../src/msal/nodeFlows/nodeCommon"; import { Context } from "mocha"; @@ -75,4 +76,30 @@ describe("ClientCertificateCredential (internal)", function() { // so each time getToken gets called, we will have to acquire a new token through the service. assert.equal(doGetTokenSpy.callCount, 2); }); + + it("supports specifying the regional authority", async function() { + const credential = new ClientCertificateCredential( + env.AZURE_TENANT_ID, + env.AZURE_CLIENT_ID, + certificatePath, + { + regionalAuthority: RegionalAuthority.AutoDiscoverRegion + } + ); + + // We'll abort since we only want to ensure the parameters are sent apporpriately. + const controller = new AbortController(); + const getTokenPromise = credential.getToken(scope, { + abortSignal: controller.signal + }); + await delay(5); + controller.abort(); + try { + await getTokenPromise; + } catch (e) { + // Nothing to do here. + } + + assert.equal(doGetTokenSpy.getCall(0).args[0].azureRegion, "AUTO_DISCOVER"); + }); }); diff --git a/sdk/identity/identity/test/internal/node/clientSecretCredential.spec.ts b/sdk/identity/identity/test/internal/node/clientSecretCredential.spec.ts index 0a002b14e849..18cccb957b03 100644 --- a/sdk/identity/identity/test/internal/node/clientSecretCredential.spec.ts +++ b/sdk/identity/identity/test/internal/node/clientSecretCredential.spec.ts @@ -5,9 +5,10 @@ import Sinon from "sinon"; import assert from "assert"; -import { env } from "@azure/test-utils-recorder"; +import { AbortController } from "@azure/abort-controller"; +import { env, delay } from "@azure/test-utils-recorder"; import { ConfidentialClientApplication } from "@azure/msal-node"; -import { ClientSecretCredential } from "../../../src"; +import { ClientSecretCredential, RegionalAuthority } from "../../../src"; import { MsalTestCleanup, msalNodeTestSetup } from "../../msalTestUtils"; import { MsalNode } from "../../../src/msal/nodeFlows/nodeCommon"; import { Context } from "mocha"; @@ -54,4 +55,30 @@ describe("ClientSecretCredential (internal)", function() { // so each time getToken gets called, we will have to acquire a new token through the service. assert.equal(doGetTokenSpy.callCount, 2); }); + + it("supports specifying the regional authority", async function() { + const credential = new ClientSecretCredential( + env.AZURE_TENANT_ID, + env.AZURE_CLIENT_ID, + env.AZURE_CLIENT_SECRET, + { + regionalAuthority: RegionalAuthority.AutoDiscoverRegion + } + ); + + // We'll abort since we only want to ensure the parameters are sent appropriately. + const controller = new AbortController(); + const getTokenPromise = credential.getToken(scope, { + abortSignal: controller.signal + }); + await delay(5); + controller.abort(); + try { + await getTokenPromise; + } catch (e) { + // Nothing to do here. + } + + assert.equal(doGetTokenSpy.getCall(0).args[0].azureRegion, "AUTO_DISCOVER"); + }); }); diff --git a/sdk/identity/identity/test/public/node/clientSecretCredential.spec.ts b/sdk/identity/identity/test/public/node/clientSecretCredential.spec.ts index fc3e58cc8cfe..46d088e86898 100644 --- a/sdk/identity/identity/test/public/node/clientSecretCredential.spec.ts +++ b/sdk/identity/identity/test/public/node/clientSecretCredential.spec.ts @@ -4,10 +4,10 @@ /* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */ import assert from "assert"; -import { env, delay } from "@azure/test-utils-recorder"; +import { env, delay, isLiveMode } from "@azure/test-utils-recorder"; import { AbortController } from "@azure/abort-controller"; import { MsalTestCleanup, msalNodeTestSetup, testTracing } from "../../msalTestUtils"; -import { ClientSecretCredential } from "../../../src"; +import { ClientSecretCredential, RegionalAuthority } from "../../../src"; import { Context } from "mocha"; describe("ClientSecretCredential", function() { @@ -81,4 +81,23 @@ describe("ClientSecretCredential", function() { ] }) ); + + it("supports specifying the regional authority", async function(this: Context) { + if (isLiveMode()) { + this.skip(); + } + + const credential = new ClientSecretCredential( + env.AZURE_TENANT_ID, + env.AZURE_CLIENT_ID, + env.AZURE_CLIENT_SECRET, + { + regionalAuthority: RegionalAuthority.AutoDiscoverRegion + } + ); + + const token = await credential.getToken(scope); + assert.ok(token?.token); + assert.ok(token?.expiresOnTimestamp! > Date.now()); + }); });