microsoft-authentication-library-for-js icon indicating copy to clipboard operation
microsoft-authentication-library-for-js copied to clipboard

Keep Me Signed In Not Working, x-ms-cpim-sso:{Id} Cookie Not Persisting

Open akopplinufpi opened this issue 1 year ago • 5 comments

Core Library

MSAL.js (@azure/msal-browser)

Core Library Version

^3.4.0

Wrapper Library

MSAL React (@azure/msal-react)

Wrapper Library Version

^2.0.7

Public or Confidential Client?

Public

Description

We are experiencing an issue where the user selects "Keep Me Signed In" (KMSI) when logging in. But the user is unable to get new tokens after 24 hours without interaction (acquiretokensilent VS acquiretokenredirect).

In the Microsoft documentation it is stated that the cookie used to persist the KMSI setting is called x-ms-cpim-sso:{Id}

image

source: https://learn.microsoft.com/en-us/azure/active-directory-b2c/cookie-definitions

I verified that the token is being successfully stored in the browser:

image

The expiration date on the cookie is 90 days from today. However, when I close the browser and re-open, the cookie is no longer there. The fact that the cookie is getting deleted therefore is breaking the KMSI functionality. I have tested this in both Chrome and Edge.

Here are the SPA redirect URI's that we are using the in the app

image

We are using user flow

image

We have keep me signed in selected

image

Error Message

No response

MSAL Logs

main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - useAccount - Updating account main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - useAccount - Updating account main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - Emitting event: msal:initializeStart main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - Emitting event: msal:initializeEnd main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - Emitting event: msal:handleRedirectStart main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - MsalProvider - msal:handleRedirectStart results in setting inProgress from startup to handleRedirect main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - useAccount - Updating account main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - useAccount - Updating account jobs:1 GET https://(my-domain.com)/favicons/site.webmanifest 404 (Not Found) site.webmanifest:1 Manifest: Line: 1, column: 1, Syntax error. main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - BrowserCacheManager: addTokenKey - idToken added to map main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - BrowserCacheManager: addTokenKey - refreshToken added to map main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - CacheManager:getIdToken - Returning id token main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - Emitting event: msal:loginSuccess main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - Emitting event: msal:handleRedirectEnd main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - CacheManager:getIdToken - Returning id token main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - MsalProvider - msal:handleRedirectEnd results in setting inProgress from handleRedirect to none main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - CacheManager:getIdToken - Returning id token main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - CacheManager:getIdToken - Returning id token main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - useAccount - Updating account main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - CacheManager:getIdToken - Returning id token main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - useAccount - Updating account main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - useAccount - Updating account main.be3b1d23.js:2 GET https://(my-domain.com)/api/jobs/jobdetails 401 (Unauthorized) (anonymous) @ main.be3b1d23.js:2 (anonymous) @ main.be3b1d23.js:2 xhr @ main.be3b1d23.js:2 HI @ main.be3b1d23.js:2 Promise.then (async) request @ main.be3b1d23.js:2 QT.forEach.YI. @ main.be3b1d23.js:2 (anonymous) @ main.be3b1d23.js:2 makeGetRequest @ main.be3b1d23.js:2 getAPIResponseAsync @ main.be3b1d23.js:2 loadJobData @ main.be3b1d23.js:2 loadJobDataAsync @ main.be3b1d23.js:2 (anonymous) @ main.be3b1d23.js:2 ol @ main.be3b1d23.js:2 _c @ main.be3b1d23.js:2 cc @ main.be3b1d23.js:2 Wo @ main.be3b1d23.js:2 (anonymous) @ main.be3b1d23.js:2 Cc @ main.be3b1d23.js:2 ic @ main.be3b1d23.js:2 C @ main.be3b1d23.js:2 I @ main.be3b1d23.js:2 main.be3b1d23.js:2 Unauthorized, trying to get token and retrying request. Route: jobs/jobDetails main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - CacheManager:getIdToken - Returning id token main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - Emitting event: msal:acquireTokenStart main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - CacheManager:getIdToken - Returning id token main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - CacheManager:getAccessToken - No token found main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - CacheManager:getRefreshToken - returning refresh token main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [3e0333b9-cdf7-4819-992f-b09dafa3560f] : @azure/[email protected] : Info - Token refresh is required due to cache outcome: 2 main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - Emitting event: msal:acquireTokenFromNetworkStart main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - CacheManager:getRefreshToken - returning refresh token main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - CacheManager:getIdToken - Returning id token main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - CacheManager:getIdToken - Returning id token main.be3b1d23.js:2 GET https://(my-domain.com)/api/user/userrole 401 (Unauthorized) (anonymous) @ main.be3b1d23.js:2 (anonymous) @ main.be3b1d23.js:2 xhr @ main.be3b1d23.js:2 HI @ main.be3b1d23.js:2 Promise.then (async) request @ main.be3b1d23.js:2 QT.forEach.YI. @ main.be3b1d23.js:2 (anonymous) @ main.be3b1d23.js:2 makeGetRequest @ main.be3b1d23.js:2 getAPIResponseAsync @ main.be3b1d23.js:2 (anonymous) @ main.be3b1d23.js:2 (anonymous) @ main.be3b1d23.js:2 ol @ main.be3b1d23.js:2 _c @ main.be3b1d23.js:2 cc @ main.be3b1d23.js:2 Wo @ main.be3b1d23.js:2 (anonymous) @ main.be3b1d23.js:2 Cc @ main.be3b1d23.js:2 ic @ main.be3b1d23.js:2 C @ main.be3b1d23.js:2 I @ main.be3b1d23.js:2 main.be3b1d23.js:2 Unauthorized, trying to get token and retrying request. Route: User/UserRole main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - CacheManager:getIdToken - Returning id token main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - BrowserCacheManager: addTokenKey - accessToken added to map main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - Emitting event: msal:acquireTokenSuccess main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - CacheManager:getIdToken - Returning id token main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - CacheManager:getIdToken - Returning id token main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - CacheManager:getIdToken - Returning id token main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:52 GMT] : [] : @azure/[email protected] : Info - useAccount - Updating account main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:55 GMT] : [] : @azure/[email protected] : Info - CacheManager:getIdToken - Returning id token main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:55 GMT] : [] : @azure/[email protected] : Info - CacheManager:getIdToken - Returning id token main.be3b1d23.js:2 [Tue, 03 Sep 2024 14:20:55 GMT] : [] : @azure/[email protected] : Info - useAccount - Updating account

