Skip to content

Commit ab3850b

Browse files
kashish2508guptakashish
andauthored
chore(playwrighttesting): removed fallback authentication logic (Azure#46667)
* Removed fallback authentication logic and modified ValidateMptPAT. * updated FetchEntraIdAccessTokenAsync test * updated test * update --------- Co-authored-by: guptakashish <[email protected]>
1 parent d5ae4b1 commit ab3850b

File tree

6 files changed

+45
-38
lines changed

6 files changed

+45
-38
lines changed

sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Constants.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,8 @@ internal class Constants
199199
internal static readonly string s_invalid_mpt_pat_error = "The Access Token provided in the environment variable is invalid.";
200200
internal static readonly string s_expired_mpt_pat_error = "The Access Token you are using is expired. Create a new token.";
201201
internal static readonly string s_invalid_os_error = "Invalid operating system, supported values are 'linux' and 'windows'.";
202+
internal static readonly string s_workspace_mismatch_error = "The provided access token does not match the specified workspace URL. Please verify that both values are correct.";
203+
internal static readonly string s_invalid_service_endpoint_error_message = "The service endpoint provided is invalid. Please verify the endpoint URL and try again.";
202204

203205
internal static readonly string s_playwright_service_disable_scalable_execution_environment_variable = "PLAYWRIGHT_SERVICE_DISABLE_SCALABLE_EXECUTION";
204206
internal static readonly string s_playwright_service_reporting_url_environment_variable = "PLAYWRIGHT_SERVICE_REPORTING_URL";

sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/EntraLifecycle.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public EntraLifecycle(TokenCredential? tokenCredential = null, JsonWebTokenHandl
2424
SetEntraIdAccessTokenFromEnvironment();
2525
}
2626

27-
internal async Task<bool> FetchEntraIdAccessTokenAsync(CancellationToken cancellationToken = default)
27+
internal async Task FetchEntraIdAccessTokenAsync(CancellationToken cancellationToken = default)
2828
{
2929
try
3030
{
@@ -33,12 +33,12 @@ internal async Task<bool> FetchEntraIdAccessTokenAsync(CancellationToken cancell
3333
_entraIdAccessToken = accessToken.Token;
3434
_entraIdAccessTokenExpiry = accessToken.ExpiresOn.ToUnixTimeSeconds();
3535
Environment.SetEnvironmentVariable(ServiceEnvironmentVariable.PlaywrightServiceAccessToken, _entraIdAccessToken);
36-
return true;
36+
return;
3737
}
3838
catch (Exception ex)
3939
{
4040
Console.Error.WriteLine(ex);
41-
return false;
41+
throw new Exception(Constants.s_no_auth_error);
4242
}
4343
}
4444

sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/PlaywrightService.cs

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using Microsoft.IdentityModel.JsonWebTokens;
88
using System;
99
using System.Collections.Generic;
10+
using System.Linq;
1011
using System.Runtime.InteropServices;
1112
using System.Text.RegularExpressions;
1213
using System.Threading;
@@ -109,7 +110,7 @@ internal PlaywrightService(OSPlatform? os = null, string? runId = null, string?
109110
// 2. Not close to expiry
110111
if (!string.IsNullOrEmpty(_entraLifecycle!._entraIdAccessToken) && _entraLifecycle!.DoesEntraIdAccessTokenRequireRotation())
111112
{
112-
_ = await _entraLifecycle.FetchEntraIdAccessTokenAsync(cancellationToken).ConfigureAwait(false);
113+
await _entraLifecycle.FetchEntraIdAccessTokenAsync(cancellationToken).ConfigureAwait(false);
113114
}
114115
if (string.IsNullOrEmpty(GetAuthToken()))
115116
{
@@ -146,24 +147,15 @@ public async Task InitializeAsync(CancellationToken cancellationToken = default)
146147
Environment.SetEnvironmentVariable(ServiceEnvironmentVariable.PlaywrightServiceUri, null);
147148
}
148149
// If default auth mechanism is Access token and token is available in the environment variable, no need to setup rotation handler
149-
if (ServiceAuth == ServiceAuthType.AccessToken && !string.IsNullOrEmpty(GetAuthToken()))
150+
if (ServiceAuth == ServiceAuthType.AccessToken)
150151
{
151152
ValidateMptPAT();
152153
return;
153154
}
154-
var operationStatus = await _entraLifecycle!.FetchEntraIdAccessTokenAsync(cancellationToken).ConfigureAwait(false);
155-
if (!operationStatus)
156-
{
157-
if (!string.IsNullOrEmpty(GetAuthToken()))
158-
{
159-
ValidateMptPAT(); // throws exception if token is invalid
160-
}
161-
return; // no need to setup rotation handler. If token is not available, it will fallback to local browser launch
155+
await _entraLifecycle!.FetchEntraIdAccessTokenAsync(cancellationToken).ConfigureAwait(false);
156+
RotationTimer = new Timer(RotationHandlerAsync, null, TimeSpan.FromMinutes(Constants.s_entra_access_token_rotation_interval_period_in_minutes), TimeSpan.FromMinutes(Constants.s_entra_access_token_rotation_interval_period_in_minutes));
162157
}
163158

164-
RotationTimer = new Timer(RotationHandlerAsync, null, TimeSpan.FromMinutes(Constants.s_entra_access_token_rotation_interval_period_in_minutes), TimeSpan.FromMinutes(Constants.s_entra_access_token_rotation_interval_period_in_minutes));
165-
}
166-
167159
/// <summary>
168160
/// Cleans up the resources used to setup entra id authentication.
169161
/// </summary>
@@ -251,18 +243,20 @@ internal static void SetReportingUrlAndWorkspaceId()
251243

252244
private void ValidateMptPAT()
253245
{
254-
try
255-
{
256246
string authToken = GetAuthToken()!;
247+
if (string.IsNullOrEmpty(authToken))
248+
throw new Exception(Constants.s_no_auth_error);
257249
JsonWebToken jsonWebToken = _jsonWebTokenHandler!.ReadJsonWebToken(authToken) ?? throw new Exception(Constants.s_invalid_mpt_pat_error);
250+
var tokenaWorkspaceId = jsonWebToken.Claims.FirstOrDefault(c => c.Type == "aid")?.Value;
251+
Match match = Regex.Match(ServiceEndpoint, @"wss://(?<region>[\w-]+)\.api\.(?<domain>playwright(?:-test|-int)?\.io|playwright\.microsoft\.com)/accounts/(?<workspaceId>[\w-]+)/");
252+
if (!match.Success)
253+
throw new Exception(Constants.s_invalid_service_endpoint_error_message);
254+
var serviceEndpointWorkspaceId = match.Groups["workspaceId"].Value;
255+
if (tokenaWorkspaceId != serviceEndpointWorkspaceId)
256+
throw new Exception(Constants.s_workspace_mismatch_error);
258257
var expiry = (long)(jsonWebToken.ValidTo - new DateTime(1970, 1, 1)).TotalSeconds;
259258
if (expiry <= DateTimeOffset.UtcNow.ToUnixTimeSeconds())
260259
throw new Exception(Constants.s_expired_mpt_pat_error);
261-
}
262-
catch (Exception)
263-
{
264-
throw new Exception(Constants.s_invalid_mpt_pat_error);
265-
}
266260
}
267261

268262
private string? getServiceCompatibleOs(OSPlatform? oSPlatform)

sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/PlaywrightServiceOptions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33

4+
using System;
45
using System.Runtime.InteropServices;
56
using Azure.Core;
67
using Azure.Identity;

sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/tests/EntraLifecycleTests.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ public async Task FetchEntraIdAccessTokenAsync_WhenTokenIsFetched_SetsTokenAndEx
132132
}
133133

134134
[Test]
135-
public async Task FetchEntraIdAccessTokenAsync_WhenTokenIsFetched_ReturnsTrue()
135+
public async Task FetchEntraIdAccessTokenAsync_WhenTokenIsFetched_ReturnVoid()
136136
{
137137
var defaultAzureCredentialMock = new Mock<DefaultAzureCredential>();
138138
var token = "valid_token";
@@ -141,20 +141,23 @@ public async Task FetchEntraIdAccessTokenAsync_WhenTokenIsFetched_ReturnsTrue()
141141
.Setup(x => x.GetTokenAsync(It.IsAny<TokenRequestContext>(), It.IsAny<CancellationToken>()))
142142
.ReturnsAsync(new AccessToken(token, expiry));
143143
EntraLifecycle entraLifecycle = new(defaultAzureCredentialMock.Object);
144-
Assert.That(await entraLifecycle.FetchEntraIdAccessTokenAsync(), Is.True);
144+
await entraLifecycle.FetchEntraIdAccessTokenAsync();
145145

146146
Environment.SetEnvironmentVariable(ServiceEnvironmentVariable.PlaywrightServiceAccessToken, null);
147147
}
148148

149149
[Test]
150-
public async Task FetchEntraIdAccessTokenAsync_WhenThrowsError_ReturnsFalse()
150+
public void FetchEntraIdAccessTokenAsync_WhenThrowsError()
151151
{
152152
var defaultAzureCredentialMock = new Mock<DefaultAzureCredential>();
153153
defaultAzureCredentialMock
154154
.Setup(x => x.GetTokenAsync(It.IsAny<TokenRequestContext>(), It.IsAny<CancellationToken>()))
155155
.ThrowsAsync(new Exception("sample exception"));
156156
EntraLifecycle entraLifecycle = new(defaultAzureCredentialMock.Object);
157-
Assert.That(await entraLifecycle.FetchEntraIdAccessTokenAsync(), Is.False);
157+
Exception? ex = Assert.ThrowsAsync<Exception>(async () =>
158+
await entraLifecycle.FetchEntraIdAccessTokenAsync());
159+
160+
Assert.That(ex!.Message, Is.EqualTo(Constants.s_no_auth_error));
158161
}
159162

160163
[Test]

sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/tests/PlaywrightServiceTests.cs

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ public void Initialize_WhenFetchesEntraIdAccessToken_SetsUpRotationHandler()
229229
}
230230

