Skip to content

Commit

Permalink
Merge branch 'dev' into axios-validate-status
Browse files Browse the repository at this point in the history
  • Loading branch information
jasonnutter authored Feb 3, 2021
2 parents aa09b1c + 03f45fd commit 13d8654
Show file tree
Hide file tree
Showing 15 changed files with 295 additions and 64 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "Fix PCA stub errors (#2963)",
"packageName": "@azure/msal-browser",
"email": "thomas.norling@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "Allow apps to not use the current page as default postLogoutRedirectUri in MSAL Browser (#2789)",
"packageName": "@azure/msal-browser",
"email": "janutter@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "Setting postLogoutRedirectUri as null will disable post logout redirect",
"packageName": "@azure/msal-common",
"email": "janutter@microsoft.com",
"dependentChangeType": "patch"
}
2 changes: 1 addition & 1 deletion lib/msal-browser/docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ const msalInstance = new PublicClientApplication(msalConfig);
| `knownAuthorities` | An array of URIs that are known to be valid. Used in B2C scenarios. | Array of strings in URI format | Empty array `[]` |
| `cloudDiscoveryMetadata` | A string containing the cloud discovery response. Used in AAD scenarios. See performance.md for more info | string | Empty string `""` |
| `redirectUri` | URI where the authorization code response is sent back to. Whatever location is specified here must have the MSAL library available to handle the response. | String in absolute or relative URI format | Login request page (`window.location.href` of page which made auth request) |
| `postLogoutRedirectUri` | URI that is redirected to after a logout() call is made. | String in absolute or relative URI format | Login request page (`window.location.href` of page which made auth request) |
| `postLogoutRedirectUri` | URI that is redirected to after a logout() call is made. | String in absolute or relative URI format. Pass `null` to disable post logout redirect. | Login request page (`window.location.href` of page which made auth request) |
| `navigateToLoginRequestUrl` | If `true`, will navigate back to the original request location before processing the authorization code response. If the `redirectUri` is the same as the original request location, this flag should be set to false. | boolean | `true` |
| `clientCapabilities` | Array of capabilities to be added to all network requests as part of the `xms_cc` claims request | Array of strings | [] |
| `protocolMode` | Enum representing the protocol mode to use. If `"AAD"`, will function on the OIDC-compliant AAD v2 endpoints; if `"OIDC"`, will function on other OIDC-compliant endpoints. | string | `"AAD"` |
Expand Down
71 changes: 68 additions & 3 deletions lib/msal-browser/docs/errors.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,71 @@
# Errors
1. [I get this error "Access to fetch at [url] has been blocked by CORS policy"](#I-get-this-error-Access-to-fetch-at-[url]-has-been-blocked-by-CORS-policy")

## I get this error "Access to fetch at [url] has been blocked by CORS policy"
***

This error occurs with MSAL.js v2.x and is due to improper configuration during **App Registration** on **Azure Portal**. In particular, you should not have both `Web` and `Single-page application` added as a platform under the **Authentication** blade in your App Registration.
**[BrowserConfigurationAuthErrors](#Browserconfigurationautherrors)**

1. [stubbed_public_client_application_called](#stubbed_public_client_application_called)

**[Other](#other)**

1. [Access to fetch at [url] has been blocked by CORS policy](#Access-to-fetch-at-[url]-has-been-blocked-by-CORS-policy)

***

## BrowserConfigurationAuthErrors

### stubbed_public_client_application_called

**Error Message**: Stub instance of Public Client Application was called. If using msal-react, please ensure context is not used without a provider.

When using `msal-react` this error is thrown when you try to use an msal component or hook without an `MsalProvider` higher up in the component tree. All `msal-react` hooks and components make use of the [React Context API](https://reactjs.org/docs/context.html) and require a provider.

❌ The following example will throw this error because the `useMsal` hook is used outside the context of `MsalProvider`:

```javascript
import { useMsal, MsalProvider } from "@azure/msal-react";
import { PublicClientApplication } from "@azure/msal-browser";

const pca = new PublicClientApplication(config);

function App() {
const { accounts } = useMsal();

return (
<MsalProvider instance={pca}>
<YourAppComponent>
</ MsalProvider>
)
}
```

✔️ To resolve the error you should refactor the code above so that the `useMsal` hook is called in a component underneath `MsalProvider`:

```javascript
import { useMsal, MsalProvider } from "@azure/msal-react";
import { PublicClientApplication } from "@azure/msal-browser";

const pca = new PublicClientApplication(config);

function ExampleComponent () {
const { accounts } = useMsal();

return <YourAppComponent />;
};

function App() {
return (
<MsalProvider instance={pca}>
<ExampleComponent />
</ MsalProvider>
)
}
```

## Other

Errors not thrown by msal, such as server errors

### Access to fetch at [url] has been blocked by CORS policy

This error occurs with MSAL.js v2.x and is due to improper configuration during **App Registration** on **Azure Portal**. In particular, you should not have both `Web` and `Single-page application` added as a platform under the **Authentication** blade in your App Registration.
33 changes: 21 additions & 12 deletions lib/msal-browser/src/app/ClientApplication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -720,16 +720,6 @@ export abstract class ClientApplication {
return UrlString.getAbsoluteUrl(redirectUri, BrowserUtils.getCurrentUri());
}

/**
* Use to get the post logout redirect uri configured in MSAL or null.
* @param requestPostLogoutRedirectUri
* @returns Post logout redirect URL
*/
protected getPostLogoutRedirectUri(requestPostLogoutRedirectUri?: string): string {
const postLogoutRedirectUri = requestPostLogoutRedirectUri || this.config.auth.postLogoutRedirectUri || BrowserUtils.getCurrentUri();
return UrlString.getAbsoluteUrl(postLogoutRedirectUri, BrowserUtils.getCurrentUri());
}

/**
* Use to get the redirectStartPage either from request or use current window
* @param requestStartPage
Expand Down Expand Up @@ -992,7 +982,26 @@ export abstract class ClientApplication {
...logoutRequest
};

validLogoutRequest.postLogoutRedirectUri = this.getPostLogoutRedirectUri(logoutRequest ? logoutRequest.postLogoutRedirectUri : "");
/*
* Only set redirect uri if logout request isn't provided or the set uri isn't null.
* Otherwise, use passed uri, config, or current page.
*/
if (!logoutRequest || logoutRequest.postLogoutRedirectUri !== null) {
if (logoutRequest && logoutRequest.postLogoutRedirectUri) {
this.logger.verbose("Setting postLogoutRedirectUri to uri set on logout request");
validLogoutRequest.postLogoutRedirectUri = UrlString.getAbsoluteUrl(logoutRequest.postLogoutRedirectUri, BrowserUtils.getCurrentUri());
} else if (this.config.auth.postLogoutRedirectUri === null) {
this.logger.verbose("postLogoutRedirectUri configured as null and no uri set on request, not passing post logout redirect");
} else if (this.config.auth.postLogoutRedirectUri) {
this.logger.verbose("Setting postLogoutRedirectUri to configured uri");
validLogoutRequest.postLogoutRedirectUri = UrlString.getAbsoluteUrl(this.config.auth.postLogoutRedirectUri, BrowserUtils.getCurrentUri());
} else {
this.logger.verbose("Setting postLogoutRedirectUri to current page");
validLogoutRequest.postLogoutRedirectUri = UrlString.getAbsoluteUrl(BrowserUtils.getCurrentUri(), BrowserUtils.getCurrentUri());
}
} else {
this.logger.verbose("postLogoutRedirectUri passed as null, not settibng post logout redirect uri");
}

return validLogoutRequest;
}
Expand All @@ -1004,7 +1013,7 @@ export abstract class ClientApplication {
* @param payload
* @param error
*/
protected emitEvent(eventType: EventType, interactionType?: InteractionType, payload?: EventPayload, error?: EventError) {
protected emitEvent(eventType: EventType, interactionType?: InteractionType, payload?: EventPayload, error?: EventError): void {
if (this.isBrowserEnvironment) {
const message: EventMessage = {
eventType: eventType,
Expand Down
16 changes: 8 additions & 8 deletions lib/msal-browser/src/app/IPublicClientApplication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@ export interface IPublicClientApplication {

export const stubbedPublicClientApplication: IPublicClientApplication = {
acquireTokenPopup: () => {
return Promise.reject(BrowserConfigurationAuthError.createStubPcaInstanceCalledError);
return Promise.reject(BrowserConfigurationAuthError.createStubPcaInstanceCalledError());
},
acquireTokenRedirect: () => {
return Promise.reject(BrowserConfigurationAuthError.createStubPcaInstanceCalledError);
return Promise.reject(BrowserConfigurationAuthError.createStubPcaInstanceCalledError());
},
acquireTokenSilent: () => {
return Promise.reject(BrowserConfigurationAuthError.createStubPcaInstanceCalledError);
return Promise.reject(BrowserConfigurationAuthError.createStubPcaInstanceCalledError());
},
getAllAccounts: () => {
return [];
Expand All @@ -57,19 +57,19 @@ export const stubbedPublicClientApplication: IPublicClientApplication = {
return null;
},
handleRedirectPromise: () => {
return Promise.reject(BrowserConfigurationAuthError.createStubPcaInstanceCalledError);
return Promise.reject(BrowserConfigurationAuthError.createStubPcaInstanceCalledError());
},
loginPopup: () => {
return Promise.reject(BrowserConfigurationAuthError.createStubPcaInstanceCalledError);
return Promise.reject(BrowserConfigurationAuthError.createStubPcaInstanceCalledError());
},
loginRedirect: () => {
return Promise.reject(BrowserConfigurationAuthError.createStubPcaInstanceCalledError);
return Promise.reject(BrowserConfigurationAuthError.createStubPcaInstanceCalledError());
},
logout: () => {
return Promise.reject(BrowserConfigurationAuthError.createStubPcaInstanceCalledError);
return Promise.reject(BrowserConfigurationAuthError.createStubPcaInstanceCalledError());
},
ssoSilent: () => {
return Promise.reject(BrowserConfigurationAuthError.createStubPcaInstanceCalledError);
return Promise.reject(BrowserConfigurationAuthError.createStubPcaInstanceCalledError());
},
addEventCallback: () => {
return null;
Expand Down
2 changes: 1 addition & 1 deletion lib/msal-browser/src/config/Configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export type BrowserAuthOptions = {
cloudDiscoveryMetadata?: string;
authorityMetadata?: string;
redirectUri?: string;
postLogoutRedirectUri?: string;
postLogoutRedirectUri?: string | null;
navigateToLoginRequestUrl?: boolean;
clientCapabilities?: Array<string>;
protocolMode?: ProtocolMode;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const BrowserConfigurationAuthErrorMessage = {
},
stubPcaInstanceCalled: {
code: "stubbed_public_client_application_called",
desc: "Stub instance of Public Client Application was called. If using msal-react, please ensure context is not used without a provider."
desc: "Stub instance of Public Client Application was called. If using msal-react, please ensure context is not used without a provider. For more visit: aka.ms/msaljs/browser-errors"
},
inMemRedirectUnavailable: {
code: "in_mem_redirect_unavailable",
Expand Down
89 changes: 89 additions & 0 deletions lib/msal-browser/test/app/IPublicClientApplication.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/

import "mocha";
import chai from "chai";
import chaiAsPromised from "chai-as-promised";
import { stubbedPublicClientApplication } from "../../src/app/IPublicClientApplication";
import { BrowserConfigurationAuthErrorMessage } from "../../src/error/BrowserConfigurationAuthError";

chai.use(chaiAsPromised);
const expect = chai.expect;

describe("IPublicClientApplication.ts Class Unit Tests", () => {
describe("stubbedPublicClientApplication tests", () => {
it("acquireTokenPopup throws", (done) => {
stubbedPublicClientApplication.acquireTokenPopup({scopes: ["openid"]}).catch(e => {
expect(e.errorCode).to.eq(BrowserConfigurationAuthErrorMessage.stubPcaInstanceCalled.code);
expect(e.errorMessage).to.eq(BrowserConfigurationAuthErrorMessage.stubPcaInstanceCalled.desc);
done();
});
});

it("acquireTokenRedirect throws", (done) => {
stubbedPublicClientApplication.acquireTokenRedirect({scopes: ["openid"]}).catch(e => {
expect(e.errorCode).to.eq(BrowserConfigurationAuthErrorMessage.stubPcaInstanceCalled.code);
expect(e.errorMessage).to.eq(BrowserConfigurationAuthErrorMessage.stubPcaInstanceCalled.desc);
done();
});
});

it("acquireTokenSilent throws", (done) => {
stubbedPublicClientApplication.acquireTokenSilent({scopes: ["openid"]}).catch(e => {
expect(e.errorCode).to.eq(BrowserConfigurationAuthErrorMessage.stubPcaInstanceCalled.code);
expect(e.errorMessage).to.eq(BrowserConfigurationAuthErrorMessage.stubPcaInstanceCalled.desc);
done();
});
});
it("handleRedirectPromise throws", (done) => {
stubbedPublicClientApplication.handleRedirectPromise().catch(e => {
expect(e.errorCode).to.eq(BrowserConfigurationAuthErrorMessage.stubPcaInstanceCalled.code);
expect(e.errorMessage).to.eq(BrowserConfigurationAuthErrorMessage.stubPcaInstanceCalled.desc);
done();
});
});

it("loginPopup throws", (done) => {
stubbedPublicClientApplication.loginPopup().catch(e => {
expect(e.errorCode).to.eq(BrowserConfigurationAuthErrorMessage.stubPcaInstanceCalled.code);
expect(e.errorMessage).to.eq(BrowserConfigurationAuthErrorMessage.stubPcaInstanceCalled.desc);
done();
});
});

it("loginRedirect throws", (done) => {
stubbedPublicClientApplication.loginRedirect().catch(e => {
expect(e.errorCode).to.eq(BrowserConfigurationAuthErrorMessage.stubPcaInstanceCalled.code);
expect(e.errorMessage).to.eq(BrowserConfigurationAuthErrorMessage.stubPcaInstanceCalled.desc);
done();
});
});

it("logout throws", (done) => {
stubbedPublicClientApplication.logout().catch(e => {
expect(e.errorCode).to.eq(BrowserConfigurationAuthErrorMessage.stubPcaInstanceCalled.code);
expect(e.errorMessage).to.eq(BrowserConfigurationAuthErrorMessage.stubPcaInstanceCalled.desc);
done();
});
});

it("ssoSilent throws", (done) => {
stubbedPublicClientApplication.ssoSilent({}).catch(e => {
expect(e.errorCode).to.eq(BrowserConfigurationAuthErrorMessage.stubPcaInstanceCalled.code);
expect(e.errorMessage).to.eq(BrowserConfigurationAuthErrorMessage.stubPcaInstanceCalled.desc);
done();
});
});

it("getLogger throws", () => {
try {
stubbedPublicClientApplication.getLogger();
} catch (e) {
expect(e.errorCode).to.eq(BrowserConfigurationAuthErrorMessage.stubPcaInstanceCalled.code);
expect(e.errorMessage).to.eq(BrowserConfigurationAuthErrorMessage.stubPcaInstanceCalled.desc);
};
});
});
});
Loading

0 comments on commit 13d8654

Please sign in to comment.