Network Trace (Preferrably Fiddler)

  • [X] Sent
  • [ ] Pending

MSAL Configuration

import { LogLevel } from "@azure/msal-browser";
// Browser check variables
// If you support IE, our recommendation is that you sign-in using Redirect APIs
// If you as a developer are testing using Edge InPrivate mode, please add "isEdge" to the if check
const ua = window.navigator.userAgent;
const msie = ua.indexOf("MSIE ");
const msie11 = ua.indexOf("Trident/");
const msedge = ua.indexOf("Edge/");
const firefox = ua.indexOf("Firefox");
const isIE = msie > 0 || msie11 > 0;
const isEdge = msedge > 0;
const isFirefox = firefox > 0; // Only needed if you need to support the redirect flow in Firefox incognito

/**
 * Enter here the user flows and custom policies for your B2C application
 * To learn more about user flows, visit: https://docs.microsoft.com/en-us/azure/active-directory-b2c/user-flow-overview
 * To learn more about custom policies, visit: https://docs.microsoft.com/en-us/azure/active-directory-b2c/custom-policy-overview
 */
export const b2cPolicies = {
	names: {
		signUpSignIn: "B2C_1_{my-b2c-tenant}.SignInSignUp",
		editProfile: "B2C_1_{my-b2c-tenant}.EditProfile"
	},
	authorities: {
		signUpSignIn: {
			authority:
				"https://{my-b2c-tenant}.b2clogin.com/{my-b2c-tenant}.onmicrosoft.com/B2C_1_{my-b2c-tenant}.SignInSignUp"
		},
		editProfile: {
			authority:
				"https://{my-b2c-tenant}.b2clogin.com/{my-b2c-tenant}.onmicrosoft.com/B2C_1_{my-b2c-tenant}.ProfileEditing"
		}
	},
	authorityDomain: "{my-b2c-tenant}.b2clogin.com"
};

// Config object to be passed to Msal on creation
export const msalConfig = {
	auth: {
		clientId: "13c05f42-a185-47f3-a9b4-15cab19dc7b3",
		authority: b2cPolicies.authorities.signUpSignIn.authority,
		knownAuthorities: [b2cPolicies.authorityDomain],
		// redirectUri: window.location.origin + "/jobs",
		postLogoutRedirectUri: "/",
		scopes: [
			"https://{my-b2c-tenant}.onmicrosoft.com/api/TrussTrax.Read",
			"https://{my-b2c-tenant}.onmicrosoft.com/api/TrussTrax.Write"
		]
	},
	cache: {
		cacheLocation: "localStorage",
		storeAuthStateInCookie: isIE || isEdge || isFirefox
	},
	system: {
		loggerOptions: {
			logLevel: LogLevel.Info,
			loggerCallback: (level, message, containsPii) => {
				if (containsPii) {
					return;
				}
				switch (level) {
					case LogLevel.Error:
						console.error(message);
						return;
					case LogLevel.Info:
						console.info(message);
						return;
					case LogLevel.Verbose:
						console.debug(message);
						return;
					case LogLevel.Warning:
						console.warn(message);
						return;
					default:
						return;
				}
			}
		}
	}
};