231231
[Test]
232-
public void Initialize_WhenFailsToFetchEntraIdAccessToken_DoesNotSetUpRotationHandler()
232+
public void Initialize_WhenFailsToFetchEntraIdAccessToken_ThrowsException()
233233
{
234234
var defaultAzureCredentialMock = new Mock<DefaultAzureCredential>();
235235
var jsonWebTokenHandlerMock = new Mock<JsonWebTokenHandler>();
@@ -238,13 +238,12 @@ public void Initialize_WhenFailsToFetchEntraIdAccessToken_DoesNotSetUpRotationHa
238238
.ThrowsAsync(new Exception());
239239
var entraLifecycleMock = new Mock<EntraLifecycle>(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object);
240240
PlaywrightService service = new(entraLifecycle: entraLifecycleMock.Object);
241-
service.InitializeAsync().Wait();
242-
defaultAzureCredentialMock.Verify(x => x.GetTokenAsync(It.IsAny<TokenRequestContext>(), It.IsAny<CancellationToken>()), Times.Once);
243-
Assert.That(service.RotationTimer, Is.Null);
241+
Exception? ex = Assert.ThrowsAsync<Exception>(async () => await service.InitializeAsync());
242+
Assert.That(ex!.Message, Is.EqualTo(Constants.s_no_auth_error));
244243
}
245244

