msal.js Angular 6单页应用程序回调和错误处理

7

我正在使用msal.js处理认证,这是一个与Angular 6 SPA相关的IT技术。但是我遇到了一些问题:

首先,我无法找到如何使用该库处理错误的清晰示例,因此我不得不从各个方面进行尝试,最终得出了以下结果:

@Injectable()
export class AuthenticationService {
_clientApplication: Msal.UserAgentApplication;
_clientApplicationPR: Msal.UserAgentApplication;
private _authority: string;

constructor(private apiService: ApiService, private backendRoutes: BackendRoutes) {
    this._authority = `https://login.microsoftonline.com/tfp/${environment.tenant}/${environment.signUpSignInPolicy}`;

    this._clientApplication =
        new Msal.UserAgentApplication(
            environment.clientID,
            this._authority,
            this._authCallback,
            {
                cacheLocation: 'localStorage',
                redirectUri: window.location.origin
            });
}

private _authCallback(errorDesc: any, token: any, error: any, tokenType: 
any) {
    console.log("in call back");
    if (token) {
        this.addUser();
    } else {
        // console.log(`${error} - ${errorDesc}`);
         if (errorDesc.indexOf("AADB2C90118") > -1) {
            //Forgotten password
            this._clientApplicationPR = new Msal.UserAgentApplication(
                environment.clientID,
                `https://login.microsoftonline.com/tfp/${environment.tenant}/${environment.passResetPolicy}`,
                this._authCallback,
                {
                    cacheLocation: 'localStorage',
                    redirectUri: window.location.origin
                });
            this._clientApplicationPR.loginRedirect(environment.b2cScopes);
        } else if (errorDesc.indexOf("AADB2C90077") > -1) {
            //Expired Token    
   this._clientApplication.acquireTokenRedirect(environment.b2cScopes);
        }
    }
}

getAuthenticationToken(): Promise<string> {
    return 
 this._clientApplication.acquireTokenSilent(environment.b2cScopes)
        .then(token => token)
        .catch(error => {
            return Promise.reject(error);
        });
    }
}

getAuthenticationToken函数被用于我的http拦截器中以设置Bearer令牌。除了在令牌过期时会发出以下错误之外,它都运行正常:

main.c20e047e67b91051727f.js:1 AADB2C90077: 用户没有现有的会话,并且请求提示参数的值为“None”。关联ID:3a627592-5ab0-4e54-b01d-e4296e4d4002 时间戳:2018-11-27 08:30:32Z |interaction_required

为了处理这种情况,您可以看到回调函数检查错误代码内容。问题是,在acquireTokenSilent失败后,我的回调函数从未被调用...我不知道为什么,也不知道我是否做错了什么? 对于acquireTokenSilent,我想您可以在Promise拒绝中处理错误,虽然不是理想的解决方案。

其次,回调函数中的上下文与我的服务上下文不同,我无法访问"this",从我读到的资料来看,它被库覆盖了。我的"AADB2C90118忘记密码错误"的临时技巧是使用适当的权限创建一个新的userAgentApplication,是否有任何方法可以在回调函数中访问我的服务上下文并避免这样做呢?

编辑我设法使事情顺利进行:"this" 在回调中引用userAgentApplication本身,因此您可以像这样操作,而无需创建新的userAgentApplication:

      constructor(private apiService: ApiService, private backendRoutes: BackendRoutes) {
            this._authority = `https://login.microsoftonline.com/tfp/${environment.tenant}/${environment.signUpSignInPolicy}`;

            this._clientApplication =
                new Msal.UserAgentApplication(
                    environment.clientID,
                    this._authority,
                    this.msalHandler,
                    {
                        cacheLocation: 'localStorage',
                        redirectUri: window.location.origin
                    });
        }

        msalHandler(errorDesc: any, token: any, error: any, tokenType: any) {
            let userAgent: Msal.UserAgentApplication = <any>(this);
            if (errorDesc.indexOf("AADB2C90118") > -1) {
                //Forgotten password
                userAgent.authority = `https://login.microsoftonline.com/tfp/${environment.tenant}/${environment.passResetPolicy}`;
                userAgent.loginRedirect(environment.b2cScopes);

            } else if (errorDesc.indexOf("AADB2C90077") > -1) {
                //Expired Token
                this.logout();
            }
        }

