Skip to content

Commit c0f6127

Browse files
authored
[Identity] InteractiveBrowserCredential for browser requires a client ID (Azure#13844)
* wip * missed InteractiveBrowserCredentialCommonOptions on the API review file * better explained first paragraph
1 parent a7dd95e commit c0f6127

File tree

8 files changed

+122
-36
lines changed

8 files changed

+122
-36
lines changed

sdk/identity/identity/CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44

55
## 1.2.4-beta.2 (Unreleased)
66

7-
- Breaking change: `DefaultAzureCredential` now only uses `InteractiveBrowserCredential` in the browser since it's the only credential intended to be used to authenticate within browsers.
7+
- `DefaultAzureCredential`'s implementation for browsers was simplified to throw a simple error instead of trying credentials that were already not supported for the browser.
8+
- Breaking Change: `InteractiveBrowserCredential` for the browser now requires the client ID to be provided.
9+
- Documentation was added to elaborate on how to configure an AAD application to support `InteractiveBrowserCredential`.
810

911
## 1.2.4-beta.1 (2021-02-12)
1012

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,60 @@
11
# Interactive Browser Credential
22

3-
The `InteractiveBrowserCredential` uses [Auth Code Flow][AuthCodeFlow], which uses [Proof Key for Code Exchange (PKCE)](https://tools.ietf.org/html/rfc7636), uses [MSAL v2.x](https://github.com/AzureAD/microsoft-authentication-library-for-js), and is enabled by default. It also allows switching back to the [Implicit Grant Flow][ImplicitGrantFlow] by passing the `flow` property with the value `implicit-grant` through to the constructor of the `InteractiveBrowserCredential`.
3+
The `InteractiveBrowserCredential` uses [Authorization Code Flow][AuthCodeFlow], which uses [Proof Key for Code Exchange (PKCE)](https://tools.ietf.org/html/rfc7636) both on the browser and on NodeJS. Under the hood it uses [@azure/msal-node](https://www.npmjs.com/package/@azure/msal-node) for NodeJS. For the browser it uses [MSAL v2.x](https://github.com/AzureAD/microsoft-authentication-library-for-js) by default (which also uses Authorization Code Flow), while it also allows switching back to the older [Implicit Grant Flow][ImplicitGrantFlow] by passing the `flow` property with the value `implicit-grant` through to the constructor of the `InteractiveBrowserCredential`, as follows:
4+
5+
```ts
6+
const credential = new InteractiveBrowserCredential({
7+
// Authorization Code Flow is recommended and used by default.
8+
// But you can switch batch to the Implicit Grant Flow if you need to:
9+
flow: "implicit-grant",
10+
});
11+
```
412

513
Follow the instructions for [creating your single-page application](https://docs.microsoft.com/azure/active-directory/develop/scenario-spa-app-registration#redirect-uri-msaljs-20-with-auth-code-flow) to correctly mark your redirect URI as enabled for CORS.
614

7-
If you attempt to use the authorization code flow and see this error:
15+
When using `InteractiveBrowserCredential` on Node, you may specify a `clientId` and `tenantId`, but otherwise we try to authenticate using a public client that's available for all Azure accounts and the default tenant of your account. For Node, this credential uses a web server to fulfill the redirection. This web server tries to use the port `80` by default. A `redirectUri` can be provided to determine the proper redirection URI with the adequate port, as follows:
16+
17+
```ts
18+
const credential = new InteractiveBrowserCredential({
19+
// You may provide a client ID if you have an application configured.
20+
clientId: "my-client-id",
21+
// You may provide a tenant ID based on the resource you are trying to access.
22+
tenantId: "my-tenant-id",
23+
// You may provide a redirectUri based on the redirectUri configured in your AAD application:
24+
redirectUri: "http://localhost:8080/"
25+
});
26+
```
27+
28+
When using `InteractiveBrowserCredential` on the browser, you will be required to pass a `clientId` in the constructor parameters, such as:
29+
30+
```ts
31+
// If you've bundled Identity for the browser...
32+
33+
const credential = new InteractiveBrowserCredential({
34+
// You MUST provide a client ID if you have an application configured.
35+
clientId: "my-client-id",
36+
// You may provide a tenant ID based on the resource you are trying to access.
37+
tenantId: "my-tenant-id",
38+
// You may provide a redirectUri based on the redirectUri configured in your AAD application:
39+
redirectUri: "http://localhost:8080/"
40+
});
41+
```
42+
43+
Azure Active Directory enterprise applications configured with redirect URIs for `Web` environments are no longer supported by the Authorization Code Flow. You can either configure your AAD application to use Single Page Application redirect URis (type `spa`), or switch to the `implicit-grant` flow.
44+
45+
## CORS error
46+
47+
If you attempt to use the Authorization Code Flow and you get an error similar to this one:
848

949
```
1050
access to XMLHttpRequest at 'https://login.microsoftonline.com/common/v2.0/oauth2/token' from origin 'yourApp.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
1151
```
1252

13-
Then you need to visit your app registration and update the redirect URI for your app to type `spa` (for "single page application").
53+
Then you need to visit your app registration and update the redirect URI you're using to the type `spa` (for "single page application").
54+
55+
## Sample code
1456

15-
You can see a sample project that uses `InteractiveBrowserCredential` with both [Auth Code Flow][AuthCodeFlow] and [Implicit Grant Flow][ImplicitGrantFlow] here: [link to the sample project](https://github.com/Azure/azure-sdk-for-js/tree/master/sdk/identity/identity/test/manual).
57+
You can see a sample project that uses `InteractiveBrowserCredential` with both [Authorization Code Flow][AuthCodeFlow] and [Implicit Grant Flow][ImplicitGrantFlow] here: [link to the sample project](https://github.com/Azure/azure-sdk-for-js/tree/master/sdk/identity/identity/test/manual).
1658

1759
[AuthCodeFlow]: https://docs.microsoft.com/azure/active-directory/develop/v2-oauth2-auth-code-flow
1860
[ImplicitGrantFlow]: https://docs.microsoft.com/azure/active-directory/develop/v2-oauth2-implicit-grant-flow

sdk/identity/identity/review/identity.api.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,9 +155,13 @@ export class InteractiveBrowserCredential implements TokenCredential {
155155
}
156156

157157
// @public
158-
export interface InteractiveBrowserCredentialOptions extends TokenCredentialOptions {
158+
export interface InteractiveBrowserCredentialBrowserOptions extends InteractiveBrowserCredentialCommonOptions {
159+
clientId: string;
160+
}
161+
162+
// @public
163+
export interface InteractiveBrowserCredentialCommonOptions extends TokenCredentialOptions {
159164
authenticationRecord?: AuthenticationRecord;
160-
clientId?: string;
161165
correlationId?: string;
162166
flow?: InteractiveBrowserAuthenticationFlow;
163167
loginStyle?: BrowserLoginStyle;
@@ -166,6 +170,11 @@ export interface InteractiveBrowserCredentialOptions extends TokenCredentialOpti
166170
tenantId?: string;
167171
}
168172

173+
// @public
174+
export interface InteractiveBrowserCredentialOptions extends InteractiveBrowserCredentialCommonOptions {
175+
clientId?: string;
176+
}
177+
169178
// @public
170179
export const logger: AzureLogger;
171180

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,36 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT license.
33

4+
import { AccessToken } from "@azure/core-http";
45
import { TokenCredentialOptions } from "../client/identityClient";
6+
import { credentialLogger, formatError } from "../util/logging";
57
import { ChainedTokenCredential } from "./chainedTokenCredential";
6-
import { InteractiveBrowserCredential } from "./interactiveBrowserCredential";
8+
9+
const BrowserNotSupportedError = new Error(
10+
"DefaultAzureCredential is not supported in the browser. Use InteractiveBrowserCredential instead."
11+
);
12+
const logger = credentialLogger("DefaultAzureCredential");
713

814
/**
915
* Provides a default {@link ChainedTokenCredential} configuration for
10-
* applications that will be deployed to Azure. The following credential
11-
* types will be tried, in order:
12-
*
13-
* - {@link InteractiveBrowserCredential}
16+
* applications that will be deployed to Azure.
1417
*
15-
* Consult the documentation of these credential types for more information
16-
* on how they attempt authentication.
18+
* Only available in NodeJS.
1719
*/
1820
export class DefaultAzureCredential extends ChainedTokenCredential {
1921
/**
2022
* Creates an instance of the DefaultAzureCredential class.
2123
*
2224
* @param options - Options for configuring the client which makes the authentication request.
2325
*/
24-
constructor(tokenCredentialOptions?: TokenCredentialOptions) {
25-
super(new InteractiveBrowserCredential(tokenCredentialOptions));
26+
constructor(_tokenCredentialOptions?: TokenCredentialOptions) {
27+
super();
28+
logger.info(formatError("", BrowserNotSupportedError));
29+
throw BrowserNotSupportedError;
30+
}
31+
32+
public getToken(): Promise<AccessToken | null> {
33+
logger.getToken.info(formatError("", BrowserNotSupportedError));
34+
throw BrowserNotSupportedError;
2635
}
2736
}

sdk/identity/identity/src/credentials/interactiveBrowserCredential.browser.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ import { AccessToken, TokenCredential, GetTokenOptions } from "@azure/core-http"
55
import { IdentityClient } from "../client/identityClient";
66
import {
77
BrowserLoginStyle,
8-
InteractiveBrowserCredentialOptions
8+
InteractiveBrowserCredentialBrowserOptions
99
} from "./interactiveBrowserCredentialOptions";
1010
import { createSpan } from "../util/tracing";
1111
import { CanonicalCode } from "@opentelemetry/api";
12-
import { DefaultTenantId, DeveloperSignOnClientId } from "../constants";
12+
import { DefaultTenantId } from "../constants";
1313
import { credentialLogger, formatSuccess, formatError } from "../util/logging";
1414
import { MSALAuthCode } from "./msalBrowser/msalAuthCode";
1515
import { MSALImplicit } from "./msalBrowser/msalImplicit";
@@ -35,13 +35,17 @@ export class InteractiveBrowserCredential implements TokenCredential {
3535
*
3636
* @param options - Options for configuring the client which makes the authentication request.
3737
*/
38-
constructor(options?: InteractiveBrowserCredentialOptions) {
39-
this.tenantId = (options && options.tenantId) || DefaultTenantId;
38+
constructor(options: InteractiveBrowserCredentialBrowserOptions) {
39+
this.tenantId = options.tenantId || DefaultTenantId;
4040

41-
// TODO: temporary - this is the Azure CLI clientID - we'll replace it when
42-
// Developer Sign On application is available
43-
// https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/identity/Azure.Identity/src/Constants.cs#L9
44-
this.clientId = (options && options.clientId) || DeveloperSignOnClientId;
41+
if (!options?.clientId) {
42+
const error = new Error(
43+
"The parameter `clientId` cannot be left undefined for the `InteractiveBrowserCredential`"
44+
);
45+
logger.info(formatError("", error));
46+
throw error;
47+
}
48+
this.clientId = options.clientId;
4549

4650
options = {
4751
...IdentityClient.getDefaultOptions(),

sdk/identity/identity/src/credentials/interactiveBrowserCredential.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,16 @@ export class InteractiveBrowserCredential implements TokenCredential {
2828
private port: number;
2929
private msalClient: MsalClient;
3030

31-
constructor(options?: InteractiveBrowserCredentialOptions) {
32-
const tenantId = (options && options.tenantId) || DefaultTenantId;
33-
const clientId = (options && options.clientId) || DeveloperSignOnClientId;
31+
constructor(options: InteractiveBrowserCredentialOptions = {}) {
32+
const tenantId = options.tenantId || DefaultTenantId;
33+
const clientId = options.clientId || DeveloperSignOnClientId;
3434

3535
checkTenantId(logger, tenantId);
3636

3737
// const persistenceEnabled = options?.persistenceEnabled ? options?.persistenceEnabled : false;
3838
// const authenticationRecord = options?.authenticationRecord;
3939

40-
if (options && options.redirectUri) {
40+
if (options.redirectUri) {
4141
if (typeof options.redirectUri === "string") {
4242
this.redirectUri = options.redirectUri;
4343
} else {
@@ -54,7 +54,7 @@ export class InteractiveBrowserCredential implements TokenCredential {
5454
}
5555

5656
let authorityHost;
57-
if (options && options.authorityHost) {
57+
if (options.authorityHost) {
5858
if (options.authorityHost.endsWith("/")) {
5959
authorityHost = options.authorityHost + tenantId;
6060
} else {

sdk/identity/identity/src/credentials/interactiveBrowserCredentialOptions.ts

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ export type BrowserLoginStyle = "redirect" | "popup";
2323
export type InteractiveBrowserAuthenticationFlow = "implicit-grant" | "auth-code";
2424

2525
/**
26-
* Defines options for the InteractiveBrowserCredential class.
26+
* Defines the common options for the InteractiveBrowserCredential class.
2727
*/
28-
export interface InteractiveBrowserCredentialOptions extends TokenCredentialOptions {
28+
export interface InteractiveBrowserCredentialCommonOptions extends TokenCredentialOptions {
2929
/**
3030
* (Only available if used from a browser)
3131
* Specifies whether a redirect or a popup window should be used to
@@ -52,11 +52,6 @@ export interface InteractiveBrowserCredentialOptions extends TokenCredentialOpti
5252
*/
5353
tenantId?: string;
5454

55-
/**
56-
* The client (application) ID of an App Registration in the tenant.
57-
*/
58-
clientId?: string;
59-
6055
/**
6156
* Correlation ID that can be customized to keep track of the browser authentication requests.
6257
*/
@@ -84,6 +79,29 @@ export interface InteractiveBrowserCredentialOptions extends TokenCredentialOpti
8479
flow?: InteractiveBrowserAuthenticationFlow;
8580
}
8681

82+
/**
83+
* Defines options for the InteractiveBrowserCredential class for NodeJS.
84+
*/
85+
export interface InteractiveBrowserCredentialOptions
86+
extends InteractiveBrowserCredentialCommonOptions {
87+
/**
88+
* The client (application) ID of an App Registration in the tenant.
89+
*/
90+
clientId?: string;
91+
}
92+
93+
/**
94+
* Defines options for the InteractiveBrowserCredential class for the browser.
95+
*/
96+
export interface InteractiveBrowserCredentialBrowserOptions
97+
extends InteractiveBrowserCredentialCommonOptions {
98+
/**
99+
* The client (application) ID of an App Registration in the tenant.
100+
* This parameter is required on the browser.
101+
*/
102+
clientId: string;
103+
}
104+
87105
/**
88106
* Optional parameters to the InteractiveBrowserCredential authenticate() method.
89107
*/

sdk/identity/identity/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ export { AzureCliCredential } from "./credentials/azureCliCredential";
2020
export { AuthenticationRecord } from "./client/msalClient";
2121
export {
2222
InteractiveBrowserCredentialOptions,
23+
InteractiveBrowserCredentialBrowserOptions,
24+
InteractiveBrowserCredentialCommonOptions,
2325
BrowserLoginStyle,
2426
InteractiveBrowserAuthenticationFlow
2527
} from "./credentials/interactiveBrowserCredentialOptions";

0 commit comments

Comments
 (0)