首页 > 解决方案 > Azure b2c 忘记密码链接有时会重定向回登录

问题描述

我的 Azure b2c 登录页面的忘记密码链接有时会重定向回登录页面。在我单击它的大约 75% 的时间里,它会重定向回登录页面,否则会成功重定向到更改密码的页面。

这是我的身份验证服务:

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();
    }
}

这是我的 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;
    }
}

AuthGuard 在 app.module 中的 Routes 中使用如下:

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

这是我的配置:

{
    "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"
}

标签: javascriptangularazureazure-ad-b2c

解决方案


我解决了这个问题。由于更改密码时用户没有登录,我的 AuthGuard 会尝试运行登录功能。为了解决这个问题,我在获取重置密码错误代码时在会话存储中添加了一条注释,如下所示:

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

然后我在我的 AuthGuard 中检查了这个注释。如果设置了注释,那么我不想运行登录功能,见下文。

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

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

推荐阅读