Please follow the issue template below. Failure to do so will result in a delay in answering your question.
React
We are using this code:
public async acquireToken(loginHint?: string): Promise<any> {
const params: any = { scopes: [`${config.auth.clientId}/User.read`], correlationId: uuid(), loginHint };
const account = this.getAccount(loginHint);
if (!account) {
this.loginRedirect(params);
return;
}
try {
const response = await this.publicClientApplication.acquireTokenSilent({
...params,
account,
authority: this.getCachedAuthority(),
redirectUri: config.auth.redirectUri + '/blank.html'
});
return response.accessToken;
} catch (e) {
console.warn(e);
if (e instanceof InteractionRequiredAuthError) {
this.loginRedirect(params);
} else {
throw e;
}
}
}
private async loginRedirect(params: any) {
this.publicClientApplication.loginRedirect({ ...params, authority: this.getCommonAuthority() });
}
and it works fine when the page url is the redirect url. but when other url is triggering the redirect, we are getting into redirect loop.
I found a workaround for that by changing the redirect function to that:
private async loginRedirect(params: any) {
// msal redirect loop workaround
setTimeout(() => this.publicClientApplication.loginRedirect({ ...params, authority: this.getCommonAuthority() }), 0);
}
interaction_in_progress: Interaction is currently in progress. Please ensure that this interaction has been completed before calling an interactive API.
{
auth: {
clientId: config.auth.clientId,
redirectUri: config.auth.redirectUri // http://localhost:3000
},
cache: {
cacheLocation: 'localStorage',
storeAuthStateInCookie: true
},
system: {
loggerOptions: {
loggerCallback: this.logCallback,
logLevel: LogLevel.Warning,
piiLoggingEnabled: false
}
}
}
no to have redirect loop from all url paths.
This is affecting me as well.
I faced same issue in Angular env with msal-browser 2.0 and solved it with navigateToLoginRequestUrl: false and set redirectUri to the page that doesn't require authentication.
As I understand error happens because I redirect to IDP in the guard if there is no account but due to handleRedirect runs after the guard i fall in the infinite redirecting loop.
class AppComponent {
constructor(private auth: AuthService) {
this.auth.handleRedirect().catch(() => console.error('error'));
}
}
class AuthGuard implements CanActivate {
constructor(private auth: AuthService) {
}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> | boolean {
const account = this.auth.getAccount();
const scopes = this.auth.ngConfig.scopes;
if (!account) {
// here I have "Interaction is currently in progress" after returning from IDP(redirect flow)
return this.auth.loginRedirect({scopes}).then(() => false);
}
return this.auth.acquireTokenSilent({scopes, account})
.then(() => true)
.catch((err: AuthError) => {
if (err instanceof InteractionRequiredAuthError) {
this.auth.loginRedirect({scopes});
return false;
}
throw err;
});
}
}
In msal 1.0 was urlContainsHash method that can help to prevent this but it removed in msal-browser 2.0
Is there a way to make it work with navigateToLoginRequestUrl: true ?
@Dolevco A redirect loop can occur if your application is attempting to check if the user is logged in before the handleRedirectPromise (which processes the redirect response) has completed. Please see our sample here.
Is your application calling handleRedirectPromise? If so, can you show us the code you are using for that?
The issue occurs when you have an if statement that calls loginRedirect when the user is not logged in and you landed on a page that is not the redirect URI. I think this is because MSAL redirects back to the captured state before the first redirect occurred, but this redirect fails because the application code also called loginRedirect, so you end up in a redirect loop.
So basically it is a race condition between the navigateToLoginRequestUrl functionality and the loginRedirect() call
we have updated the code to wait for the redirectPromise to be resolved. but steel our workaround with the setTimeout statement required. this is the code:
```js
constructor() {
this.publicClientApplication = new PublicClientApplication({
auth: {
clientId: config.auth.clientId,
redirectUri: config.auth.redirectUri
},
cache: {
cacheLocation: 'localStorage',
storeAuthStateInCookie: true
},
system: {
loggerOptions: {
loggerCallback: this.logCallback,
logLevel: LogLevel.Warning,
piiLoggingEnabled: false
}
}
});
this.redirectPromise = this.publicClientApplication.handleRedirectPromise().catch((e: Error) => console.error(e));
}
@traceMinifiedAsync('acquireToken')
public async acquireToken(loginHint?: string): Promise<any> {
const params: any = { scopes: [`${config.auth.clientId}/User.read`], correlationId: uuid(), loginHint };
console.info('AcquireToken', addLogProps({ authCorrelationId: params.correlationId }));
await this.redirectPromise;
const account = this.getAccountInfo(loginHint);
if (!account) {
this.loginRedirect(params);
return;
}
try {
const response = await this.publicClientApplication.acquireTokenSilent({
...params,
account,
authority: this.getCachedAuthority(),
redirectUri: config.auth.redirectUri + '/blank.html'
});
return response.accessToken;
} catch (e) {
const requiresInteraction = e instanceof InteractionRequiredAuthError;
(requiresInteraction ? console.warn : console.error)(e, addLogProps({ authCorrelationId: params.correlationId }));
if (requiresInteraction) {
this.loginRedirect(params);
} else {
throw e;
}
}
}`
A better workaround is to check if window.location.hash is empty before calling loginRedirect
I've added APP_INITIALIZER into my angular project.
function initializer(auth: AuthService) {
return () => auth.handleRedirect().catch(err => console.error(err));
}
It prevents running an application until promise resolve. Example here
Did you think about running handleRedirectPromise before bootstrapping the project ?
Not all of our routes are require authentication. we must run some logic before calling the login.
But if no redirection callback the handleRedirectPromise will do nothing. It would be the solution.
@Dolevco Do you know why the setTimeout is still required in your updated code? I'm not sure why it would be if the code is awaiting the result of the handleRedirectPromise().
We have some updates to the handleRedirect code coming in #2045 and #2078 that may make this flow more reliable. We plan to release this on Monday.
not sure why it is still required. I guess it should wait to a callback that is not awaited before, that may be related to msal internal logic.
again, this happens only on path that is not the redirect uri.
To avoid infinite loop when the uri is different than redirectUri you have to set navigateToLoginRequestUrl to false.
but then it won't redirect back to the requested url. it will redirect to the root url that registered in the application redirect uri
This issue has not seen activity in 14 days. It will be closed in 7 days if it remains stale.
@Dolevco Is this still an issue? If so can you try upgrading to 2.1.0?
Hi I was wondering if this was fixed or how someone fixed this issue? I'm having the same problem and I've followed the instructions here about the redirectURI and navigateToLoginRequestUrl.
My application is using handleRedirectPromise and it checks to see if a user is logged in. I am pretty stumped so any help I can get would be great.
However I am using version 2.0.0 so would updating to 2.1.0 help?
@amng9560 Loops can happen for a number of reasons without more information it's hard to identify what might be causing yours. Updating to 2.1.0 would be a good first step as we did fix some bugs related to redirect since 2.0.0. Also please make sure that handleRedirectPromise resolves before calling any other function such as loginRedirect as not waiting for a response might create a race condition between your login logic and handling the response from the login. If you try those things and still can't get it working please open a new issue with steps to repro, thanks!
To clarify an earlier point about navigateToLoginRequestUrl; this should not cause a redirect loop on its own. If changing that flag one way or the other solves the loop there's either a usage problem or there's a bug with the library which we would very much like to know about. If that's the case please open a new issue with steps to repro so we can get that fixed.
This issue has not seen activity in 14 days. It will be closed in 7 days if it remains stale.
This issue has been closed due to inactivity. If this has not been resolved please open a new issue. Thanks!