Relevant Code Snippets

in app.jsx, we wrap our /jobs route in a component we call <RequireAuth>.


<Route
    path="/jobs"
    element={
        <RequireAuth>
	    <PageTracking route="/jobs">
	        <Jobs
		    isLoading={isLoading}
		    jobsStatuses={jobStatuses}
		/>
            </PageTracking>
	</RequireAuth>
    }
/>

here is the relevant code from RequireAuth

return (
	<>
		<MsalAuthenticationTemplate
			interactionType={InteractionType.Redirect}
			authenticationRequest={config.auth.scopes}
			errorComponent={ErrorComponent}
			loadingComponent={LoadingComponent}>
			{role === null ? <CenterSpinner /> : role === "None" ? <Welcome /> : children}
		</MsalAuthenticationTemplate>
	</>
);

Here is the code from our API class that is responsible for getting a valid token and attaching it to network requests. After 24 hours, when a network request is made, the user is redirected to /notauthorized

public getTokenAsync = async (redirectOnUnauthorized: boolean = true) => {
		const activeAccount = this.msalInstance.getActiveAccount();
		const accounts = this.msalInstance.getAllAccounts();

		const request = {
			scopes: this.msalInstance.controller.config.auth.scopes,
			account: activeAccount || accounts[0]
		};

		try {
			// tokens expire every 20 or 30 mins. politely ask MSAL for a new token
			const authResult = await this.msalInstance.acquireTokenSilent(request);
			return authResult.accessToken;
		} catch (error) {
			// if we cant get a token, the user needs to re-authenticate

			if (redirectOnUnauthorized) {
				if (window.location.pathname !== "/notauthorized") {
					window.location.href = "/notauthorized";
				}
			} else {
				throw new Error("User not authorized");
			}

			// TODO this would be nice to get this working
			// const result = await this.msalInstance.acquireTokenRedirect(request);
			// return result.accessToken;
		}
	};

If the users role is null, we we render a spinner, while the user is being redirected to B2C to sign in. You can see the scopes that are used to sign in from the MSAL Configuration above. Once the user comes back from being redirected to B2C, the user gets a token, which is then used to make an API call to get the role. Once the user has their role, they are let in to the app, which is rendered by {children}

Reproduction Steps

If you would like to reproduce this issue in our dev environment, please contact me and I will be able to get you access.

Expected Behavior

The expected behavior is that the cookie x-ms-cpim-sso:{Id} should not get deleted when the browser closes. it should stay in the cookies in the browser and allow the user to stay signed in for up to 90 days. What we are experiencing is that the user is again unable to retrieve a token after 24 hours without interaction.

Identity Provider

Azure B2C Custom Policy

Browsers Affected (Select all that apply)

Chrome, Edge

Regression

No response

Source

External (Customer)

akopplinufpi avatar Sep 03 '24 15:09 akopplinufpi

Update: Ive found that if I shift+F5 refresh, then the cookie re-appears in the browser. However this still does not explain why users are booted out after 24 hours

akopplinufpi avatar Sep 03 '24 19:09 akopplinufpi

@akopplinufpi A SPA RT is only valid for 24 hrs by design in the default case. MSAL JS however does not control the browser cookies or the KMSI cookie presence enabling the server to issue a valid RT silently. Did you check with B2C service if they have an explanation? Support link here.

Please let me know if there is no response and I can try engage the B2C team here.

sameerag avatar Sep 06 '24 22:09 sameerag

Hi @sameerag thank you for taking the time to respond! I went to create the support request, and it looks like the "Technical" issue type is not available. I went ahead and created a request anyways... Can you ping the B2C team to see if they got the request? support request number: 2409090040008015

image

akopplinufpi avatar Sep 09 '24 12:09 akopplinufpi

@akopplinufpi we are having the same issue

jsomdev avatar Sep 16 '24 14:09 jsomdev

@jsomdev unfortunately the response that I have gotten from Microsoft from the ticket that I created has been 1) it should be working as expected; and 2) its probably something you are doing wrong within your own application. However, we are not doing anything to interact with cookies directly - we are using @azure/msal-react and @azure/msal-browser libraries for authentication, so I dont see how it could be something that we are doing within the application.

akopplinufpi avatar Sep 16 '24 14:09 akopplinufpi

Hi @akopplinufpi

Did you happen to learn anything more about this issue? 🤔

Thanks,

Saturn avatar Jan 13 '25 17:01 Saturn

@Saturn no sadly. their support just told us it was our fault.

akopplinufpi avatar Jan 13 '25 18:01 akopplinufpi