Skip to content

Commit a42f981

Browse files
committed
chore: update integration tests to remove consent and HTML parsing
1 parent 9e79b68 commit a42f981

7 files changed

Lines changed: 51 additions & 153 deletions

File tree

user-management/src/Stickerlandia.UserManagement.Api/Areas/Identity/Pages/Account/Register.cshtml.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ namespace Stickerlandia.UserManagement.Api.Areas.Identity.Pages.Account;
1818

1919
public class RegisterModel : PageModel
2020
{
21-
private readonly RegisterCommandHandler registerCommandHandler;
21+
private readonly RegisterCommandHandler _registerCommandHandler;
2222
private readonly SignInManager<PostgresUserAccount> _signInManager;
2323
private readonly UserManager<PostgresUserAccount> _userManager;
2424
private readonly IUserStore<PostgresUserAccount> _userStore;
@@ -39,7 +39,7 @@ public RegisterModel(
3939
_signInManager = signInManager;
4040
_logger = logger;
4141
_emailSender = emailSender;
42-
this.registerCommandHandler = registerCommandHandler;
42+
this._registerCommandHandler = registerCommandHandler;
4343
}
4444

4545
/// <summary>
@@ -126,7 +126,7 @@ public async Task<IActionResult> OnPostAsync(string returnUrl = null)
126126
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
127127
if (ModelState.IsValid)
128128
{
129-
var registerResult = await registerCommandHandler.Handle(new RegisterUserCommand
129+
var registerResult = await _registerCommandHandler.Handle(new RegisterUserCommand
130130
{
131131
EmailAddress = Input.Email,
132132
FirstName = Input.FirstName,

user-management/src/Stickerlandia.UserManagement.Lambda/MigrationFunction.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ await manager.CreateAsync(new OpenIddictApplicationDescriptor
4040
{
4141
ClientId = "web-ui",
4242
ClientType = OpenIddictConstants.ClientTypes.Public,
43+
// An implicit consent type is used for the web UI, meaning users will NOT be prompted to consent to requested scoipes.
44+
ConsentType = OpenIddictConstants.ConsentTypes.Implicit,
4345
PostLogoutRedirectUris =
4446
{
4547
new Uri("https://localhost:3000")

user-management/src/Stickerlandia.UserManagement.MigrationService/Worker.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ await manager.CreateAsync(new OpenIddictApplicationDescriptor
6363
{
6464
ClientId = "web-ui",
6565
ClientType = OpenIddictConstants.ClientTypes.Public,
66+
// An implicit consent type is used for the web UI, meaning users will NOT be prompted to consent to requested scoipes.
67+
ConsentType = OpenIddictConstants.ConsentTypes.Implicit,
6668
PostLogoutRedirectUris =
6769
{
6870
new Uri("https://localhost:3000")

user-management/tests/Stickerlandia.UserManagement.IntegrationTest/AccountTests.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,14 @@ public async Task WhenAUserRegistersThenCanRetrieveTheirAccountDetails()
8888

8989
// Act
9090
var registerResult = await _driver.RegisterUser(emailAddress, password);
91+
registerResult.Should().NotBeNull();
92+
9193
var loginResponse = await _driver.Login(emailAddress, password);
94+
loginResponse.Should().NotBeNull();
95+
9296
var userAccount = await _driver.GetUserAccount(loginResponse!.AuthToken);
9397

9498
// Assert
95-
registerResult.Should().NotBeNull();
96-
loginResponse.Should().NotBeNull();
9799
userAccount.Should().NotBeNull();
98100
userAccount!.EmailAddress.Should().Be(emailAddress);
99101
}

user-management/tests/Stickerlandia.UserManagement.IntegrationTest/Drivers/AccountDriver.cs

Lines changed: 23 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ internal sealed class AccountDriver : IDisposable
1616
private readonly ITestOutputHelper _testOutputHelper;
1717
private readonly IMessaging _messaging;
1818
private readonly HttpClient _oauthClient;
19+
private readonly HttpClient _httpClient;
1920

2021
public AccountDriver(ITestOutputHelper testOutputHelper, HttpClient httpClient, IMessaging messaging, CookieContainer cookieContainer)
2122
{
@@ -31,10 +32,23 @@ public AccountDriver(ITestOutputHelper testOutputHelper, HttpClient httpClient,
3132
AllowAutoRedirect = false
3233
};
3334

35+
// Create a separate HttpClient for OAuth2.0 operations that doesn't auto-follow redirects
36+
var mainHttpHandler = new HttpClientHandler()
37+
{
38+
CookieContainer = cookieContainer,
39+
UseCookies = true,
40+
CheckCertificateRevocationList = true,
41+
AllowAutoRedirect = true
42+
};
43+
3444
_oauthClient = new HttpClient(oauthHandler, true)
3545
{
3646
BaseAddress = httpClient.BaseAddress
3747
};
48+
_httpClient = new HttpClient(mainHttpHandler, true)
49+
{
50+
BaseAddress = httpClient.BaseAddress
51+
};
3852
}
3953

4054
public async Task<RegisterResponse?> RegisterUser(string emailAddress, string password)
@@ -71,8 +85,8 @@ public AccountDriver(ITestOutputHelper testOutputHelper, HttpClient httpClient,
7185
_testOutputHelper.WriteLine($"Got registration page content, length: {pageContent.Length}");
7286

7387
// Step 2: Extract all form fields, including hidden ones
74-
var formFields = HtmlFormParser.ExtractFormFields(pageContent);
75-
var antiForgeryToken = HtmlFormParser.ExtractAntiForgeryToken(pageContent);
88+
var formFields = new Dictionary<string, string>();
89+
var antiForgeryToken = FormDataExtractor.ExtractAntiForgeryToken(pageContent);
7690

7791
_testOutputHelper.WriteLine($"Extracted {formFields.Count} form fields");
7892

@@ -82,7 +96,7 @@ public AccountDriver(ITestOutputHelper testOutputHelper, HttpClient httpClient,
8296
_testOutputHelper.WriteLine("Added anti-forgery token");
8397
}
8498

85-
// Step 3: Set the user registration data
99+
// Step 3: Set the user registration data
86100
formFields["Input.Email"] = emailAddress;
87101
formFields["Input.Password"] = password;
88102
formFields["Input.ConfirmPassword"] = password;
@@ -187,7 +201,9 @@ public AccountDriver(ITestOutputHelper testOutputHelper, HttpClient httpClient,
187201
// Step 4: Follow authorization flow using OAuth client (no auto-redirect)
188202
var authorizationResponse = await _oauthClient.GetAsync(authorizationUrl);
189203

190-
if (!authorizationResponse.IsSuccessStatusCode)
204+
// The authorization response should return HttpStatusCode.Found (302) if successful, with the redirect location containing the authorization code.
205+
// In the 'Location' header
206+
if (authorizationResponse.StatusCode != HttpStatusCode.Found)
191207
{
192208
_testOutputHelper.WriteLine($"Authorization request failed: {authorizationResponse.StatusCode}");
193209
var errorContent = await authorizationResponse.Content.ReadAsStringAsync();
@@ -277,8 +293,8 @@ private async Task AuthenticateUserViaIdentityUI(string emailAddress, string pas
277293
var loginPageContent = await loginPageResponse.Content.ReadAsStringAsync();
278294

279295
// Step 2: Extract form fields and anti-forgery token
280-
var formFields = HtmlFormParser.ExtractFormFields(loginPageContent);
281-
var antiForgeryToken = HtmlFormParser.ExtractAntiForgeryToken(loginPageContent);
296+
var formFields = new Dictionary<string, string>();
297+
var antiForgeryToken = FormDataExtractor.ExtractAntiForgeryToken(loginPageContent);
282298

283299
if (antiForgeryToken != null)
284300
{
@@ -341,12 +357,6 @@ private static string BuildAuthorizationUrl(string codeChallenge, string state)
341357
// If no direct redirect, check if we need to handle a consent form
342358
var responseContent = await response.Content.ReadAsStringAsync();
343359

344-
if (IsConsentForm(responseContent))
345-
{
346-
_testOutputHelper.WriteLine("Consent form detected, submitting consent");
347-
return await HandleConsentForm(responseContent, state);
348-
}
349-
350360
_testOutputHelper.WriteLine("No authorization code or consent form found in response");
351361
_testOutputHelper.WriteLine($"Response status: {response.StatusCode}");
352362
_testOutputHelper.WriteLine($"Response content: {responseContent}");
@@ -515,75 +525,10 @@ private static bool HasValidationErrors(string htmlContent)
515525
return false;
516526
}
517527

518-
private static bool IsConsentForm(string htmlContent)
519-
{
520-
// Check for consent form indicators
521-
return htmlContent.Contains("Allow this application to access", StringComparison.OrdinalIgnoreCase) ||
522-
htmlContent.Contains("consent", StringComparison.OrdinalIgnoreCase) ||
523-
htmlContent.Contains("Grant access", StringComparison.OrdinalIgnoreCase) ||
524-
htmlContent.Contains("Authorize", StringComparison.OrdinalIgnoreCase) ||
525-
htmlContent.Contains("name=\"submit.Allow\"", StringComparison.OrdinalIgnoreCase);
526-
}
527-
528-
private async Task<string?> HandleConsentForm(string consentFormHtml, string state)
529-
{
530-
try
531-
{
532-
// Extract form fields from the consent form
533-
var formFields = HtmlFormParser.ExtractFormFields(consentFormHtml);
534-
var formAction = HtmlFormParser.ExtractFormAction(consentFormHtml);
535-
var antiForgeryToken = HtmlFormParser.ExtractAntiForgeryToken(consentFormHtml);
536-
537-
if (antiForgeryToken != null)
538-
{
539-
formFields["__RequestVerificationToken"] = antiForgeryToken;
540-
}
541-
542-
// Add consent approval
543-
formFields["submit.Accept"] = "Accept";
544-
formFields.Remove("submit.Deny"); // Remove deny if present
545-
546-
// Determine the form action URL
547-
var actionUrl = string.IsNullOrEmpty(formAction) ? TestConstants.AuthorizeEndpoint : formAction;
548-
549-
_testOutputHelper.WriteLine($"Submitting consent form to: {actionUrl}");
550-
551-
// Submit the consent form using OAuth client (no auto-redirect)
552-
using var consentContent = new FormUrlEncodedContent(formFields);
553-
var consentResponse = await _oauthClient.PostAsync(actionUrl, consentContent);
554-
555-
if (consentResponse.StatusCode == HttpStatusCode.Redirect)
556-
{
557-
_testOutputHelper.WriteLine("Consent approved - got redirect response");
558-
return await ExtractAuthorizationCode(consentResponse);
559-
}
560-
561-
var consentResponseContent = await consentResponse.Content.ReadAsStringAsync();
562-
_testOutputHelper.WriteLine($"Unexpected consent response: {consentResponse.StatusCode}");
563-
_testOutputHelper.WriteLine($"Content: {consentResponseContent}");
564-
565-
return null;
566-
}
567-
catch (HttpRequestException ex)
568-
{
569-
_testOutputHelper.WriteLine($"HTTP error handling consent form: {ex.Message}");
570-
return null;
571-
}
572-
catch (InvalidOperationException ex)
573-
{
574-
_testOutputHelper.WriteLine($"Invalid operation error handling consent form: {ex.Message}");
575-
return null;
576-
}
577-
catch (JsonException ex)
578-
{
579-
_testOutputHelper.WriteLine($"JSON parsing error handling consent form: {ex.Message}");
580-
return null;
581-
}
582-
}
583-
584528
public void Dispose()
585529
{
586530
_oauthClient?.Dispose();
531+
_httpClient?.Dispose();
587532
GC.SuppressFinalize(this);
588533
}
589534
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
2+
// This product includes software developed at Datadog (https://www.datadoghq.com/).
3+
// Copyright 2025 Datadog, Inc.
4+
5+
using System.Text.RegularExpressions;
6+
7+
namespace Stickerlandia.UserManagement.IntegrationTest.Drivers;
8+
9+
internal static class FormDataExtractor
10+
{
11+
public static string? ExtractAntiForgeryToken(string html)
12+
{
13+
var tokenPattern = @"<input[^>]*name=""__RequestVerificationToken""[^>]*value=""([^""]+)""[^>]*>";
14+
var match = Regex.Match(html, tokenPattern, RegexOptions.IgnoreCase);
15+
return match.Success ? match.Groups[1].Value : null;
16+
}
17+
}

user-management/tests/Stickerlandia.UserManagement.IntegrationTest/Drivers/HtmlFormParser.cs

Lines changed: 0 additions & 70 deletions
This file was deleted.

0 commit comments

Comments
 (0)