import { Injectable , NgZone } from '@angular/core' ; import { Observable , bindCallback , timer } from 'rxjs' ; import { map } from 'rxjs/operators' ; import * as lib from 'adal-angular' ; @ Injectable ( ) export class AdalService { private context : adal . AuthenticationContext = < any > null ; private loginRefreshTimer = < any > null ; private user : adal . User = { authenticated : false , userName : '' , error : '' , token : '' , profile : { } , loginCached : false } ; constructor ( private ngZone ) { } public init ( configOptions ) { if ( ! configOptions ) { throw new Error ( 'You must set config, when calling init.' ) ; } const existingHash = window . location . hash ; let pathDefault = window . location . href ; if ( existingHash ) { pathDefault = pathDefault . replace ( existingHash , '' ) ; } configOptions . redirectUri = configOptions . redirectUri || pathDefault ; configOptions . postLogoutRedirectUri = configOptions . postLogoutRedirectUri || pathDefault ; this . context = lib . inject ( configOptions ) ; this . updateDataFromCache ( ) ; if ( this . user . loginCached && ! this . user . authenticated && window . self == window . top && ! this . isInCallbackRedirectMode ) { this . refreshLoginToken ( ) ; } else if ( this . user . loginCached && this . user . authenticated && ! this . loginRefreshTimer && window . self == window . top ) { this . setupLoginTokenRefreshTimer ( ) ; } } public get config ( ) : adal . Config { return this . context . config ; } public get userInfo ( ) : adal . User { return this . user ; } public login ( ) { this . context . login ( ) ; } public loginInProgress ( ) { return this . context . loginInProgress ( ) ; } public logOut ( ) { this . context . logOut ( ) ; } public handleWindowCallback ( removeHash = true ) { const hash = window . location . hash ; if ( this . context . isCallback ( hash ) ) { var isPopup = false ; if ( this . context . _openedWindows . length > 0 && this . context . _openedWindows [ this . context . _openedWindows . length - 1 ] . opener && this . context . _openedWindows [ this . context . _openedWindows . length - 1 ] . opener . _adalInstance ) { this . context = this . context . _openedWindows [ this . context . _openedWindows . length - 1 ] . opener . _adalInstance ; isPopup = true ; } else { try { if ( window . parent && window . parent . _adalInstance ) { this . context = window . parent . _adalInstance ; } } catch { } } const requestInfo = this . context . getRequestInfo ( hash ) ; this . context . saveTokenFromHash ( requestInfo ) ; var callback = this . context . _callBackMappedToRenewStates [ requestInfo . stateResponse ] || this . context . callback ; if ( requestInfo . requestType === this . context . REQUEST_TYPE . LOGIN ) { this . updateDataFromCache ( ) ; this . setupLoginTokenRefreshTimer ( ) ; } else if ( requestInfo . requestType === this . context . REQUEST_TYPE . RENEW_TOKEN ) { this . updateDataFromCache ( ) ; } if ( requestInfo . stateMatch ) { if ( typeof callback === 'function' ) { if ( requestInfo . requestType === this . context . REQUEST_TYPE . RENEW_TOKEN ) { if ( requestInfo . parameters [ 'access_token' ] ) { callback ( this . context . _getItem ( this . context . CONSTANTS . STORAGE . ERROR_DESCRIPTION ) , requestInfo . parameters [ 'access_token' ] ) ; } else if ( requestInfo . parameters [ 'id_token' ] ) { callback ( this . context . _getItem ( this . context . CONSTANTS . STORAGE . ERROR_DESCRIPTION ) , requestInfo . parameters [ 'id_token' ] ) ; } else if ( requestInfo . parameters [ 'error' ] ) { callback ( this . context . _getItem ( this . context . CONSTANTS . STORAGE . ERROR_DESCRIPTION ) , null ) ; this . context . _renewFailed = true ; } } } } } if ( removeHash ) { if ( window . location . hash ) { if ( window . history . replaceState ) { window . history . replaceState ( '' , '/' , window . location . pathname ) ; } else { window . location . hash = '' ; } } } } public getCachedToken ( resource ) { return this . context . getCachedToken ( resource ) ; } public acquireToken ( resource ) { return bindCallback < string | null , string | null > ( ( callback ) => { this . context . acquireToken ( resource , ( error , tokenOut ) => { if ( error ) { this . context . error ( 'Error when acquiring token for resource: ' + resource , error ) ; callback ( null , error ) ; } else { callback ( tokenOut , null ) ; } } ) ; } ) ( ) . pipe < string | null > ( map ( ( result ) => { if ( ! result [ 0 ] && result [ 1 ] ) { throw ( result [ 1 ] ) ; } return result [ 0 ] ; } ) ) ; } public getUser ( ) { return bindCallback < adal . User | null > ( ( callback ) => { this . context . getUser ( ( error , user ? ) => { if ( error ) { this . context . error ( 'Error when getting user' , error ) ; callback ( null ) ; } else { callback ( user || null ) ; } } ) ; } ) ( ) ; } public clearCache ( ) { this . context . clearCache ( ) ; } public clearCacheForResource ( resource ) { this . context . clearCacheForResource ( resource ) ; } public info ( message ) { this . context . info ( message ) ; } public verbose ( message ) { this . context . verbose ( message ) ; } public getResourceForEndpoint ( url ) { return this . context ? null : this . context . getResourceForEndpoint ( url ) ; } public refreshDataFromCache ( ) { this . updateDataFromCache ( ) ; } private updateDataFromCache ( ) { const token = this . context . getCachedToken ( < any > this . context . config . loginResource ) ; this . user . authenticated = token !== null && token . length > 0 ; const user = this . context . getCachedUser ( ) ; if ( user ) { this . user . userName = user . userName ; this . user . profile = user . profile ; this . user . token = token ; this . user . error = this . context . getLoginError ( ) ; this . user . loginCached = true ; } else { this . user . userName = '' ; this . user . profile = { } ; this . user . token = '' ; this . user . error = this . context . getLoginError ( ) ; this . user . loginCached = false ; } } private refreshLoginToken ( ) { if ( ! this . user . loginCached ) throw ( "User not logged in" ) ; this . acquireToken ( < any > this . context . config . loginResource ) . subscribe ( ( token ) => { this . user . token = token ; if ( this . user . authenticated == false ) { this . user . authenticated = true ; this . user . error = '' ; window . location . reload ( ) ; } else { this . setupLoginTokenRefreshTimer ( ) ; } } , ( error ) => { this . user . authenticated = false ; this . user . error = this . context . getLoginError ( ) ; } ) ; } private now ( ) { return Math . round ( new Date ( ) . getTime ( ) / 1000.0 ) ; } private get isInCallbackRedirectMode ( ) : boolean { return window . location . href . indexOf ( "#access_token" ) !== - 1 || window . location . href . indexOf ( "#id_token" ) !== - 1 ; } ; private setupLoginTokenRefreshTimer ( ) { let exp = this . context . _getItem ( this . context . CONSTANTS . STORAGE . EXPIRATION_KEY + < any > this . context . config . loginResource ) ; let timerDelay = exp - this . now ( ) - ( this . context . config . expireOffsetSeconds || 300 ) > 0 ? exp - this . now ( ) - ( this . context . config . expireOffsetSeconds || 300 ) : 1 ; if ( this . loginRefreshTimer ) this . loginRefreshTimer . unsubscribe ( ) ; this . ngZone . runOutsideAngular ( ( ) => { this . loginRefreshTimer = timer ( timerDelay * 1000 ) . subscribe ( ( x ) => { this . refreshLoginToken ( ) ; } ) ; } ) ; } }