msal.js angular 6 SPA callback and errors handling

lazizanie

I m using msal.js on a angular 6 SPA to handle authentication, I have a few problems: First, I couldn t find a clear example on how to handle errors with the lib, so I picked things left and right, and I arrived to the next result:

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

The getAuthenticationToken function is used into my httpinterceptor to set the bearer token. This is working fine, except when my token expires emitting the next error:

main.c20e047e67b91051727f.js:1 AADB2C90077: User does not have an existing session and request prompt parameter has a value of 'None'. Correlation ID: 3a627592-5ab0-4e54-b01d-e4296e4d4002 Timestamp: 2018-11-27 08:30:32Z |interaction_required

In my attempt to handle this case, you can see the callback checking the error code content. The problem is, my callback is never called after a acquireTokenSilent failure...I wonder why and if I am doing something wrong? For the acquireTokenSilent, I m guessing you can handle the error into the promise rejection. Although not ideal.

Secondly, the context in the callback is not the same as my service one, I don t have access to "this", from what I ve read, it is overwritten by the library. My temporary hack for "AADB2C90118 forgotten password error", is to dirtily create a new userAgentAplication with the appropriate authority, is there any way to have access to my service context in the callback and avoid doing that?

Edit I managed to make things work out fine: "this" references the userAgentApplication itself in the callback so instead of creating a new one you can do something like this:

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

OR bind this to your callBack:

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

and my interceptor:

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

For infos, I m using the latest "msal": "^0.2.3", "typescript": "~2.7.2", "@angular/core": "^6.0.3".

lazizanie

My Final version. working fine.

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

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

分類Dev

Authentication on angular spa using msal.js

分類Dev

Catching and handling backend errors in Angular

分類Dev

OpenLayers 6 + Angular 8 : LineString(s) not showing up on Vector Layer (With no errors from JS)

分類Dev

Angular 6 handling of 403 response with new RxJS

分類Dev

handling nested callback functions

分類Dev

How to handle azure access token between web api and angular apps without using MSAL.js

分類Dev

Handling two consecutive errors

分類Dev

Handling multiple errors in go

分類Dev

Cannot get errors from input validation in Angular 6

分類Dev

Elixir/Phoenix handling erlang errors

分類Dev

handling unicode encode errors in python

分類Dev

Handling Errors in PocketSphinx Android app

分類Dev

msal.js - Logout without redirect

分類Dev

msal.jsとMicrosoftGraphi API

分類Dev

Adding Cheerio.js to an Angular 6 project?

分類Dev

cytoscape.jsとAngular6

分類Dev

call a callback in other callback js

分類Dev

MSAL.jsを使用してAngularを介してAzureADでユーザーを作成する

分類Dev

Handling errors with clojure core.async pipeline

分類Dev

Handling AWS Lambda errors with API Gateway

分類Dev

Customize errors handling in uu-parsinglib in Haskell

分類Dev

Azure Webjobs SDK and Queue handling errors

分類Dev

Callback for Three JS traverse

分類Dev

Azure msal-angular cannot read auth attributes from LocalStorage?

分類Dev

Access Angular component property/ function inside callback functions of anime.js

分類Dev

ASP.NET Core 2 Angular 6SPAテンプレートアプリケーションの公開

分類Dev

MSAL.jsでLocalStorageを使用する方法

分類Dev

Reactのmsal.js-Azure認証

分類Dev

Angular 6 + Popper.js(jQueryなし)

Related 関連記事

ホットタグ

アーカイブ