246245
[Test]
247-
public void Initialize_WhenEntraIdAccessTokenFailsAndMptPatIsSet_DoesNotSetUpRotationHandler()
246+
public void Initialize_WhenEntraIdAccessTokenFailsAndMptPatIsSet_ThrowsException()
248247
{
249248
var token = GetToken(new Dictionary<string, object>
250249
{
@@ -257,12 +256,12 @@ public void Initialize_WhenEntraIdAccessTokenFailsAndMptPatIsSet_DoesNotSetUpRot
257256
.ThrowsAsync(new Exception());
258257
var entraLifecycleMock = new Mock<EntraLifecycle>(defaultAzureCredentialMock.Object, new JsonWebTokenHandler());
259258
PlaywrightService service = new(entraLifecycle: entraLifecycleMock.Object, jsonWebTokenHandler: new JsonWebTokenHandler());
260-
service.InitializeAsync().Wait();
261-
Assert.That(service.RotationTimer, Is.Null);
259+
Exception? ex = Assert.ThrowsAsync<Exception>(async () => await service.InitializeAsync());
260+
Assert.That(ex!.Message, Is.EqualTo(Constants.s_no_auth_error));
262261
}
263262

264263
[Test]
265-
public void Initialize_WhenEntraIdAccessTokenFailsAndMptPatIsNotSet_DoesNotSetUpRotationHandler()
264+
public void Initialize_WhenEntraIdAccessTokenFailsAndMptPatIsNotSet_ThrowsException()
266265
{
267266
var token = GetToken(new Dictionary<string, object>
268267
{
@@ -274,8 +273,8 @@ public void Initialize_WhenEntraIdAccessTokenFailsAndMptPatIsNotSet_DoesNotSetUp
274273
.ThrowsAsync(new Exception());
275274
var entraLifecycleMock = new Mock<EntraLifecycle>(defaultAzureCredentialMock.Object, new JsonWebTokenHandler());
276275
PlaywrightService service = new(entraLifecycle: entraLifecycleMock.Object, jsonWebTokenHandler: new JsonWebTokenHandler());
277-
service.InitializeAsync().Wait();
278-
Assert.That(service.RotationTimer, Is.Null);
276+
Exception? ex = Assert.ThrowsAsync<Exception>(async () => await service.InitializeAsync());
277+
Assert.That(ex!.Message, Is.EqualTo(Constants.s_no_auth_error));
279278
}
280279

281280
[Test]
@@ -335,9 +334,17 @@ public void Initialize_WhenDefaultAuthIsMptPATAndPATIsSet_DoesNotSetUpRotationHa
335334
{
336335
var token = GetToken(new Dictionary<string, object>
337336
{
338-
{"aid", "account-id-guid"},
337+
{"aid", "eastus_bd830e63-6120-40cb-8cd7-f0739502d888"},
339338
});
340339
Environment.SetEnvironmentVariable(ServiceEnvironmentVariable.PlaywrightServiceAccessToken, token);
340+
var testRubric = new Dictionary<string, string>
341+
{
342+
{ "url", "wss://eastus.api.playwright.microsoft.com/accounts/eastus_bd830e63-6120-40cb-8cd7-f0739502d888/browsers" },
343+
{ "workspaceId", "eastus_bd830e63-6120-40cb-8cd7-f0739502d888" },
344+
{ "region", "eastus" },
345+
{ "domain", "playwright.microsoft.com" }
346+
};
347+
Environment.SetEnvironmentVariable(ServiceEnvironmentVariable.PlaywrightServiceUri, $"{testRubric["url"]}");
341348
var defaultAzureCredentialMock = new Mock<DefaultAzureCredential>();
342349
var entraLifecycleMock = new Mock<EntraLifecycle>(defaultAzureCredentialMock.Object, new JsonWebTokenHandler());
343350
PlaywrightService service = new(entraLifecycle: entraLifecycleMock.Object, jsonWebTokenHandler: new JsonWebTokenHandler(), serviceAuth: ServiceAuthType.AccessToken);

0 commit comments

Comments
 (0)