diff --git a/src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs b/src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs index ce283af73fc5..c7c8126e4da1 100644 --- a/src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs +++ b/src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs @@ -57,7 +57,7 @@ public static IEndpointConventionBuilder MapIdentityApi(this IEndpointRou // NOTE: We cannot inject UserManager directly because the TUser generic parameter is currently unsupported by RDG. // https://github.com/dotnet/aspnetcore/issues/47338 routeGroup.MapPost("/register", async Task> - ([FromBody] RegisterRequest registration, [FromServices] IServiceProvider sp) => + ([FromBody] RegisterRequest registration, HttpContext context, [FromServices] IServiceProvider sp) => { var userManager = sp.GetRequiredService>(); @@ -85,7 +85,7 @@ public static IEndpointConventionBuilder MapIdentityApi(this IEndpointRou return CreateValidationProblem(result); } - await SendConfirmationEmailAsync(user, userManager, email); + await SendConfirmationEmailAsync(user, userManager, context, email); return TypedResults.Ok(); }); @@ -194,7 +194,7 @@ await signInManager.ValidateSecurityStampAsync(refreshTicket.Principal) is not T }); routeGroup.MapPost("/resendConfirmationEmail", async Task - ([FromBody] ResendEmailRequest resendRequest, [FromServices] IServiceProvider sp) => + ([FromBody] ResendEmailRequest resendRequest, HttpContext context, [FromServices] IServiceProvider sp) => { var userManager = sp.GetRequiredService>(); if (await userManager.FindByEmailAsync(resendRequest.Email) is not { } user) @@ -202,7 +202,7 @@ await signInManager.ValidateSecurityStampAsync(refreshTicket.Principal) is not T return TypedResults.Ok(); } - await SendConfirmationEmailAsync(user, userManager, resendRequest.Email); + await SendConfirmationEmailAsync(user, userManager, context, resendRequest.Email); return TypedResults.Ok(); }); @@ -348,7 +348,7 @@ await emailSender.SendEmailAsync(resetRequest.Email, "Reset your password", }); accountGroup.MapPost("/info", async Task, ValidationProblem, NotFound>> - (ClaimsPrincipal claimsPrincipal, [FromBody] InfoRequest infoRequest, [FromServices] IServiceProvider sp) => + (ClaimsPrincipal claimsPrincipal, [FromBody] InfoRequest infoRequest, HttpContext context, [FromServices] IServiceProvider sp) => { var userManager = sp.GetRequiredService>(); if (await userManager.GetUserAsync(claimsPrincipal) is not { } user) @@ -382,14 +382,14 @@ await emailSender.SendEmailAsync(resetRequest.Email, "Reset your password", if (email != infoRequest.NewEmail) { - await SendConfirmationEmailAsync(user, userManager, infoRequest.NewEmail, isChange: true); + await SendConfirmationEmailAsync(user, userManager, context, infoRequest.NewEmail, isChange: true); } } return TypedResults.Ok(await CreateInfoResponseAsync(user, claimsPrincipal, userManager)); }); - async Task SendConfirmationEmailAsync(TUser user, UserManager userManager, string email, bool isChange = false) + async Task SendConfirmationEmailAsync(TUser user, UserManager userManager, HttpContext context, string email, bool isChange = false) { if (confirmEmailEndpointName is null) { @@ -414,7 +414,7 @@ async Task SendConfirmationEmailAsync(TUser user, UserManager userManager routeValues.Add("changedEmail", email); } - var confirmEmailUrl = linkGenerator.GetPathByName(confirmEmailEndpointName, routeValues) + var confirmEmailUrl = linkGenerator.GetUriByName(context, confirmEmailEndpointName, routeValues) ?? throw new NotSupportedException($"Could not find endpoint named '{confirmEmailEndpointName}'."); await emailSender.SendEmailAsync(email, "Confirm your email", diff --git a/src/Identity/test/Identity.FunctionalTests/MapIdentityApiTests.cs b/src/Identity/test/Identity.FunctionalTests/MapIdentityApiTests.cs index a0357f9560d5..bf74b839ba32 100644 --- a/src/Identity/test/Identity.FunctionalTests/MapIdentityApiTests.cs +++ b/src/Identity/test/Identity.FunctionalTests/MapIdentityApiTests.cs @@ -13,6 +13,7 @@ using Identity.DefaultUI.WebSite; using Identity.DefaultUI.WebSite.Data; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.AspNetCore.Identity.UI.Services; @@ -31,8 +32,9 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests; public class MapIdentityApiTests : LoggedTest { - private string Email { get; } = $"{Guid.NewGuid()}@example.com"; - private string Password { get; } = "[PLACEHOLDER]-1a"; + private static string Email { get; } = $"{Guid.NewGuid()}@example.com"; + private static string Password { get; } = "[PLACEHOLDER]-1a"; + private static Uri BaseAddress { get; } = new Uri("http://example.com"); [Theory] [MemberData(nameof(AddIdentityModes))] @@ -1273,7 +1275,10 @@ private async Task CreateAppAsync(Action + { + options.BaseAddress = BaseAddress; + }); builder.Services.AddSingleton(LoggerFactory); builder.Services.AddAuthorization(); @@ -1348,7 +1353,9 @@ private static string GetEmailConfirmationLink(TestEmail email) Assert.True(confirmationMatch.Success); Assert.Equal(2, confirmationMatch.Groups.Count); - return WebUtility.HtmlDecode(confirmationMatch.Groups[1].Value); + var url = WebUtility.HtmlDecode(confirmationMatch.Groups[1].Value); + Assert.StartsWith(BaseAddress.ToString(), url); + return url; } private static string GetPasswordResetCode(TestEmail email)