@@ -71,6 +71,9 @@ describe('Auth', () => {
71
71
const identityTenantId = '9bc3ab49-b65d-410a-85ad-de819febfddd' ;
72
72
const appId = '9bc3ab49-b65d-410a-85ad-de819febfddc' ;
73
73
const tenant = '9bc3ab49-b65d-410a-85ad-de819febfddd' ;
74
+ const serviceConnectionId = 'cf54610a-5868-477c-b766-1ac6bd2ef63c' ;
75
+ const serviceConnectionAppId = 'b5882c53-5488-4a65-9bdd-4c1754e7e8dd' ;
76
+ const serviceConnectionTenantId = 'b11abebe-6eab-4e99-9571-b6f5433f9d5d' ;
74
77
const federatedIdentityAudience = 'api://AzureADTokenExchange' ;
75
78
const activeConnection : Connection = { name : identityId , identityId, identityName, active : true , appId, tenant, authType : AuthType . DeviceCode , certificateType : CertificateType . Unknown , accessTokens : { } , cloudType : CloudType . Public , identityTenantId : identityTenantId , deactivate : ( ) => { } } ;
76
79
const base64EncodedPemCert = 'QmFnIEF0dHJpYnV0ZXMNCiAgICBsb2NhbEtleUlEOiBDQyBGNCBGMiBBMyBDMyBEMiAwOSBDNSAxMiBCMyA3MiA0QiBCOCA4MyBBNSA0NyA0QyAwOSAyMSBEQyANCnN1YmplY3Q9QyA9IEFVLCBTVCA9IFNvbWUtU3RhdGUsIE8gPSBJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQNCg0KaXNzdWVyPUMgPSBBVSwgU1QgPSBTb21lLVN0YXRlLCBPID0gSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkDQoNCi0tLS0tQkVHSU4gQ0VSVElGSUNBVEUtLS0tLQ0KTUlJRGF6Q0NBbE9nQXdJQkFnSVVXb25VNFM0RTcxRjVZMU5zU0xYbUlhZ1dkNVl3RFFZSktvWklodmNOQVFFTA0KQlFBd1JURUxNQWtHQTFVRUJoTUNRVlV4RXpBUkJnTlZCQWdNQ2xOdmJXVXRVM1JoZEdVeElUQWZCZ05WQkFvTQ0KR0VsdWRHVnlibVYwSUZkcFpHZHBkSE1nVUhSNUlFeDBaREFlRncweE9UQTNNVEl5TVRVek1qbGFGdzB5TURBMw0KTVRFeU1UVXpNamxhTUVVeEN6QUpCZ05WQkFZVEFrRlZNUk13RVFZRFZRUUlEQXBUYjIxbExWTjBZWFJsTVNFdw0KSHdZRFZRUUtEQmhKYm5SbGNtNWxkQ0JYYVdSbmFYUnpJRkIwZVNCTWRHUXdnZ0VpTUEwR0NTcUdTSWIzRFFFQg0KQVFVQUE0SUJEd0F3Z2dFS0FvSUJBUUNsa01lQXlKbTJkMy95aEV0NHZGYjYrYjEyUGxRSDB4VGx1a1BoK2xScg0KOXJDNk5DM3dObnoySm5vbE1HclhuZVp2TlN5czFONVpSTm0yTjhQdy9QOExxeHJSenFFOFBNVC96NnN1UFhSUg0KWm5hZ2xaUklXb0NNR25pRVlDZVJHZnI4R2JpUXcwYlZEeXFuSnJaZjByS0pHbnZUNlY3QmpUdFloRWIzeXhoNA0KSmNUSnIrVDl0OEFYaldmemt6alBZdklxYmhha3FxcHd1SEVPYkh4T201cHVERTFBNVJOZm8wamcrTmZtVko5VQ0KMWR1RjVzdmE2NVQ5Q1RtdEdlbVNlUGlzWmgxZmhoOS94QmJwTCs0RUJWUXZqdEZXWk5zMVJHMW9QUllscmpzaQ0KTXFsaHNUdjhDZXI5cWUxcVNTdHFjMmJsc3hGek1zNmxZOHAvUHIrYm5uR3pBZ01CQUFHalV6QlJNQjBHQTFVZA0KRGdRV0JCU203cWFreXQwY2xxN0lnRFRWdkUrWEpaNFU5akFmQmdOVkhTTUVHREFXZ0JTbTdxYWt5dDBjbHE3SQ0KZ0RUVnZFK1hKWjRVOWpBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUEwR0NTcUdTSWIzRFFFQkN3VUFBNElCQVFBYQ0KQnVqTytveU0yL0Q0SzNpS3lqVDVzbHF2UFVlVzFrZVVXYVdSVDZXRTY0VkFPbTlPZzU1bkIyOE5TSVVXampXMA0KdTJEUHF3SzJiOEFXalEveWp3S3NUMXVTdzcyQ0VEY2o3SkE1VXA5UWpBa0hIZmFoQWtOd0o5M0llcmFBdTEyVQ0KN25FRDdIN20yeGZscDVwM0dadzNHUE0rZmpBaDZLOUZIRDI0bWdGUTh4b2JPQSttVEVvV2ZIVVQrZ1pUMGxYdQ0KazFrVTJVelVOd2dwc3c4V04wNFFzWU5XcFF5d3ppUWtuZTQzNW5tdmxZOGZRc2hPSnErK0JCS0thd0xEcjk3bA0KRTBYQUxEZDZlVVhQenZ5OU1xZlozeUswRmUzMy8zbnZnUnE4QWZ3azRsbzhac2ZYWUlSTXA3b3BER0VmaUZmNQ0KM3JTTGxSZG9TNDQ4OVFZRnAyYUQNCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0NCkJhZyBBdHRyaWJ1dGVzDQogICAgbG9jYWxLZXlJRDogQ0MgRjQgRjIgQTMgQzMgRDIgMDkgQzUgMTIgQjMgNzIgNEIgQjggODMgQTUgNDcgNEMgMDkgMjEgREMgDQpLZXkgQXR0cmlidXRlczogPE5vIEF0dHJpYnV0ZXM+DQotLS0tLUJFR0lOIFBSSVZBVEUgS0VZLS0tLS0NCk1JSUV2Z0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktnd2dnU2tBZ0VBQW9JQkFRQ2xrTWVBeUptMmQzL3kNCmhFdDR2RmI2K2IxMlBsUUgweFRsdWtQaCtsUnI5ckM2TkMzd05uejJKbm9sTUdyWG5lWnZOU3lzMU41WlJObTINCk44UHcvUDhMcXhyUnpxRThQTVQvejZzdVBYUlJabmFnbFpSSVdvQ01HbmlFWUNlUkdmcjhHYmlRdzBiVkR5cW4NCkpyWmYwcktKR252VDZWN0JqVHRZaEViM3l4aDRKY1RKcitUOXQ4QVhqV2Z6a3pqUFl2SXFiaGFrcXFwd3VIRU8NCmJIeE9tNXB1REUxQTVSTmZvMGpnK05mbVZKOVUxZHVGNXN2YTY1VDlDVG10R2VtU2VQaXNaaDFmaGg5L3hCYnANCkwrNEVCVlF2anRGV1pOczFSRzFvUFJZbHJqc2lNcWxoc1R2OENlcjlxZTFxU1N0cWMyYmxzeEZ6TXM2bFk4cC8NClByK2Jubkd6QWdNQkFBRUNnZ0VBUjRsMytqZ3kybmxseWtiSlNXQ3ZnSCs2RWtZNkRxdHd3eFlwVUpIV09sUDcNCjVtaTNWS3htY0FFT0U5V0l4S05RTnNyV0E5TnlRMFlSZjc4MnBZRGJQcEp1NHlxUjFqSTN1SVJsWlhSZU52RzcNCjNnVGpiaVBVbVRTeTBCZXY0TzFGMmZuUEdwV1ZuR2VTT1dqcnNobWExTXlocGwyV2VMRHFiSU96R2t3aHhYOXkNClRhRFd5MjErbDFpNVNGWUZTdHdXOWlhOXRORTFTTTU4WnpQWk0yK0NDdHhQVEFBQXRJRmZXUVdTbnhodUxMenMNCjNyVDRVOGNLZzJITVBXb29rOS9peWxsa0xEVXBPanhJR2tHWXdheDVnR2xvR0xZYWVoelc5Q3hobzgvc3A4WjUNCkVNNVFvczVJSTF2K21pNHhHa0RTdW4rbDYzcDN5Nm54T3pqM1h1MzRlUUtCZ1FEUDNtRWttN2lVaTlhRUxweXYNCkIxeDFlRFR2UmEwcllZMHZUaXFrYzhyUGc0NU1uOUNWRWZqdnV3YkN4M21tTExabThqZVY3ZTFHWjZJeXYreEUNCmcxeFkrUTd0RUlCb1FwWThlemg0UVYvMXRkZkhiUzNPcGdIbHVqMGd5MWxqT2QrbkxzS2RNQWRlYVF3Uy9WK2MNCk51Sks0Y3oyQWl6UXU1dHQ4WHdoOGdvU0Z3S0JnUURMNXRjZnF0VmdMQWJmMnJQbEhBLzdNcU1sWGpqNUQ0ejkNCjZmTWlCVDdOWHlYUGx6a2pJQkxOdG9OWlBCVTFzeERFb2tiNUtyTlhLTUtIaU9nTkQ0cWtDYkdnRFk2WUdaS3cNCkg4bDlLWDBaM2pwcEp0TURvQ21yQW9hSmZTUXNreGJXSDd4VlFGVzdPVWQ0dHMxZ3FDbTBUTFVxeW9lcW1EK3INCmg3WFlaa2RxeFFLQmdBK2NpZnN2M3NyNVBhRXJ4d1MyTHRGN3Q2NElzNXJBZHRRSXNOY3RBeHhXcXdkQ01XNGcNCnJXdUR4bHcya3dKUjlWa0I4LzdFb2I5WjVTcWVrMllKMzVPbkVPSHBEVnZITkhWU1k4bFVUNXFxajR3Z3ZRSDYNCkljWlpHR0l3STRSNlFqdlNIVGVrOWNpM1p2cStJTUlndFJvZW4wQVNwYjcvZUFybnlnVGFvcnI5QW9HQkFJT3QNCllOSEhqaUtjYkJnV2NjU01tZGw4T3hXL3dvVTlRSzBkYjNGUjk5dkREWFVCVU5uWk5hdDVxVnR3VExZd0hLMFANCnEwdndBbjlRQ0VoazVvN0FzYVQ3eWFUMS9GZEhkSTZmQ0l6MnhSNTJnRHcxNFdIZkJlbTFLTk1UYU5BTWNWdjQNCmhMUjlacUFRL3BIN1k2aC9FT2VwL2ZsVGI4ZUFxT1dLTDZvL2F2R05Bb0dCQUlHc0c1VExuSmlPU044SUtGU04NCmJmK3IrNkhWL2R6MkluNjhSR255MTB0OGpwbUpPbGgrdXRncGtvOXI2Y09uWGY4VHM2SFAveTBtbDl5YXhvMlANCm52c2wwcFlseFQxQy9taXJaZWxYKzFaQTltdFpHT2RxbzZhdVZUM1drcXBpb3c2WUtzbzl2Z2RHWmRWRUxiMEINCnUvdyt4UjBvN21aSEpwVEdmS09KdE53MQ0KLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLQ0K' ;
@@ -129,6 +132,7 @@ describe('Auth', () => {
129
132
sinonUtil . restore ( [
130
133
cli . getConfig ( ) . get ,
131
134
request . get ,
135
+ request . post ,
132
136
( auth as any ) . getClientApplication ,
133
137
( auth as any ) . getDeviceCodeResponse ,
134
138
( auth as any ) . storeConnectionInfo ,
@@ -1404,12 +1408,8 @@ describe('Auth', () => {
1404
1408
if ( opts . url === `https://login.microsoftonline.com/${ tenant } /oauth2/v2.0/token` ) {
1405
1409
return {
1406
1410
"access_token" : accessToken ,
1407
- "client_id" : "a04566df-9a65-4e90-ae3d-574572a16423" ,
1408
- "expires_in" : "86399" ,
1409
- "expires_on" : "1587847593" ,
1410
- "ext_expires_in" : "86399" ,
1411
- "not_before" : "1587760893" ,
1412
- "resource" : "https://contoso.sharepoint.com/" ,
1411
+ "expires_in" : "3599" ,
1412
+ "ext_expires_in" : "3599" ,
1413
1413
"token_type" : "Bearer"
1414
1414
} ;
1415
1415
}
@@ -1430,17 +1430,228 @@ describe('Auth', () => {
1430
1430
assert . strictEqual ( requestPostStub . firstCall . args [ 0 ] . data . indexOf ( `client_assertion=${ federatedIdentityClientAssertion } ` ) > - 1 , true ) ;
1431
1431
} ) ;
1432
1432
1433
- it ( 'fails when not using federated identity from GitHub Action' , async ( ) => {
1433
+ it ( 'calls api with correct params using federated identity flow from Azure DevOps Pipeline without Service Connection' , async ( ) => {
1434
+ process . env = {
1435
+ SYSTEM_OIDCREQUESTURI : 'https://contoso.visualstudio.com/0d0a69da-77db-472d-9e84-8beb80d4de42/_apis/distributedtask/hubs/build/plans/dec90b47-fb8f-4dad-8ef3-4e06ae1ca4ab/jobs/037420a7-9ab4-4003-8520-087b4b4a80b7/oidctoken' ,
1436
+ SYSTEM_ACCESSTOKEN : 'eyJ0eXAiOiJKV1QiLCJ-DevOpsFederationToken-...'
1437
+ } ;
1438
+ const federatedIdentityClientAssertion = 'eyJ0eXAiOiJKV1QiLCJ-DevOpsFederationClientAssertion-...' ;
1439
+ const accessToken = 'eyJ0eXAiOiJKV1QiLCJ-EntraIDAccessToken...' ;
1440
+
1441
+ const requestPostStub = sinon . stub ( request , 'post' ) . callsFake ( async ( opts ) => {
1442
+ if ( opts . url === `${ process . env . SYSTEM_OIDCREQUESTURI } ?api-version=7.1` ) {
1443
+ return {
1444
+ "oidcToken" : federatedIdentityClientAssertion
1445
+ } ;
1446
+ }
1447
+ else if ( opts . url === `https://login.microsoftonline.com/${ tenant } /oauth2/v2.0/token` ) {
1448
+ return {
1449
+ "access_token" : accessToken ,
1450
+ "expires_in" : "3599" ,
1451
+ "ext_expires_in" : "3599" ,
1452
+ "token_type" : "Bearer"
1453
+ } ;
1454
+ }
1455
+
1456
+ throw { error : { "error" : "Invalid POST Request" } } ;
1457
+ } ) ;
1458
+
1459
+ auth . connection . authType = AuthType . FederatedIdentity ;
1460
+ auth . connection . appId = appId ;
1461
+ auth . connection . tenant = tenant ;
1462
+
1463
+ const ensuredAccessToken = await auth . ensureAccessToken ( resource , logger , true ) ;
1464
+ assert . strictEqual ( ensuredAccessToken , accessToken ) ;
1465
+ assert . strictEqual ( requestPostStub . firstCall . args [ 0 ] . headers ?. Authorization , `Bearer ${ process . env . SYSTEM_ACCESSTOKEN } ` ) ;
1466
+ assert ( requestPostStub . calledTwice ) ;
1467
+ assert . strictEqual ( requestPostStub . secondCall . args [ 0 ] . data . indexOf ( `client_id=${ appId } ` ) > - 1 , true ) ;
1468
+ assert . strictEqual ( requestPostStub . secondCall . args [ 0 ] . data . indexOf ( `client_assertion=${ federatedIdentityClientAssertion } ` ) > - 1 , true ) ;
1469
+ } ) ;
1470
+
1471
+ it ( 'calls api with correct params using federated identity flow from Azure DevOps Pipeline with Service Connection' , async ( ) => {
1472
+ process . env = {
1473
+ SYSTEM_OIDCREQUESTURI : 'https://contoso.visualstudio.com/0d0a69da-77db-472d-9e84-8beb80d4de42/_apis/distributedtask/hubs/build/plans/dec90b47-fb8f-4dad-8ef3-4e06ae1ca4ab/jobs/037420a7-9ab4-4003-8520-087b4b4a80b7/oidctoken' ,
1474
+ SYSTEM_ACCESSTOKEN : 'eyJ0eXAiOiJKV1QiLCJ-DevOpsFederationToken-...' ,
1475
+ AZURESUBSCRIPTION_SERVICE_CONNECTION_ID : serviceConnectionId ,
1476
+ AZURESUBSCRIPTION_CLIENT_ID : serviceConnectionAppId ,
1477
+ AZURESUBSCRIPTION_TENANT_ID : serviceConnectionTenantId
1478
+ } ;
1479
+ const federatedIdentityClientAssertion = 'eyJ0eXAiOiJKV1QiLCJ-DevOpsFederationClientAssertion-...' ;
1480
+ const accessToken = 'eyJ0eXAiOiJKV1QiLCJ-EntraIDAccessToken...' ;
1481
+
1482
+ const requestPostStub = sinon . stub ( request , 'post' ) . callsFake ( async ( opts ) => {
1483
+ if ( opts . url === `${ process . env . SYSTEM_OIDCREQUESTURI } ?api-version=7.1&serviceConnectionId=${ serviceConnectionId } ` ) {
1484
+ return {
1485
+ "oidcToken" : federatedIdentityClientAssertion
1486
+ } ;
1487
+ }
1488
+ else if ( opts . url === `https://login.microsoftonline.com/${ serviceConnectionTenantId } /oauth2/v2.0/token` ) {
1489
+ return {
1490
+ "access_token" : accessToken ,
1491
+ "expires_in" : "3599" ,
1492
+ "ext_expires_in" : "3599" ,
1493
+ "token_type" : "Bearer"
1494
+ } ;
1495
+ }
1496
+
1497
+ throw { error : { "error" : "Invalid POST Request" } } ;
1498
+ } ) ;
1499
+
1500
+ auth . connection . authType = AuthType . FederatedIdentity ;
1501
+
1502
+ const ensuredAccessToken = await auth . ensureAccessToken ( resource , logger , true ) ;
1503
+ assert . strictEqual ( ensuredAccessToken , accessToken ) ;
1504
+ assert . strictEqual ( requestPostStub . firstCall . args [ 0 ] . headers ?. Authorization , `Bearer ${ process . env . SYSTEM_ACCESSTOKEN } ` ) ;
1505
+ assert ( requestPostStub . calledTwice ) ;
1506
+ assert . strictEqual ( requestPostStub . secondCall . args [ 0 ] . data . indexOf ( `client_id=${ serviceConnectionAppId } ` ) > - 1 , true ) ;
1507
+ assert . strictEqual ( requestPostStub . secondCall . args [ 0 ] . data . indexOf ( `client_assertion=${ federatedIdentityClientAssertion } ` ) > - 1 , true ) ;
1508
+ } ) ;
1509
+
1510
+ it ( 'calls api with correct params using federated identity flow from Azure DevOps Pipeline with Service Connection, overriding appId option' , async ( ) => {
1511
+ process . env = {
1512
+ SYSTEM_OIDCREQUESTURI : 'https://contoso.visualstudio.com/0d0a69da-77db-472d-9e84-8beb80d4de42/_apis/distributedtask/hubs/build/plans/dec90b47-fb8f-4dad-8ef3-4e06ae1ca4ab/jobs/037420a7-9ab4-4003-8520-087b4b4a80b7/oidctoken' ,
1513
+ SYSTEM_ACCESSTOKEN : 'eyJ0eXAiOiJKV1QiLCJ-DevOpsFederationToken-...' ,
1514
+ AZURESUBSCRIPTION_SERVICE_CONNECTION_ID : serviceConnectionId ,
1515
+ AZURESUBSCRIPTION_CLIENT_ID : serviceConnectionAppId ,
1516
+ AZURESUBSCRIPTION_TENANT_ID : serviceConnectionTenantId
1517
+ } ;
1518
+ const federatedIdentityClientAssertion = 'eyJ0eXAiOiJKV1QiLCJ-DevOpsFederationClientAssertion-...' ;
1519
+ const accessToken = 'eyJ0eXAiOiJKV1QiLCJ-EntraIDAccessToken...' ;
1520
+
1521
+ const requestPostStub = sinon . stub ( request , 'post' ) . callsFake ( async ( opts ) => {
1522
+ if ( opts . url === `${ process . env . SYSTEM_OIDCREQUESTURI } ?api-version=7.1&serviceConnectionId=${ serviceConnectionId } ` ) {
1523
+ return {
1524
+ "oidcToken" : federatedIdentityClientAssertion
1525
+ } ;
1526
+ }
1527
+ else if ( opts . url === `https://login.microsoftonline.com/${ serviceConnectionTenantId } /oauth2/v2.0/token` ) {
1528
+ return {
1529
+ "access_token" : accessToken ,
1530
+ "expires_in" : "3599" ,
1531
+ "ext_expires_in" : "3599" ,
1532
+ "token_type" : "Bearer"
1533
+ } ;
1534
+ }
1535
+
1536
+ throw { error : { "error" : "Invalid POST Request" } } ;
1537
+ } ) ;
1538
+
1539
+ auth . connection . authType = AuthType . FederatedIdentity ;
1540
+ auth . connection . appId = appId ;
1541
+
1542
+ const ensuredAccessToken = await auth . ensureAccessToken ( resource , logger , true ) ;
1543
+ assert . strictEqual ( ensuredAccessToken , accessToken ) ;
1544
+ assert . strictEqual ( requestPostStub . firstCall . args [ 0 ] . headers ?. Authorization , `Bearer ${ process . env . SYSTEM_ACCESSTOKEN } ` ) ;
1545
+ assert ( requestPostStub . calledTwice ) ;
1546
+ assert . strictEqual ( requestPostStub . secondCall . args [ 0 ] . data . indexOf ( `client_id=${ serviceConnectionAppId } ` ) > - 1 , true ) ;
1547
+ assert . strictEqual ( requestPostStub . secondCall . args [ 0 ] . data . indexOf ( `client_assertion=${ federatedIdentityClientAssertion } ` ) > - 1 , true ) ;
1548
+ } ) ;
1549
+
1550
+ it ( 'calls api with correct params using federated identity flow from Azure DevOps Pipeline with Service Connection, overriding tenant option' , async ( ) => {
1551
+ process . env = {
1552
+ SYSTEM_OIDCREQUESTURI : 'https://contoso.visualstudio.com/0d0a69da-77db-472d-9e84-8beb80d4de42/_apis/distributedtask/hubs/build/plans/dec90b47-fb8f-4dad-8ef3-4e06ae1ca4ab/jobs/037420a7-9ab4-4003-8520-087b4b4a80b7/oidctoken' ,
1553
+ SYSTEM_ACCESSTOKEN : 'eyJ0eXAiOiJKV1QiLCJ-DevOpsFederationToken-...' ,
1554
+ AZURESUBSCRIPTION_SERVICE_CONNECTION_ID : serviceConnectionId ,
1555
+ AZURESUBSCRIPTION_CLIENT_ID : serviceConnectionAppId ,
1556
+ AZURESUBSCRIPTION_TENANT_ID : serviceConnectionTenantId
1557
+ } ;
1558
+ const federatedIdentityClientAssertion = 'eyJ0eXAiOiJKV1QiLCJ-DevOpsFederationClientAssertion-...' ;
1559
+ const accessToken = 'eyJ0eXAiOiJKV1QiLCJ-EntraIDAccessToken...' ;
1560
+
1561
+ const requestPostStub = sinon . stub ( request , 'post' ) . callsFake ( async ( opts ) => {
1562
+ if ( opts . url === `${ process . env . SYSTEM_OIDCREQUESTURI } ?api-version=7.1&serviceConnectionId=${ serviceConnectionId } ` ) {
1563
+ return {
1564
+ "oidcToken" : federatedIdentityClientAssertion
1565
+ } ;
1566
+ }
1567
+ else if ( opts . url === `https://login.microsoftonline.com/${ serviceConnectionTenantId } /oauth2/v2.0/token` ) {
1568
+ return {
1569
+ "access_token" : accessToken ,
1570
+ "expires_in" : "3599" ,
1571
+ "ext_expires_in" : "3599" ,
1572
+ "token_type" : "Bearer"
1573
+ } ;
1574
+ }
1575
+
1576
+ throw { error : { "error" : "Invalid POST Request" } } ;
1577
+ } ) ;
1578
+
1579
+ auth . connection . authType = AuthType . FederatedIdentity ;
1580
+ auth . connection . appId = '' ;
1581
+ auth . connection . tenant = tenant ;
1582
+
1583
+ const ensuredAccessToken = await auth . ensureAccessToken ( resource , logger , true ) ;
1584
+ assert . strictEqual ( ensuredAccessToken , accessToken ) ;
1585
+ assert . strictEqual ( requestPostStub . firstCall . args [ 0 ] . headers ?. Authorization , `Bearer ${ process . env . SYSTEM_ACCESSTOKEN } ` ) ;
1586
+ assert ( requestPostStub . calledTwice ) ;
1587
+ assert . strictEqual ( requestPostStub . secondCall . args [ 0 ] . data . indexOf ( `client_id=${ serviceConnectionAppId } ` ) > - 1 , true ) ;
1588
+ assert . strictEqual ( requestPostStub . secondCall . args [ 0 ] . data . indexOf ( `client_assertion=${ federatedIdentityClientAssertion } ` ) > - 1 , true ) ;
1589
+ } ) ;
1590
+
1591
+ it ( 'fails when not using federated identity from GitHub Action or Azure DevOps Pipeline' , async ( ) => {
1434
1592
process . env = {
1435
1593
ACTIONS_ID_TOKEN_REQUEST_URL : '' ,
1436
- ACTIONS_ID_TOKEN_REQUEST_TOKEN : ''
1594
+ ACTIONS_ID_TOKEN_REQUEST_TOKEN : '' ,
1595
+ SYSTEM_OIDCREQUESTURI : '' ,
1596
+ SYSTEM_ACCESSTOKEN : ''
1437
1597
} ;
1438
1598
1439
1599
auth . connection . authType = AuthType . FederatedIdentity ;
1440
1600
auth . connection . appId = appId ;
1441
1601
auth . connection . tenant = tenant ;
1442
1602
1443
- await assert . rejects ( auth . ensureAccessToken ( resource , logger , true ) , new CommandError ( 'Federated identity is currently only supported in GitHub Actions.' ) ) ;
1603
+ await assert . rejects ( auth . ensureAccessToken ( resource , logger , true ) , new CommandError ( 'Federated identity is currently only supported in GitHub Actions and Azure DevOps.' ) ) ;
1604
+ } ) ;
1605
+
1606
+ it ( 'fails when using federated identity from Azure DevOps Pipeline, but SYSTEM_ACCESSTOKEN not available' , async ( ) => {
1607
+ process . env = {
1608
+ SYSTEM_OIDCREQUESTURI : 'https://contoso.visualstudio.com/0d0a69da-77db-472d-9e84-8beb80d4de42/_apis/distributedtask/hubs/build/plans/dec90b47-fb8f-4dad-8ef3-4e06ae1ca4ab/jobs/037420a7-9ab4-4003-8520-087b4b4a80b7/oidctoken' ,
1609
+ SYSTEM_ACCESSTOKEN : ''
1610
+ } ;
1611
+
1612
+ auth . connection . authType = AuthType . FederatedIdentity ;
1613
+ auth . connection . appId = appId ;
1614
+ auth . connection . tenant = tenant ;
1615
+
1616
+ await assert . rejects ( auth . ensureAccessToken ( resource , logger , true ) , new CommandError ( `The SYSTEM_ACCESSTOKEN environment variable is not available. Please check the Azure DevOps pipeline task configuration. It should contain 'SYSTEM_ACCESSTOKEN: $(System.AccessToken)' in the env section.` ) ) ;
1617
+ } ) ;
1618
+
1619
+ it ( 'fails when using federated identity from Azure DevOps Pipeline without using service connection, with empty appId and tenant option' , async ( ) => {
1620
+ process . env = {
1621
+ SYSTEM_OIDCREQUESTURI : 'https://contoso.visualstudio.com/0d0a69da-77db-472d-9e84-8beb80d4de42/_apis/distributedtask/hubs/build/plans/dec90b47-fb8f-4dad-8ef3-4e06ae1ca4ab/jobs/037420a7-9ab4-4003-8520-087b4b4a80b7/oidctoken' ,
1622
+ SYSTEM_ACCESSTOKEN : 'eyJ0eXAiOiJKV1QiLCJ-DevOpsFederationToken-...'
1623
+ } ;
1624
+
1625
+ auth . connection . authType = AuthType . FederatedIdentity ;
1626
+ auth . connection . appId = undefined ;
1627
+ auth . connection . tenant = 'common' ;
1628
+
1629
+ await assert . rejects ( auth . ensureAccessToken ( resource , logger , true ) , new CommandError ( `The appId and tenant parameters are required when not using a service connection.` ) ) ;
1630
+ } ) ;
1631
+
1632
+ it ( 'fails when using federated identity from Azure DevOps Pipeline without using service connection, with empty appId option' , async ( ) => {
1633
+ process . env = {
1634
+ SYSTEM_OIDCREQUESTURI : 'https://contoso.visualstudio.com/0d0a69da-77db-472d-9e84-8beb80d4de42/_apis/distributedtask/hubs/build/plans/dec90b47-fb8f-4dad-8ef3-4e06ae1ca4ab/jobs/037420a7-9ab4-4003-8520-087b4b4a80b7/oidctoken' ,
1635
+ SYSTEM_ACCESSTOKEN : 'eyJ0eXAiOiJKV1QiLCJ-DevOpsFederationToken-...'
1636
+ } ;
1637
+
1638
+ auth . connection . authType = AuthType . FederatedIdentity ;
1639
+ auth . connection . tenant = 'common' ;
1640
+
1641
+ await assert . rejects ( auth . ensureAccessToken ( resource , logger , true ) , new CommandError ( `The appId and tenant parameters are required when not using a service connection.` ) ) ;
1642
+ } ) ;
1643
+
1644
+ it ( 'fails when using federated identity from Azure DevOps Pipeline without using service connection, with empty tenant option' , async ( ) => {
1645
+ process . env = {
1646
+ SYSTEM_OIDCREQUESTURI : 'https://contoso.visualstudio.com/0d0a69da-77db-472d-9e84-8beb80d4de42/_apis/distributedtask/hubs/build/plans/dec90b47-fb8f-4dad-8ef3-4e06ae1ca4ab/jobs/037420a7-9ab4-4003-8520-087b4b4a80b7/oidctoken' ,
1647
+ SYSTEM_ACCESSTOKEN : 'eyJ0eXAiOiJKV1QiLCJ-DevOpsFederationToken-...'
1648
+ } ;
1649
+
1650
+ auth . connection . authType = AuthType . FederatedIdentity ;
1651
+ auth . connection . appId = appId ;
1652
+ auth . connection . tenant = 'common' ;
1653
+
1654
+ await assert . rejects ( auth . ensureAccessToken ( resource , logger , true ) , new CommandError ( `The appId and tenant parameters are required when not using a service connection.` ) ) ;
1444
1655
} ) ;
1445
1656
1446
1657
it ( 'returns access token if persisting connection fails' , async ( ) => {
0 commit comments