Skip to content

Commit

Permalink
Merge branch 'dev' into claims_bugfix
Browse files Browse the repository at this point in the history
  • Loading branch information
Robbie-Microsoft committed Apr 29, 2024
2 parents fe49721 + b2bd5ce commit a1d53eb
Show file tree
Hide file tree
Showing 8 changed files with 138 additions and 23 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "Add getAccount API to IPublicClientApplication (#7019)",
"packageName": "@azure/msal-browser",
"email": "dasau@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "Fix useIsAuthenticated returning incorrect value during useEffect update #7057",
"packageName": "@azure/msal-react",
"email": "kade@hatchedlabs.com",
"dependentChangeType": "patch"
}
1 change: 1 addition & 0 deletions lib/msal-browser/docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const msalConfig = {
postLogoutRedirectUri: "enter_postlogout_uri_here",
navigateToLoginRequestUrl: true,
clientCapabilities: ["CP1"],
protocolMode: "AAD"
},
cache: {
cacheLocation: "sessionStorage",
Expand Down
15 changes: 14 additions & 1 deletion lib/msal-browser/docs/errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ msalInstance.acquireTokenSilent(); // This will also no longer throw this error

## Other

Errors not thrown by MSAL, such as server errors.
Errors not thrown by MSAL, such as server or cache errors.

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

Expand All @@ -418,3 +418,16 @@ This error occurs with MSAL.js v2.x and is due to improper configuration during
> Your Redirect URI is eligible for the Authorization Code Flow with PKCE.
![image](https://user-images.githubusercontent.com/5307810/110390912-922fa380-801b-11eb-9e2b-d7aa88ca0687.png)

### cache_quota_exceeded

**Error messages**:

- Exceeded cache storage capacity

This error occurs when MSAL.js surpasses the allotted storage limit when attempting to save token information in the [configured cache storage](./caching.md#cache-storage). See [here](https://developer.mozilla.org/en-US/docs/Web/API/Storage_API/Storage_quotas_and_eviction_criteria#web_storage) for web storage limits.

**Mitigation**:

1. Make sure the configured cache storage has enough capacity to allow MSAL.js to persist token payload. The amount of cache storage required depends on the number of [cached artifacts](./caching.md#cached-artifacts).
2. Disable [claimsBasedCachingEnabled](./configuration.md#cache-config-options) cache config option. When enabled, it caches access tokens under a key containing the hash of the requested claims. Depending on the MSAL.js API usage, it may result in the vast number of access tokens persisted in the cache storage.
5 changes: 5 additions & 0 deletions lib/msal-browser/src/app/IPublicClientApplication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/

import {
AccountFilter,
AccountInfo,
Logger,
PerformanceCallbackFunction,
Expand Down Expand Up @@ -43,6 +44,7 @@ export interface IPublicClientApplication {
removePerformanceCallback(callbackId: string): boolean;
enableAccountStorageEvents(): void;
disableAccountStorageEvents(): void;
getAccount(accountFilter: AccountFilter): AccountInfo | null;
getAccountByHomeId(homeAccountId: string): AccountInfo | null;
getAccountByLocalId(localId: string): AccountInfo | null;
getAccountByUsername(userName: string): AccountInfo | null;
Expand Down Expand Up @@ -113,6 +115,9 @@ export const stubbedPublicClientApplication: IPublicClientApplication = {
getAllAccounts: () => {
return [];
},
getAccount: () => {
return null;
},
getAccountByHomeId: () => {
return null;
},
Expand Down
12 changes: 4 additions & 8 deletions lib/msal-react/src/hooks/useIsAuthenticated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Licensed under the MIT License.
*/

import { useState, useEffect } from "react";
import { useMemo } from "react";
import { useMsal } from "./useMsal";
import { AccountIdentifiers } from "../types/AccountIdentifiers";
import { AccountInfo, InteractionStatus } from "@azure/msal-browser";
Expand Down Expand Up @@ -32,16 +32,12 @@ function isAuthenticated(
export function useIsAuthenticated(matchAccount?: AccountIdentifiers): boolean {
const { accounts: allAccounts, inProgress } = useMsal();

const [hasAuthenticated, setHasAuthenticated] = useState<boolean>(() => {
const isUserAuthenticated = useMemo(() => {
if (inProgress === InteractionStatus.Startup) {
return false;
}
return isAuthenticated(allAccounts, matchAccount);
});
}, [allAccounts, inProgress, matchAccount]);

useEffect(() => {
setHasAuthenticated(isAuthenticated(allAccounts, matchAccount));
}, [allAccounts, matchAccount]);

return hasAuthenticated;
return isUserAuthenticated;
}
32 changes: 18 additions & 14 deletions lib/msal-react/test/components/MsalAuthenticationTemplate.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -665,7 +665,7 @@ describe("MsalAuthenticationTemplate tests", () => {
expect(acquireTokenPopupSpy).toHaveBeenCalledTimes(1)
);
expect(
screen.queryByText("This text will always display.")
await screen.findByText("This text will always display.")
).toBeInTheDocument();
expect(
screen.queryByText("A user is authenticated!")
Expand Down Expand Up @@ -697,16 +697,18 @@ describe("MsalAuthenticationTemplate tests", () => {
return Promise.resolve();
});

render(
<MsalProvider instance={pca}>
<p>This text will always display.</p>
<MsalAuthenticationTemplate
interactionType={InteractionType.Redirect}
>
<span> A user is authenticated!</span>
</MsalAuthenticationTemplate>
</MsalProvider>
);
await act(async () => {
render(
<MsalProvider instance={pca}>
<p>This text will always display.</p>
<MsalAuthenticationTemplate
interactionType={InteractionType.Redirect}
>
<span> A user is authenticated!</span>
</MsalAuthenticationTemplate>
</MsalProvider>
);
});

await waitFor(() =>
expect(handleRedirectSpy).toHaveBeenCalledTimes(1)
Expand Down Expand Up @@ -765,7 +767,7 @@ describe("MsalAuthenticationTemplate tests", () => {
);
await waitFor(() => expect(ssoSilentSpy).toHaveBeenCalledTimes(1));
expect(
screen.queryByText("This text will always display.")
await screen.findByText("This text will always display.")
).toBeInTheDocument();
expect(
screen.queryByText("A user is authenticated!")
Expand Down Expand Up @@ -863,7 +865,7 @@ describe("MsalAuthenticationTemplate tests", () => {
await waitFor(() =>
expect(acquireTokenSilentSpy).toHaveBeenCalledTimes(1)
);
act(() => {
await act(async () => {
const eventMessage: EventMessage = {
eventType: EventType.ACQUIRE_TOKEN_SUCCESS,
interactionType: InteractionType.Redirect,
Expand Down Expand Up @@ -923,7 +925,9 @@ describe("MsalAuthenticationTemplate tests", () => {
await waitFor(() =>
expect(acquireTokenSilentSpy).toHaveBeenCalledTimes(1)
);
expect(screen.queryByText("Error Occurred")).toBeInTheDocument();
expect(
await screen.findByText("Error Occurred")
).toBeInTheDocument();
expect(
screen.queryByText("This text will always display.")
).toBeInTheDocument();
Expand Down
82 changes: 82 additions & 0 deletions lib/msal-react/test/hooks/useIsAuthenticated.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import React from "react";
import { render, screen, waitFor, act } from "@testing-library/react";
import "@testing-library/jest-dom";
import { Configuration, PublicClientApplication } from "@azure/msal-browser";
import { TEST_CONFIG, testAccount } from "../TestConstants";
import { MsalProvider, useIsAuthenticated, withMsal } from "../../src/index";

describe("useIsAuthenticated tests", () => {
let pca: PublicClientApplication;
const msalConfig: Configuration = {
auth: {
clientId: TEST_CONFIG.MSAL_CLIENT_ID,
},
system: {
allowNativeBroker: false,
},
};

let handleRedirectSpy: jest.SpyInstance;

beforeEach(() => {
pca = new PublicClientApplication(msalConfig);
handleRedirectSpy = jest.spyOn(pca, "handleRedirectPromise");
jest.spyOn(pca, "getAllAccounts").mockImplementation(() => []);
});

afterEach(() => {
jest.clearAllMocks();
});

test("useAuthenticated always returns true if user has an account", async () => {
const invalidAuthStateCallback = jest.fn();

const testComponent = ({ ...props }) => {
const isAuth = useIsAuthenticated();
const accounts = props.msalContext.accounts;

if (accounts.length > 0 && !isAuth) {
invalidAuthStateCallback();
}

return (
<>
<p>This component has been wrapped by msal</p>
{accounts.length === 0 && <p>No accounts</p>}
{accounts.length > 0 && <p>Has accounts</p>}
{isAuth && <p>Is authed</p>}
{!isAuth && <p>Not authed</p>}
</>
);
};

const WrappedComponent = withMsal(testComponent);
const { rerender } = render(
<MsalProvider instance={pca}>
<WrappedComponent />
</MsalProvider>
);

await waitFor(() => expect(handleRedirectSpy).toHaveBeenCalledTimes(1));

expect(await screen.findByText("No accounts")).toBeInTheDocument();
expect(await screen.findByText("Not authed")).toBeInTheDocument();

const pcaWithAccounts = new PublicClientApplication(msalConfig);
jest.spyOn(pcaWithAccounts, "getAllAccounts").mockImplementation(() => [
testAccount,
]);

await act(async () =>
rerender(
<MsalProvider instance={pcaWithAccounts}>
<WrappedComponent />
</MsalProvider>
)
);

expect(await screen.findByText("Has accounts")).toBeInTheDocument();
expect(await screen.findByText("Is authed")).toBeInTheDocument();
expect(invalidAuthStateCallback).toHaveBeenCalledTimes(0);
});
});

0 comments on commit a1d53eb

Please sign in to comment.