Skip to content

Commit f0a0037

Browse files
authored
use longer lived access tokens for cosmosdb auth (#233255)
1 parent 818169a commit f0a0037

File tree

3 files changed

+71
-14
lines changed

3 files changed

+71
-14
lines changed

build/azure-pipelines/common/publish.js

Lines changed: 33 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build/azure-pipelines/common/publish.ts

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import * as yauzl from 'yauzl';
1212
import * as crypto from 'crypto';
1313
import { retry } from './retry';
1414
import { CosmosClient } from '@azure/cosmos';
15-
import { ClientSecretCredential, ClientAssertionCredential } from '@azure/identity';
15+
import { ClientSecretCredential } from '@azure/identity';
1616
import * as cp from 'child_process';
1717
import * as os from 'os';
1818
import { 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+
5080
interface 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 = /^vscode_(?<product>[^_]+)_(?<os>[^_]+)(?:_legacy)?_(?<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.
692721
async 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) {

build/azure-pipelines/product-build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ variables:
143143
- name: PRSS_PROVISION_TENANT_ID
144144
value: 72f988bf-86f1-41af-91ab-2d7cd011db47
145145
- name: AZURE_DOCUMENTDB_ENDPOINT
146-
value: https://vscode.documents.azure.com:443/
146+
value: https://vscode.documents.azure.com/
147147
- name: VSCODE_MIXIN_REPO
148148
value: microsoft/vscode-distro
149149
- name: skipComponentGovernanceDetection

0 commit comments

Comments
 (0)