将此绑定到您的回调函数中:
   this._clientApplication =
            new Msal.UserAgentApplication(
                environment.clientID,
                this._authority,
                this.msalHandler.bind(this),
                {
                    cacheLocation: 'localStorage',
                    redirectUri: window.location.origin
                });

以及我的拦截器:

 intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        return from(this.authenticationService.getAuthenticationToken()
            .then(token => {
                return req.clone({
                    setHeaders: {
                        Authorization: `Bearer ${token}`
                    }
                });
            })
            .catch(err => {
                this.authenticationService.msalHandler(err,null,null,null);
                return req;
            }))
            .switchMap(req => {
                return next.handle(req);
            });
    }

我的信息如下:我在使用最新的"msal": "^0.2.3", "typescript": "~2.7.2", "@angular/core": "^6.0.3"。


1
你解决过这个问题吗? - David C
是的,经过我自己编辑后,我的解决方案可以工作。只是在另一个地方处理acquiretokensilent对我来说不太美观。 - lazizanie
1个回答

1
我的最终版本。正常工作。
import {of,  Observable, empty, throwError } from 'rxjs';
import { Injectable } from '@angular/core';
import { environment } from '../../environments/environment';
import * as Msal from 'msal';
import { User } from "msal/lib-commonjs/User";
import { ApiService } from './api.service';
import { BackendRoutes } from './backend.routes';

@Injectable()
export class AuthenticationService {
    private _clientApplication: Msal.UserAgentApplication;
    private _authority: string;
    private _authorityPasseReset: string;
    private _authorityEditProfile: string;
    private _authoritySignIn: string;
    private _authoritySignUp: string;

    constructor(private apiService: ApiService, private backendRoutes: BackendRoutes) {
        // this._authority = `https://login.microsoftonline.com/tfp/${environment.tenant}/${environment.signInPolicy}`;
        this._authority = `https://${environment.tenant}.b2clogin.com/${environment.tenant}.onmicrosoft.com/`;

        this._authorityPasseReset = this._authority + `${environment.passResetPolicy}`;
        this._authorityEditProfile = this._authority + `${environment.editProfilePolicy}`;
        this._authoritySignIn = this._authority + `${environment.signInPolicy}`;
        this._authoritySignUp = this._authority + `${environment.signUpPolicy}`;


        this._clientApplication =
            new Msal.UserAgentApplication(
                environment.clientID,
                this._authoritySignIn,
                this.msalHandler,
                {
                    cacheLocation: 'localStorage',
                    redirectUri: window.location.origin + '/acceuil',
                    validateAuthority: false
                });
    }

    init() {
        this.refreshToken()
            .then(token => console.debug('token refreshed'))
            .catch(err => this.msalHandler(err, null, null, null));
    }


    msalHandler(errorDesc: any, token: any, error: any, tokenType: any) {
        let userAgent: Msal.UserAgentApplication = <any>(this);
        if (errorDesc.indexOf("AADB2C90118") > -1) {
            //Forgotten password
            userAgent.authority = this._authorityPasseReset;
            userAgent.loginRedirect(environment.b2cScopes);

        } else if (errorDesc.indexOf("AADB2C90077") > -1) {
            //Expired Token, function call from interceptor with proper context
            this.logout();
        }
    }

    login(): void {
        this._clientApplication.loginRedirect(environment.b2cScopes);
    }

    register(): void {
        this._clientApplication.authority = this._authoritySignUp;
        this._clientApplication.loginRedirect(environment.b2cScopes);
    }

    logout(): void {
        this._clientApplication.logout();
    }

    editProfile(): void {
        this._clientApplication.authority = this._authorityEditProfile;
        this._clientApplication.loginRedirect(environment.b2cScopes);
    }

    refreshToken(): Promise<string> {
        return this._clientApplication.acquireTokenSilent(environment.b2cScopes, this._authoritySignIn, this.getUser())
    }

    getAuthenticationToken(): Promise<string> {
        return this._clientApplication.acquireTokenSilent(environment.b2cScopes)
            .then(token => token)
            .catch(error => {
                return Promise.reject(error);
            });
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接