import { EndpointStorage , IEndpointConfiguration } from './endpoint.manager' ; import { TokenStorage , IToken , ICode , IError } from './token.manager' ; import { Dialog } from '../helpers/dialog' ; import { CustomError } from '../errors/custom.error' ; /**
 * Custom error type to handle OAuth specific errors.
 */ export class AuthError extends CustomError { constructor param message param state constructor ( message , public innerError ? ) { super ( 'AuthError' , message , innerError ) ; } } /**
 * Helper for performing Implicit OAuth Authentication with registered endpoints.
 */ export class Authenticator { constructor param endpoints param tokens constructor ( public endpoints ? , public tokens ? ) { if ( endpoints == null ) { this . endpoints = new EndpointStorage ( ) ; } if ( tokens == null ) { this . tokens = new TokenStorage ( ) ; } } param { string } provider param { boolean } force return { Promise < IToken | ICode > } authenticate ( provider , force = false , useMicrosoftTeams = false ) { let token = this . tokens . get ( provider ) ; let hasTokenExpired = TokenStorage . hasExpired ( token ) ; if ( ! hasTokenExpired && ! force ) { return Promise . resolve ( token ) ; } return this . _openAuthDialog ( provider , useMicrosoftTeams ) ; } return { boolean } static isAuthDialog ( useMicrosoftTeams = false ) { if ( ! /(access_token|code|error|state)/gi . test ( location . href ) ) { return false ; } Dialog . close ( location . href , useMicrosoftTeams ) ; return true ; } param { string } url param { string } exclude param { string } delimiter return { object } static getUrlParams ( url = location . href , exclude = location . origin , delimiter = '#' ) { if ( exclude ) { url = url . replace ( exclude , '' ) ; } let [ left , right ] = url . split ( delimiter ) ; let tokenString = right == null ? left : right ; if ( tokenString . indexOf ( '?' ) !== - 1 ) { tokenString = tokenString . split ( '?' ) [ 1 ] ; } return Authenticator . extractParams ( tokenString ) ; } static extractParams ( segment ) { if ( segment == null || segment . trim ( ) === '' ) { return null ; } let params = { } ; let regex = /([^&=]+)=([^&]*)/g ; let matchParts ; while ( ( matchParts = regex . exec ( segment ) ) !== null ) { if ( matchParts [ 1 ] === '/state' ) { matchParts [ 1 ] = matchParts [ 1 ] . replace ( '/' , '' ) ; } params [ decodeURIComponent ( matchParts [ 1 ] ) ] = decodeURIComponent ( matchParts [ 2 ] ) ; } return params ; } private async _openAuthDialog ( provider , useMicrosoftTeams ) { let endpoint = this . endpoints . get ( provider ) ; if ( endpoint == null ) { return Promise . reject ( new AuthError ( `No such registered endpoint: ${ provider } could be found.` ) ) as any ; } let { state , url } = EndpointStorage . getLoginParams ( endpoint ) ; let redirectUrl = await new Dialog < string > ( url , 1024 , 768 , useMicrosoftTeams ) . result ; return this . _handleTokenResult ( redirectUrl , endpoint , state ) ; } param { Endpoint } endpoint param { object } data param { object } headers return { Promise < IToken > } private _exchangeCodeForToken ( endpoint , data , headers ? ) { return new Promise ( ( resolve , reject ) => { if ( endpoint . tokenUrl == null ) { console . warn ( 'We couldn\'t exchange the received code for an access_token. The value returned is not an access_token. Please set the tokenUrl property or refer to our docs.' ) ; return resolve ( data ) ; } let xhr = new XMLHttpRequest ( ) ; xhr . open ( 'POST' , endpoint . tokenUrl ) ; xhr . setRequestHeader ( 'Accept' , 'application/json' ) ; xhr . setRequestHeader ( 'Content-Type' , 'application/json' ) ; for ( let header in headers ) { if ( header === 'Accept' || header === 'Content-Type' ) { continue ; } xhr . setRequestHeader ( header , headers [ header ] ) ; } xhr . onerror = ( ) => reject ( new AuthError ( 'Unable to send request due to a Network error' ) ) ; xhr . onload = ( ) => { try { if ( xhr . status === 200 ) { let json = JSON . parse ( xhr . responseText ) ; if ( json == null ) { return reject ( new AuthError ( 'No access_token or code could be parsed.' ) ) ; } else if ( 'access_token' in json ) { this . tokens . add ( endpoint . provider , json ) ; return resolve ( json as IToken ) ; } else { return reject ( new AuthError ( json . error , json . state ) ) ; } } else if ( xhr . status !== 200 ) { return reject ( new AuthError ( 'Request failed. ' + xhr . response ) ) ; } } catch ( e ) { return reject ( new AuthError ( 'An error occurred while parsing the response' ) ) ; } } ; xhr . send ( JSON . stringify ( data ) ) ; } ) ; } private _handleTokenResult ( redirectUrl , endpoint , state ) { let result = Authenticator . getUrlParams ( redirectUrl , endpoint . redirectUrl ) ; if ( result == null ) { throw new AuthError ( 'No access_token or code could be parsed.' ) ; } else if ( endpoint . state && + result . state !== state ) { throw new AuthError ( 'State couldn\'t be verified' ) ; } else if ( 'code' in result ) { return this . _exchangeCodeForToken ( endpoint , result as ICode ) ; } else if ( 'access_token' in result ) { return this . tokens . add ( endpoint . provider , result as IToken ) ; } else { throw new AuthError ( ( result as IError ) . error ) ; } } }