import { Component } from '@angular/core' ; import { AfterViewChecked , OnDestroy , OnInit } from '@angular/core' ; import { ActivatedRoute } from '@angular/router' ; import { OpenYoloCredential as Credential , OpenYoloCredentialHintOptions as CredentialHintOptions , OpenYoloCredentialRequestOptions as CredentialRequestOptions , RenderMode } from '../../../../ts/protocol/data' ; import { AffiliationProvider , AUTHENTICATION_METHODS , ClientConfigurationProvider , CredentialDataProvider , DisplayCallbacks , InteractionProvider , LocalStateProvider , PrimaryClientConfiguration , ProviderFrame } from '../../../../ts/spi/spi' ; import { CredentialStoreService , StoredCredential } from '../credential_store/credential_store.service' ; @ Component ( { selector : 'app-openyolo-provider' , templateUrl : './openyolo_provider.component.html' , styleUrls : [ './openyolo_provider.component.css' ] } ) export class OpenYoloProviderComponent implements OnInit , OnDestroy , AfterViewChecked , InteractionProvider { private providerFramePromise : Promise < ProviderFrame > ; private renderPromiseResolvers : Function [ ] = [ ] ; renderMode : string ; failed = false ; displayCredentials : DisplayCredential [ ] | null ; pickResultPromise : Promise < Credential > ; pickResolver : ( value ? : Credential | PromiseLike < Credential > ) => void ; pickRejector : ( reason ? : any ) => void ; constructor ( private credentialStoreService , private route ) { } ngOnInit ( ) { let clientAuthDomain = null ; let clientNonce = null ; this . route . queryParams . forEach ( ( queryParams ) => { clientAuthDomain = queryParams [ 'client' ] || null ; clientNonce = queryParams [ 'id' ] || null ; this . renderMode = queryParams [ 'renderMode' ] || null ; } ) ; if ( ! this . renderMode ) { this . renderMode = RenderMode . bottomSheet ; } this . providerFramePromise = ProviderFrame . initialize ( { allowDirectAuth : true , clientAuthDomain , clientNonce , affiliationProvider : new SimpleAffiliationProvider ( ) , clientConfigurationProvider : new SimpleClientConfigurationProvider ( ) , credentialDataProvider : new CredentialDataProviderImpl ( this . credentialStoreService ) , interactionProvider : this , localStateProvider : new LocalStateProviderImpl ( ) , window } ) ; this . providerFramePromise . catch ( ( err ) => { this . failed = true ; console . error ( `Provider initialization failed: ${ err . code }` , err ) ; } ) ; } ngOnDestroy ( ) { this . providerFramePromise . then ( ( providerFrame ) => { providerFrame . dispose ( ) ; } ) ; } ngAfterViewChecked ( ) { for ( let resolver of this . renderPromiseResolvers ) { resolver ( ) ; } this . renderPromiseResolvers = [ ] ; } createRenderPromise ( ) { return new Promise < void > ( ( resolve ) => { this . renderPromiseResolvers . push ( resolve ) ; } ) ; } waitForRender ( satisfied ) { let thisRef = this ; return new Promise < void > ( async function ( resolve ) { while ( ! satisfied ( ) ) { await thisRef . createRenderPromise ( ) ; } resolve ( ) ; } ) ; } async showHintPicker ( hints , options , displayCallbacks ) { this . displayCredentials = hints . map ( ( hint ) => { return new DisplayCredential ( hint ) ; } ) ; this . pickResultPromise = new Promise < Credential > ( ( resolve , reject ) => { this . pickResolver = resolve ; this . pickRejector = reject ; } ) ; await this . waitForRender ( ( ) => { return document . getElementsByClassName ( 'picker' ) . length > 0 ; } ) ; let pickerElem = document . getElementsByClassName ( 'picker' ) [ 0 ] ; let computedStyle = window . getComputedStyle ( pickerElem ) ; let height = pickerElem . clientHeight ; height += parseInt ( computedStyle . getPropertyValue ( 'margin-top' ) , 10 ) ; height += parseInt ( computedStyle . getPropertyValue ( 'border-top' ) , 10 ) ; height += parseInt ( computedStyle . getPropertyValue ( 'border-bottom' ) , 10 ) ; height += parseInt ( computedStyle . getPropertyValue ( 'margin-bottom' ) , 10 ) ; displayCallbacks . requestDisplayOptions ( { height } ) ; try { return await this . pickResultPromise ; } finally { this . displayCredentials = null ; } } async showCredentialPicker ( credentials , options , displayCallbacks ) { return null ; } async showSaveConfirmation ( credential , displayCallbacks ) { return false ; } async showAutoSignIn ( credential , displayCallbacks , options ) { return ; } async dispose ( ) { return ; } handleCredentialClick ( displayCredential ) { this . pickResolver ( displayCredential . credential ) ; } dismiss ( ) { this . pickResolver ( null ) ; } } /**
 * A wrapper for a credential that reformats it specifically to make display
 * in the angular template easier.
 */ export class DisplayCredential { isEmail = false ; isPhone = false ; imageUrl : string = null ; firstLine : string ; secondLine : string = null ; constructor ( public credential ) { if ( credential . displayName ) { this . firstLine = credential . displayName ; this . secondLine = credential . id ; } else { this . firstLine = credential . id ; } if ( credential . id . indexOf ( '@' ) !== - 1 ) { this . isEmail = true ; } else if ( /[0-9-+. \(\)]/ . test ( credential . id ) ) { this . isPhone = true ; } } } /**
 * Affiliation provider that simply returns the provided authentication domain
 * as equivalent to itself.
 */ class SimpleAffiliationProvider implements AffiliationProvider { async getEquivalentDomains ( authDomain ) { return [ authDomain ] ; } } /**
 * Client configuration provider that assumes that the API is enabled for all
 * clients.
 */ class SimpleClientConfigurationProvider implements ClientConfigurationProvider { async getConfiguration ( authDomain ) { return { type : 'primary' , apiEnabled : true , allowNestedFrameRequests : true , authenticationEndpoint : authDomain + '/login' , requireProxyLogin : false } ; } } class CredentialDataProviderImpl implements CredentialDataProvider { constructor ( private credentialStoreService ) { } async getAllCredentials ( authDomains , options ) { let credentials = this . credentialStoreService . getAllCredentials ( ) ; let resultCredentials = [ ] ; for ( let credential of credentials ) { if ( ! authDomains || authDomains . findIndex ( ( authDomain ) => authDomain === credential . from ) !== - 1 ) { resultCredentials . push ( this . translateToExternalCredential ( credential ) ) ; } } return resultCredentials ; } async getAllHints ( options ) { return this . credentialStoreService . getAllCredentials ( ) . map ( c => this . translateToExternalCredential ( c ) ) ; } async upsertCredential ( credential , original ? ) { return credential ; } async deleteCredential ( credential ) { return ; } private translateToExternalCredential ( credential ) { return { id : credential . id , displayName : credential . display , authMethod : AUTHENTICATION_METHODS . ID_AND_PASSWORD , authDomain : credential . from , password : credential . password } ; } } class LocalStateProviderImpl implements LocalStateProvider { private retainedCredentials = ( { } as { [ key ] : Credential ; } ) ; async isAutoSignInEnabled ( authDomain ) { return false ; } async setAutoSignInEnabled ( authDomains , enabled ) { return ; } async retainCredentialForSession ( authDomain , credential ) { this . retainedCredentials [ authDomain ] = credential ; } async getRetainedCredential ( authDomain ) { if ( ! ( authDomain in this . retainedCredentials ) ) { return null ; } let credential = this . retainedCredentials [ authDomain ] ; delete this . retainedCredentials [ authDomain ] ; return credential ; } }