简体   繁体   中英

Azure b2c Forgot password link sometimes redirect back to login

The Forgot password link of my Azure b2c login page sometimes redirect back to the login page. It redirects back to the login page in about 75% of the times I click it, and otherwise successfully to the page for changing password.

Here is my AuthenticationService:

export class AuthenticationService {
    private id_token: string;
    private access_token: string;
    private applicationSettings: ApplicationSettings;
    private authority: string;

    clientApplication: UserAgentApplication;
    jwtHelper: JwtHelper;

    constructor(private settingsProvider: SettingsProvider) {
        this.applicationSettings = settingsProvider.configuration;
        this.authority = this.getAuthority();
        this.jwtHelper = new JwtHelper();
        this.clientApplication = new Msal.UserAgentApplication(
            this.applicationSettings.clientID,
            this.authority,
            (errorDesc: any, token: any, error: any, tokenType: any) => {
                this.logonCallback.call(this, errorDesc, token, error, tokenType);
            },
            this.applicationSettings.clientOptions
        );
    }

    login(): void {
        this.clientApplication.loginRedirect(
            this.applicationSettings.b2cScopes);
    }

    loginPopup(): void {
        var storedThis = this;
        this.clientApplication.loginPopup(this.applicationSettings.b2cScopes)
        .then((idToken) => {
            this.id_token = idToken;
            console.log("ID token:", idToken);
            this.getAccessToken(this.applicationSettings.b2cScopes)
            .catch((reason) => {
                console.log('Unable to acquire token after login:', reason);
                storedThis.logout();
            });
        }, function (error) {
            console.log(error);
        });
    }

    getAccessToken(scopes: string[]): Promise<string> {
        var storedThis = this;
        if(this.access_token) {
            return new Promise((resolve, reject) => {
                resolve(this.access_token)
            });
        }

        let tokenPromise = this.clientApplication.acquireTokenSilent(scopes);
        tokenPromise.then((token) => {
            this.access_token = token;
            console.log("Access token:", token);
        });
        tokenPromise.catch((reason) => {
            this.clientApplication.acquireTokenPopup(scopes)
                .then((token) => {
                    this.access_token = token;
                    console.log("Access token:", token);
                }, function (error) {
                    console.log('Unable to acquire token using popup:', error);
                  storedThis.logout();
            });
        });
        return tokenPromise;
    }

    logout(): void {
        sessionStorage.removeItem('customerId');
        sessionStorage.removeItem('customerIsActive');
        this.clientApplication.logout();
    };

    isAuthenticated(): boolean  {
        let user = this.clientApplication.getUser();
        return user !== null;
    }

    getUser() {
        let user = this.clientApplication.getUser();
        return user;
    }

    getB2cScopes() {
        return this.applicationSettings.b2cScopes;
    }

    // Called after loginRedirect or acquireTokenPopup
    private logonCallback(errorDesc: any, token: any, error: any, tokenType: any) {
        // Password reset functionality
        if (errorDesc) {
            if (errorDesc.indexOf('AADB2C90118') > -1) {
                localStorage.removeItem('theme');
                this.clientApplication = new Msal.UserAgentApplication(
                    this.applicationSettings.clientID,
                    this.getPasswordResetAuthority(),
                    this.passwordResetCallback,
                    {
                        "redirectUri": this.applicationSettings.baseUrl + "/login"
                    }
                );
                this.login();
            }
        }

        // Redirect to previous page functionality
        var loginRedirectPath = sessionStorage.getItem('loginRedirectPath');
        if (loginRedirectPath != null) {
            sessionStorage.removeItem('loginRedirectPath');
            window.location.replace(
                this.settingsProvider.configuration.clientOptions.redirectUri + loginRedirectPath);
            return;
        }
        // Get access token
        if (!errorDesc || errorDesc.indexOf('AADB2C90118') == -1) {
            console.log("ErrorNr: AADB2C90118");
            this.getAccessToken(this.applicationSettings.b2cScopes)
            .catch((reason) => {
                console.log('Unable to acquire token after login:', reason);
            });
        }
    }

    getAuthority() {
        return this.applicationSettings.b2cDomain + "/tfp/" + 
        this.applicationSettings.tenant + "/" + 
        this.applicationSettings.signUpSignInPolicy;
    }

    getPasswordResetAuthority() {
        return this.applicationSettings.b2cDomain + "/tfp/" + 
        this.applicationSettings.tenant + "/" + 
        this.applicationSettings.passwordResetPolicy;
    }

    passwordResetCallback() {
        this.logout();
    }
}

Here is my AuthGuard:

export class AuthGuard implements CanActivate {
    constructor(private authenticationService: AuthenticationService, private router: Router, private themeProvider: ThemeProvider) { }

    canActivate(): any {
        console.log('auth gaurd');
        let userIsAuthenticated = this.authenticationService.isAuthenticated();
        if (userIsAuthenticated) {
            return true;
        }

        this.authenticationService.login();
        return false;
    }
}

The AuthGuard is used like this in the Routes in app.module:

{ path: '', canActivate: [AuthGuard],  children: [ ...

Here is my configurations:

{
    "tenant": "XXXXX.onmicrosoft.com",
    "clientID": "XXXXX",
    "signUpSignInPolicy": "B2C_1_Sign_Up_or_In",
    "passwordResetPolicy": "B2C_1_Password_Reset_Policy",
    "baseUrl": "http://localhost:4200",
    "b2cScopes": [
        "XXXXX"
    ],
    "clientOptions": {
        "redirectUri": "http://localhost:4200",
        "postLogoutRedirectUri": "http://localhost:4200",
        "validateAuthority": true,
        "cacheLocation": "sessionStorage"
    },
    "b2cDomain": "https://login.microsoftonline.com"
}

I solved the issue. Since the user is not loged in when changing the password, my AuthGuard will try to run the login function. To solve this I added a note into session storage when getting the reset password error code, like this:

if (errorDesc.indexOf('AADB2C90118') > -1) {
    ...
    sessionStorage.setItem('changePassword', 'true'); // Added this line
    ...

Then I checked for this note in my AuthGuard. If the note is set, then I do not want to run the login function, see below.

var changePassword = sessionStorage.getItem("changePassword");

if (changePassword == null)
  this.authenticationService.login();
else
  sessionStorage.removeItem("changePassword");

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM