@@ -12,7 +12,7 @@ import * as yauzl from 'yauzl';
12
12
import * as crypto from 'crypto' ;
13
13
import { retry } from './retry' ;
14
14
import { CosmosClient } from '@azure/cosmos' ;
15
- import { ClientSecretCredential , ClientAssertionCredential } from '@azure/identity' ;
15
+ import { ClientSecretCredential } from '@azure/identity' ;
16
16
import * as cp from 'child_process' ;
17
17
import * as os from 'os' ;
18
18
import { Worker , isMainThread , workerData } from 'node:worker_threads' ;
@@ -47,6 +47,36 @@ class Temp {
47
47
}
48
48
}
49
49
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
+
50
80
interface RequestOptions {
51
81
readonly body ?: string ;
52
82
}
@@ -636,7 +666,7 @@ function getRealType(type: string) {
636
666
}
637
667
}
638
668
639
- async function processArtifact ( artifact : Artifact , artifactFilePath : string ) : Promise < void > {
669
+ async function processArtifact ( artifact : Artifact , artifactFilePath : string , cosmosDBAccessToken : string ) : Promise < void > {
640
670
const log = ( ...args : any [ ] ) => console . log ( `[${ artifact . name } ]` , ...args ) ;
641
671
const match = / ^ v s c o d e _ (?< product > [ ^ _ ] + ) _ (?< os > [ ^ _ ] + ) (?: _ l e g a c y ) ? _ (?< arch > [ ^ _ ] + ) _ (?< unprocessedType > [ ^ _ ] + ) $ / . exec ( artifact . name ) ;
642
672
@@ -674,8 +704,7 @@ async function processArtifact(artifact: Artifact, artifactFilePath: string): Pr
674
704
675
705
await retry ( async ( attempt ) => {
676
706
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 } ` ) } ) ;
679
708
const scripts = client . database ( 'builds' ) . container ( quality ) . scripts ;
680
709
await scripts . storedProcedure ( 'createAsset' ) . execute ( '' , [ commit , asset , true ] ) ;
681
710
} ) ;
@@ -691,8 +720,8 @@ async function processArtifact(artifact: Artifact, artifactFilePath: string): Pr
691
720
// the CDN and finally update the build in Cosmos DB.
692
721
async function main ( ) {
693
722
if ( ! isMainThread ) {
694
- const { artifact, artifactFilePath } = workerData ;
695
- await processArtifact ( artifact , artifactFilePath ) ;
723
+ const { artifact, artifactFilePath, cosmosDBAccessToken } = workerData ;
724
+ await processArtifact ( artifact , artifactFilePath , cosmosDBAccessToken ) ;
696
725
return ;
697
726
}
698
727
@@ -713,6 +742,7 @@ async function main() {
713
742
714
743
let resultPromise = Promise . resolve < PromiseSettledResult < void > [ ] > ( [ ] ) ;
715
744
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' ) ! ) ;
716
746
717
747
while ( true ) {
718
748
const [ timeline , artifacts ] = await Promise . all ( [ retry ( ( ) => getPipelineTimeline ( ) ) , retry ( ( ) => getPipelineArtifacts ( ) ) ] ) ;
@@ -754,7 +784,7 @@ async function main() {
754
784
755
785
processing . add ( artifact . name ) ;
756
786
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 } } ) ;
758
788
worker . on ( 'error' , reject ) ;
759
789
worker . on ( 'exit' , code => {
760
790
if ( code === 0 ) {
0 commit comments