import { AzureAuth } from './AzureAuth' ; import { AzureAuthType , ProviderSettings , SecureStorageProvider , CachingProvider , Logger , MessageDisplayer , ErrorLookup , StringLookup , AADResource , LoginResponse , Tenant , Deferred , AuthorizationCodePostData , OAuthTokenResponse , AuthRequest , UserInteraction } from '../models' ; import * as crypto from 'crypto' ; import * as qs from 'qs' ; import { AzureAuthError } from '../errors/AzureAuthError' ; import { ErrorCodes } from '../errors/errors' ; export class AzureCodeGrant 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 { codeVerifier , codeChallenge } = this . createCryptoValues ( ) ; const state = await this . authRequest . getState ( ) ; const loginQuery = { response_type : 'code' , response_mode : 'query' , client_id : this . clientId , redirect_uri : this . providerSettings . redirectUri , state , prompt : 'select_account' , code_challenge_method : 'S256' , code_challenge : codeChallenge , resource : resource . endpoint } ; const signInUrl = `${ this . loginEndpointUrl }${ tenant . id }/oauth2/authorize?${ qs . stringify ( loginQuery ) }` ; await this . userInteraction . openUrl ( signInUrl ) ; const authCode = await this . authRequest . getAuthorizationCode ( signInUrl , authCompletePromise ) ; const response = await this . getTokenWithAuthorizationCode ( tenant , resource , { authCode , redirectUri : this . providerSettings . redirectUri , codeVerifier } ) ; if ( ! response ) { throw new AzureAuthError ( ErrorCodes . GetAccessTokenAuthCodeGrant , this . errorLookup . getSimpleError ( ErrorCodes . GetAccessTokenAuthCodeGrant ) ) ; } return { authComplete : authCompleteDeferred ! , response } ; } param tenant param resource param authCode param redirectUri param codeVerifier private async getTokenWithAuthorizationCode ( tenant , resource , { authCode , redirectUri , codeVerifier } ) { const postData = { grant_type : 'authorization_code' , code : authCode , client_id : this . clientId , code_verifier : codeVerifier , redirect_uri : redirectUri , resource : resource . endpoint } ; return this . getToken ( tenant , resource , postData ) ; } private createCryptoValues ( ) { const codeVerifier = this . toBase64UrlEncoding ( crypto . randomBytes ( 32 ) . toString ( 'base64' ) ) ; const codeChallenge = this . toBase64UrlEncoding ( crypto . createHash ( 'sha256' ) . update ( codeVerifier ) . digest ( 'base64' ) ) ; return { codeVerifier , codeChallenge } ; } } interface CryptoValues { codeVerifier : string ; codeChallenge : string ; } interface AuthCodeResponse { authCode : string ; codeVerifier : string ; redirectUri : string ; }