Skip to content

Move nagivateToLoginRequestUrl out of Configuration #7855

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 19 commits into
base: msal-v5
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
48c37cc
Add debug param to 1p-build
hectormmg Jun 5, 2025
99f93ea
Add debug param to 1p-build (#7811)
hectormmg Jun 5, 2025
90eb889
Merge branch 'dev' of https://github.com/AzureAD/microsoft-authentica…
hectormmg Jun 5, 2025
75f6237
Suppress false-positive CodeQL finding in NavigationClient (#7814)
konstantin-msft Jun 6, 2025
fa85c44
[v4] Remove access tokens synchronously (#7822)
tnorling Jun 11, 2025
1eb9ee5
Release PR: official (#7829)
msal-js-release-automation[bot] Jun 11, 2025
97ea591
Bumped axios to latest version in all msal-node samples (#7831)
Robbie-Microsoft Jun 16, 2025
4b956d0
Bumped the dotenv package in all msal-node samples (#7823)
Robbie-Microsoft Jun 16, 2025
aedd762
Client Credential Sample: Moved client secret from code to .env file …
Robbie-Microsoft Jun 16, 2025
14fe619
Added support for DEFAULT_IDENTITY_CLIENT_ID environment variable in …
Robbie-Microsoft Jun 16, 2025
24170c2
Merge branch 'dev' of https://github.com/AzureAD/microsoft-authentica…
hectormmg Jun 17, 2025
5c8521d
Merge branch 'dev' into msal-v5
hectormmg Jun 17, 2025
24eb724
Merge branch 'msal-v5' of https://github.com/AzureAD/microsoft-authen…
hectormmg Jun 19, 2025
d16f8cb
Move navigateToLoginRequestUrl to request config
hectormmg Jun 19, 2025
9192903
Update interface and docs
hectormmg Jun 19, 2025
7fae6d2
Change files
hectormmg Jun 19, 2025
12a247a
Update API review and documentating comments
hectormmg Jun 19, 2025
609284a
Update API review
hectormmg Jun 19, 2025
d81962c
Format fix
hectormmg Jun 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "major",
"comment": "Move navigateToLoginRequestUrl to request config",
"packageName": "@azure/msal-browser",
"email": "[email protected]",
"dependentChangeType": "patch"
}
2 changes: 1 addition & 1 deletion lib/msal-browser/FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ For a full implementation, please refer to the app creation scripts in the [Vani

The redirect flow can be confusing, as redirecting away from the page means you are creating a whole new instance of the application when you return. This means that calling a redirect method cannot return anything. Rather, what happens is that the page is redirected away, you enter your credentials, and you are redirected back to your application with the response in the url hash.

If `navigateToLoginRequestUrl` property in MSAL configuration parameters is set to **true**, you will be redirected again to the page you were on when you called `loginRedirect`, unless that page was also set as your `redirectUri`. On the final page your application must call `handleRedirectPromise()` in order to process the hash and cache tokens in local/session storage.
If `navigateToLoginRequestUrl` property is passed in as an option to `handleRedirectPromise` and set to **true**, you will be redirected again to the page you were on when you called `loginRedirect`, unless that page was also set as your `redirectUri`. On the final page your application must call `handleRedirectPromise()` in order to process the hash and cache tokens in local/session storage.

As this function returns a promise you can call `.then` and `.catch`, similar to `loginPopup`.

Expand Down
13 changes: 9 additions & 4 deletions lib/msal-browser/apiReview/msal-browser.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,6 @@ export type BrowserAuthOptions = {
authorityMetadata?: string;
redirectUri?: string;
postLogoutRedirectUri?: string | null;
navigateToLoginRequestUrl?: boolean;
clientCapabilities?: Array<string>;
OIDCOptions?: OIDCOptions;
azureCloudOptions?: AzureCloudOptions;
Expand Down Expand Up @@ -731,7 +730,9 @@ export interface IController {
// @internal (undocumented)
getPerformanceClient(): IPerformanceClient;
// (undocumented)
handleRedirectPromise(hash?: string): Promise<AuthenticationResult | null>;
handleRedirectPromise(hash?: string, options?: {
navigateToLoginRequestUrl?: boolean;
}): Promise<AuthenticationResult | null>;
// (undocumented)
hydrateCache(result: AuthenticationResult, request: SilentRequest | SsoSilentRequest | RedirectRequest | PopupRequest): Promise<void>;
// (undocumented)
Expand Down Expand Up @@ -1257,7 +1258,10 @@ export class PublicClientApplication implements IPublicClientApplication {
getConfiguration(): BrowserConfiguration;
getLogger(): Logger;
// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
handleRedirectPromise(hash?: string | undefined): Promise<AuthenticationResult | null>;
// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
handleRedirectPromise(hash?: string | undefined, options?: {
navigateToLoginRequestUrl?: boolean;
}): Promise<AuthenticationResult | null>;
// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
hydrateCache(result: AuthenticationResult, request: SilentRequest | SsoSilentRequest | RedirectRequest | PopupRequest): Promise<void>;
// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
Expand Down Expand Up @@ -1382,6 +1386,7 @@ function redirectPreflightCheck(initialized: boolean, config: BrowserConfigurati
export type RedirectRequest = Partial<Omit<CommonAuthorizationUrlRequest, "responseMode" | "scopes" | "earJwk" | "codeChallenge" | "codeChallengeMethod" | "requestedClaimsHash" | "platformBroker">> & {
scopes: Array<string>;
redirectStartPage?: string;
navigateToLoginRequestUrl?: boolean;
};

// Warning: (ae-missing-release-tag) "replaceHash" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
Expand Down Expand Up @@ -1569,7 +1574,7 @@ export type WrapperSKU = (typeof WrapperSKU)[keyof typeof WrapperSKU];
// src/cache/LocalStorage.ts:297:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/LocalStorage.ts:355:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/LocalStorage.ts:386:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/config/Configuration.ts:210:5 - (ae-forgotten-export) The symbol "InternalAuthOptions" needs to be exported by the entry point index.d.ts
// src/config/Configuration.ts:207:5 - (ae-forgotten-export) The symbol "InternalAuthOptions" needs to be exported by the entry point index.d.ts
// src/event/EventHandler.ts:113:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/event/EventHandler.ts:139:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/index.ts:8:12 - (tsdoc-characters-after-block-tag) The token "@azure" looks like a TSDoc tag but contains an invalid character "/"; if it is not a tag, use a backslash to escape the "@"
Expand Down
7 changes: 7 additions & 0 deletions lib/msal-browser/docs/v4-migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ await loadExternalTokens(
1. The `skipAuthorityMetadataCache` parameter has been removed from BrowserAuthOptions in Configuration.
1. The `protocolMode` parameter has been moved to SystemOptions instead of BrowserAuthOptions in Configuration.
1. The `supportsNestedAppAuth` parameter has been removed. Use the `createNestablePublicClientApplication` API for Nested Apps instead. Read more about Nested Apps [here](./initialization.md#nested-app-configuration).
1. The `navigateTologinRequestUrl` parameter has been removed from BrowserAuthOptions in Configuration. Default behavior remains the same (set to true), in order to maintain custom configuration:
1. For APIs that take a `RedirectRequest` object, the type now has a `navigateToLoginRequestUrl` parameter
1. For `handleRedirectPromise`, `navigateToLoginRequestUrl` has to be passed in as an option as follows:

```typescript
pca.handleRedirectPromise(hash, { navigateToLoginRequestUrl: false })
```

### CacheOptions changes

Expand Down
8 changes: 6 additions & 2 deletions lib/msal-browser/src/app/PublicClientApplication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,12 +212,16 @@ export class PublicClientApplication implements IPublicClientApplication {
* has loaded during redirect flows. This should be invoked on all page loads involved in redirect
* auth flows.
* @param hash Hash to process. Defaults to the current value of window.location.hash. Only needs to be provided explicitly if the response to be handled is not contained in the current value.
* @param options Object containing optional configuration for redirect promise handling.
* @returns Token response or null. If the return value is null, then no auth redirect was detected.
*/
handleRedirectPromise(
hash?: string | undefined
hash?: string | undefined,
options?: {
navigateToLoginRequestUrl?: boolean;
}
): Promise<AuthenticationResult | null> {
return this.controller.handleRedirectPromise(hash);
return this.controller.handleRedirectPromise(hash, options);
}

/**
Expand Down
6 changes: 1 addition & 5 deletions lib/msal-browser/src/config/Configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,7 @@ export type BrowserAuthOptions = {
* The redirect URI where the window navigates after a successful logout.
*/
postLogoutRedirectUri?: string | null;
/**
* Boolean indicating whether to navigate to the original request URL after the auth server navigates to the redirect URL.
*/
navigateToLoginRequestUrl?: boolean;

/**
* Array of capabilities which will be added to the claims.access_token.xms_cc request property on every network request.
*/
Expand Down Expand Up @@ -241,7 +238,6 @@ export function buildConfiguration(
redirectUri:
typeof window !== "undefined" ? BrowserUtils.getCurrentUri() : "",
postLogoutRedirectUri: "",
navigateToLoginRequestUrl: true,
clientCapabilities: [],
OIDCOptions: {
responseMode: Constants.ResponseMode.FRAGMENT,
Expand Down
5 changes: 4 additions & 1 deletion lib/msal-browser/src/controllers/IController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,10 @@ export interface IController {

getAllAccounts(accountFilter?: AccountFilter): AccountInfo[];

handleRedirectPromise(hash?: string): Promise<AuthenticationResult | null>;
handleRedirectPromise(
hash?: string,
options?: { navigateToLoginRequestUrl?: boolean }
): Promise<AuthenticationResult | null>;

loginPopup(request?: PopupRequest): Promise<AuthenticationResult>;

Expand Down
21 changes: 17 additions & 4 deletions lib/msal-browser/src/controllers/StandardController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -392,10 +392,14 @@ export class StandardController implements IController {
* has loaded during redirect flows. This should be invoked on all page loads involved in redirect
* auth flows.
* @param hash Hash to process. Defaults to the current value of window.location.hash. Only needs to be provided explicitly if the response to be handled is not contained in the current value.
* @param options Object containing optional configuration for redirect promise handling.
* @returns Token response or null. If the return value is null, then no auth redirect was detected.
*/
async handleRedirectPromise(
hash?: string
hash?: string,
options?: {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we create a type for options?

navigateToLoginRequestUrl?: boolean;
}
): Promise<AuthenticationResult | null> {
this.logger.verbose("handleRedirectPromise called");
// Block token acquisition before initialize has been called
Expand All @@ -410,7 +414,7 @@ export class StandardController implements IController {
const redirectResponseKey = hash || "";
let response = this.redirectResponse.get(redirectResponseKey);
if (typeof response === "undefined") {
response = this.handleRedirectPromiseInternal(hash);
response = this.handleRedirectPromiseInternal(hash, options);
this.redirectResponse.set(redirectResponseKey, response);
this.logger.verbose(
"handleRedirectPromise has been called for the first time, storing the promise"
Expand All @@ -435,7 +439,10 @@ export class StandardController implements IController {
* @returns
*/
private async handleRedirectPromiseInternal(
hash?: string
hash?: string,
options?: {
navigateToLoginRequestUrl?: boolean;
}
): Promise<AuthenticationResult | null> {
if (!this.browserStorage.isInteractionInProgress(true)) {
this.logger.info(
Expand Down Expand Up @@ -518,7 +525,13 @@ export class StandardController implements IController {
this.logger,
this.performanceClient,
rootMeasurement.event.correlationId
)(hash, standardRequest, codeVerifier, rootMeasurement);
)(
hash,
standardRequest,
codeVerifier,
rootMeasurement,
options
);
}
} catch (e) {
this.browserStorage.resetRequestCache();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,8 @@ export class PlatformAuthInteractionClient extends BaseInteractionClient {
);

const nativeRequest = await this.initializeNativeRequest(request);
const navigateToLoginRequestUrl =
request.navigateToLoginRequestUrl ?? true;

try {
await this.platformAuthProvider.sendMessage(nativeRequest);
Expand All @@ -336,7 +338,7 @@ export class PlatformAuthInteractionClient extends BaseInteractionClient {
timeout: this.config.system.redirectNavigationTimeout,
noHistory: false,
};
const redirectUri = this.config.auth.navigateToLoginRequestUrl
const redirectUri = navigateToLoginRequestUrl
? window.location.href
: this.getRedirectUri(request.redirectUri);
rootMeasurement.end({ success: true });
Expand Down
12 changes: 9 additions & 3 deletions lib/msal-browser/src/interaction_client/RedirectClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,12 +288,18 @@ export class RedirectClient extends StandardInteractionClient {
hash: string = "",
request: CommonAuthorizationUrlRequest,
pkceVerifier: string,
parentMeasurement: InProgressPerformanceEvent
parentMeasurement: InProgressPerformanceEvent,
options?: {
navigateToLoginRequestUrl?: boolean;
}
): Promise<AuthenticationResult | null> {
const serverTelemetryManager = this.initializeServerTelemetryManager(
ApiId.handleRedirectPromise
);

const navigateToLoginRequestUrl =
options?.navigateToLoginRequestUrl ?? true;

try {
const [serverParams, responseString] = this.getRedirectResponse(
hash || ""
Expand Down Expand Up @@ -330,7 +336,7 @@ export class RedirectClient extends StandardInteractionClient {

if (
loginRequestUrlNormalized === currentUrlNormalized &&
this.config.auth.navigateToLoginRequestUrl
navigateToLoginRequestUrl
) {
// We are on the page we need to navigate to - handle hash
this.logger.verbose(
Expand All @@ -350,7 +356,7 @@ export class RedirectClient extends StandardInteractionClient {
);

return handleHashResult;
} else if (!this.config.auth.navigateToLoginRequestUrl) {
} else if (!navigateToLoginRequestUrl) {
this.logger.verbose(
"NavigateToLoginRequestUrl set to false, handling response"
);
Expand Down
2 changes: 2 additions & 0 deletions lib/msal-browser/src/request/RedirectRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { CommonAuthorizationUrlRequest } from "@azure/msal-common/browser";
* - claims - In cases where Azure AD tenant admin has enabled conditional access policies, and the policy has not been met, exceptions will contain claims that need to be consented to.
* - nonce - A value included in the request that is returned in the id token. A randomly generated unique value is typically used to mitigate replay attacks.
* - redirectStartPage - The page that should be returned to after loginRedirect or acquireTokenRedirect. This should only be used if this is different from the redirectUri and will default to the page that initiates the request. When the navigateToLoginRequestUrl config option is set to false this parameter will be ignored.
* - navigateToLoginRequestUrl - If true, the browser will navigate to the login request URL after the redirect response is received. If false, the response will be returned to the calling code and the browser will not navigate to the login request URL.
*/
export type RedirectRequest = Partial<
Omit<
Expand All @@ -46,4 +47,5 @@ export type RedirectRequest = Partial<
> & {
scopes: Array<string>;
redirectStartPage?: string;
navigateToLoginRequestUrl?: boolean;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels wrong for this to be part of RedirectRequest, what needs to happen to make this only applicable to `handleRedirectPromise?

};
3 changes: 0 additions & 3 deletions lib/msal-browser/test/config/Configuration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ describe("Configuration.ts Class Unit Tests", () => {
);
expect(emptyConfig.auth.redirectUri).toBeDefined();
expect(emptyConfig.auth.postLogoutRedirectUri).toBe("");
expect(emptyConfig.auth.navigateToLoginRequestUrl).toBe(true);
expect(emptyConfig.auth?.azureCloudOptions?.azureCloudInstance).toBe(
AzureCloudInstance.None
);
Expand Down Expand Up @@ -234,7 +233,6 @@ describe("Configuration.ts Class Unit Tests", () => {
authority: TEST_CONFIG.validAuthority,
redirectUri: TEST_URIS.TEST_ALTERNATE_REDIR_URI,
postLogoutRedirectUri: TEST_URIS.TEST_LOGOUT_URI,
navigateToLoginRequestUrl: false,
},
cache: {
cacheLocation: BrowserCacheLocation.LocalStorage,
Expand All @@ -261,7 +259,6 @@ describe("Configuration.ts Class Unit Tests", () => {
expect(newConfig.auth.postLogoutRedirectUri).toBe(
TEST_URIS.TEST_LOGOUT_URI
);
expect(newConfig.auth.navigateToLoginRequestUrl).toBe(false);
// Cache config checks
expect(newConfig.cache).not.toBeNull();
expect(newConfig.cache?.cacheLocation).not.toBeNull();
Expand Down
24 changes: 16 additions & 8 deletions lib/msal-browser/test/interaction_client/RedirectClient.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,6 @@ describe("RedirectClient", () => {
auth: {
// @ts-ignore
...pca.config.auth,
navigateToLoginRequestUrl: false,
},
},
browserStorage,
Expand All @@ -368,7 +367,10 @@ describe("RedirectClient", () => {
TEST_HASHES.TEST_SUCCESS_CODE_HASH_REDIRECT,
testRequest,
TEST_CONFIG.TEST_VERIFIER,
rootMeasurement
rootMeasurement,
{
navigateToLoginRequestUrl: false,
}
)
.catch((e) => {
expect(e).toEqual("Error in handleResponse");
Expand Down Expand Up @@ -850,7 +852,6 @@ describe("RedirectClient", () => {
let pca = new PublicClientApplication({
auth: {
clientId: TEST_CONFIG.MSAL_CLIENT_ID,
navigateToLoginRequestUrl: false,
},
});

Expand Down Expand Up @@ -883,7 +884,10 @@ describe("RedirectClient", () => {
"",
testRequest,
TEST_CONFIG.TEST_VERIFIER,
rootMeasurement
rootMeasurement,
{
navigateToLoginRequestUrl: false,
}
);
expect(tokenResponse?.uniqueId).toEqual(testTokenResponse.uniqueId);
expect(tokenResponse?.tenantId).toEqual(testTokenResponse.tenantId);
Expand Down Expand Up @@ -1147,7 +1151,6 @@ describe("RedirectClient", () => {
let pca = new PublicClientApplication({
auth: {
clientId: TEST_CONFIG.MSAL_CLIENT_ID,
navigateToLoginRequestUrl: false,
},
});

Expand Down Expand Up @@ -1180,7 +1183,10 @@ describe("RedirectClient", () => {
"",
testRequest,
TEST_CONFIG.TEST_VERIFIER,
rootMeasurement
rootMeasurement,
{
navigateToLoginRequestUrl: false,
}
);
expect(tokenResponse?.uniqueId).toEqual(testTokenResponse.uniqueId);
expect(tokenResponse?.tenantId).toEqual(testTokenResponse.tenantId);
Expand Down Expand Up @@ -1667,7 +1673,6 @@ describe("RedirectClient", () => {
let pca = new PublicClientApplication({
auth: {
clientId: TEST_CONFIG.MSAL_CLIENT_ID,
navigateToLoginRequestUrl: false,
},
});

Expand Down Expand Up @@ -1717,7 +1722,10 @@ describe("RedirectClient", () => {
"",
testRequest,
TEST_CONFIG.TEST_VERIFIER,
rootMeasurement
rootMeasurement,
{
navigateToLoginRequestUrl: false,
}
);
});

Expand Down