import { AzureAuth } from './AzureAuth' ; import { ProviderSettings , SecureStorageProvider , CachingProvider , Logger , MessageDisplayer , ErrorLookup , StringLookup , AzureAuthType , Tenant , AADResource , LoginResponse , DeviceCodeStartPostData , Deferred , DeviceCodeCheckPostData , AuthRequest , UserInteraction } from '../models' ; import { AzureAuthError } from '../errors/AzureAuthError' ; import { ErrorCodes } from '../errors/errors' ; export class AzureDeviceCode extends AzureAuth { constructor ( protected readonly providerSettings , protected readonly secureStorage , protected readonly cachingProvider , protected readonly logger , protected readonly messageDisplayer , protected readonly errorLookup , protected readonly userInteraction , protected readonly stringLookup , protected readonly authRequest ) { super ( providerSettings , secureStorage , cachingProvider , logger , messageDisplayer , errorLookup , userInteraction , stringLookup , AzureAuthType . AuthCodeGrant ) ; } protected async login ( tenant , resource ) { let authCompleteDeferred ; let authCompletePromise = new Promise < void > ( ( resolve , reject ) => authCompleteDeferred = { resolve , reject } ) ; const uri = `${ this . loginEndpointUrl }/${ this . commonTenant . id }/oauth2/devicecode` ; const postData = { client_id : this . clientId , resource : resource . endpoint } ; const postResult = await this . makePostRequest ( uri , postData ) ; const initialDeviceLogin = postResult . data ; await this . authRequest . displayDeviceCodeScreen ( initialDeviceLogin . message , initialDeviceLogin . user_code , initialDeviceLogin . verification_url ) ; const finalDeviceLogin = await this . setupPolling ( initialDeviceLogin ) ; const accessTokenString = finalDeviceLogin . access_token ; const refreshTokenString = finalDeviceLogin . refresh_token ; const currentTime = new Date ( ) . getTime ( ) / 1000 ; const expiresOn = `${ currentTime + finalDeviceLogin . expires_in }` ; const result = await this . getTokenHelper ( tenant , resource , accessTokenString , refreshTokenString , expiresOn ) ; if ( ! result ) { throw new AzureAuthError ( ErrorCodes . GetAccessTokenDeviceCodeLogin , this . errorLookup . getSimpleError ( ErrorCodes . GetAccessTokenDeviceCodeLogin ) ) ; } authCompletePromise . finally ( async ( ) => await this . authRequest . closeDeviceCodeScreen ( ) ) . catch ( this . logger . error ) ; return { response : result , authComplete : authCompleteDeferred ! , } ; } private setupPolling ( info ) { const fiveMinutes = 5 * 60 * 1000 ; return new Promise < DeviceCodeLoginResult > ( ( resolve , reject ) => { let timeout ; const timer = setInterval ( async ( ) => { const x = await this . checkForResult ( info ) ; if ( ! x . access_token ) { return ; } clearTimeout ( timeout ) ; clearInterval ( timer ) ; resolve ( x ) ; } , info . interval * 1000 ) ; timeout = setTimeout ( ( ) => { clearInterval ( timer ) ; reject ( new AzureAuthError ( ErrorCodes . GetAccessTokenDeviceCodeLogin , this . errorLookup . getSimpleError ( ErrorCodes . GetAccessTokenDeviceCodeLogin ) ) ) ; } , fiveMinutes ) ; } ) ; } private async checkForResult ( info ) { const uri = `${ this . loginEndpointUrl }/${ this . commonTenant }/oauth2/token` ; const postData = { grant_type : 'urn:ietf:params:oauth:grant-type:device_code' , client_id : this . clientId , tenant : this . commonTenant . id , code : info . device_code } ; const postResult = await this . makePostRequest ( uri , postData ) ; const result = postResult . data ; return result ; } } interface DeviceCodeLogin { device_code : string ; expires_in : number ; interval : number ; message : string ; user_code : string ; verification_url : string ; } interface DeviceCodeLoginResult { token_type : string ; scope : string ; expires_in : number ; access_token : string ; refresh_token : string ; }