@@ -12,7 +12,7 @@ import * as yauzl from 'yauzl';
1212import * as crypto from 'crypto' ;
1313import { retry } from './retry' ;
1414import { CosmosClient } from '@azure/cosmos' ;
15- import { ClientSecretCredential , ClientAssertionCredential } from '@azure/identity' ;
15+ import { ClientSecretCredential } from '@azure/identity' ;
1616import * as cp from 'child_process' ;
1717import * as os from 'os' ;
1818import { Worker , isMainThread , workerData } from 'node:worker_threads' ;
@@ -47,6 +47,36 @@ class Temp {
4747 }
4848}
4949
50+ /**
51+ * Gets an access token converted from a WIF/OIDC id token.
52+ * We need this since this build job takes a while to run and while id tokens live for 10 minutes only, access tokens live for 24 hours.
53+ * Source: https://goodworkaround.com/2021/12/21/another-deep-dive-into-azure-ad-workload-identity-federation-using-github-actions/
54+ */
55+ export async function getAccessToken ( endpoint : string , tenantId : string , clientId : string , idToken : string ) : Promise < string > {
56+ const body = new URLSearchParams ( {
57+ scope : `${ endpoint } .default` ,
58+ client_id : clientId ,
59+ grant_type : 'client_credentials' ,
60+ client_assertion_type : 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer' ,
61+ client_assertion : encodeURIComponent ( idToken )
62+ } ) ;
63+
64+ const response = await fetch ( `https://login.microsoftonline.com/${ tenantId } /oauth2/v2.0/token` , {
65+ method : 'POST' ,
66+ headers : {
67+ 'Content-Type' : 'application/x-www-form-urlencoded'
68+ } ,
69+ body : body . toString ( )
70+ } ) ;
71+
72+ if ( ! response . ok ) {
73+ throw new Error ( `HTTP error! status: ${ response . status } ` ) ;
74+ }
75+
76+ const aadToken = await response . json ( ) ;
77+ return aadToken . access_token ;
78+ }
79+
5080interface RequestOptions {
5181 readonly body ?: string ;
5282}
@@ -636,7 +666,7 @@ function getRealType(type: string) {
636666 }
637667}
638668
639- async function processArtifact ( artifact : Artifact , artifactFilePath : string ) : Promise < void > {
669+ async function processArtifact ( artifact : Artifact , artifactFilePath : string , cosmosDBAccessToken : string ) : Promise < void > {
640670 const log = ( ...args : any [ ] ) => console . log ( `[${ artifact . name } ]` , ...args ) ;
641671 const match = / ^ v s c o d e _ (?< product > [ ^ _ ] + ) _ (?< os > [ ^ _ ] + ) (?: _ l e g a c y ) ? _ (?< arch > [ ^ _ ] + ) _ (?< unprocessedType > [ ^ _ ] + ) $ / . exec ( artifact . name ) ;
642672
@@ -674,8 +704,7 @@ async function processArtifact(artifact: Artifact, artifactFilePath: string): Pr
674704
675705 await retry ( async ( attempt ) => {
676706 log ( `Creating asset in Cosmos DB (attempt ${ attempt } )...` ) ;
677- const aadCredentials = new ClientAssertionCredential ( process . env [ 'AZURE_TENANT_ID' ] ! , process . env [ 'AZURE_CLIENT_ID' ] ! , ( ) => Promise . resolve ( process . env [ 'AZURE_ID_TOKEN' ] ! ) ) ;
678- const client = new CosmosClient ( { endpoint : e ( 'AZURE_DOCUMENTDB_ENDPOINT' ) , aadCredentials } ) ;
707+ const client = new CosmosClient ( { endpoint : e ( 'AZURE_DOCUMENTDB_ENDPOINT' ) ! , tokenProvider : ( ) => Promise . resolve ( `type=aad&ver=1.0&sig=${ cosmosDBAccessToken } ` ) } ) ;
679708 const scripts = client . database ( 'builds' ) . container ( quality ) . scripts ;
680709 await scripts . storedProcedure ( 'createAsset' ) . execute ( '' , [ commit , asset , true ] ) ;
681710 } ) ;
@@ -691,8 +720,8 @@ async function processArtifact(artifact: Artifact, artifactFilePath: string): Pr
691720// the CDN and finally update the build in Cosmos DB.
692721async function main ( ) {
693722 if ( ! isMainThread ) {
694- const { artifact, artifactFilePath } = workerData ;
695- await processArtifact ( artifact , artifactFilePath ) ;
723+ const { artifact, artifactFilePath, cosmosDBAccessToken } = workerData ;
724+ await processArtifact ( artifact , artifactFilePath , cosmosDBAccessToken ) ;
696725 return ;
697726 }
698727
@@ -713,6 +742,7 @@ async function main() {
713742
714743 let resultPromise = Promise . resolve < PromiseSettledResult < void > [ ] > ( [ ] ) ;
715744 const operations : { name : string ; operation : Promise < void > } [ ] = [ ] ;
745+ const cosmosDBAccessToken = await getAccessToken ( e ( 'AZURE_DOCUMENTDB_ENDPOINT' ) ! , e ( 'AZURE_TENANT_ID' ) ! , e ( 'AZURE_CLIENT_ID' ) ! , e ( 'AZURE_ID_TOKEN' ) ! ) ;
716746
717747 while ( true ) {
718748 const [ timeline , artifacts ] = await Promise . all ( [ retry ( ( ) => getPipelineTimeline ( ) ) , retry ( ( ) => getPipelineArtifacts ( ) ) ] ) ;
@@ -754,7 +784,7 @@ async function main() {
754784
755785 processing . add ( artifact . name ) ;
756786 const promise = new Promise < void > ( ( resolve , reject ) => {
757- const worker = new Worker ( __filename , { workerData : { artifact, artifactFilePath } } ) ;
787+ const worker = new Worker ( __filename , { workerData : { artifact, artifactFilePath, cosmosDBAccessToken } } ) ;
758788 worker . on ( 'error' , reject ) ;
759789 worker . on ( 'exit' , code => {
760790 if ( code === 0 ) {
0 commit comments