Skip to content

Commit 9781991

Browse files
authored
Use absolute URLs for confirmation emails (#50297)
This PR updates the MapIdentityApi endpoints that send confirmation emails to use absolute URLs. Previously the emails used relative links which of course do not work for links in emails. Fixes #50296
1 parent b11467e commit 9781991

File tree

2 files changed

+19
-12
lines changed

2 files changed

+19
-12
lines changed

src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public static IEndpointConventionBuilder MapIdentityApi<TUser>(this IEndpointRou
5757
// NOTE: We cannot inject UserManager<TUser> directly because the TUser generic parameter is currently unsupported by RDG.
5858
// https://github.com/dotnet/aspnetcore/issues/47338
5959
routeGroup.MapPost("/register", async Task<Results<Ok, ValidationProblem>>
60-
([FromBody] RegisterRequest registration, [FromServices] IServiceProvider sp) =>
60+
([FromBody] RegisterRequest registration, HttpContext context, [FromServices] IServiceProvider sp) =>
6161
{
6262
var userManager = sp.GetRequiredService<UserManager<TUser>>();
6363

@@ -85,7 +85,7 @@ public static IEndpointConventionBuilder MapIdentityApi<TUser>(this IEndpointRou
8585
return CreateValidationProblem(result);
8686
}
8787

88-
await SendConfirmationEmailAsync(user, userManager, email);
88+
await SendConfirmationEmailAsync(user, userManager, context, email);
8989
return TypedResults.Ok();
9090
});
9191

@@ -194,15 +194,15 @@ await signInManager.ValidateSecurityStampAsync(refreshTicket.Principal) is not T
194194
});
195195

196196
routeGroup.MapPost("/resendConfirmationEmail", async Task<Ok>
197-
([FromBody] ResendEmailRequest resendRequest, [FromServices] IServiceProvider sp) =>
197+
([FromBody] ResendEmailRequest resendRequest, HttpContext context, [FromServices] IServiceProvider sp) =>
198198
{
199199
var userManager = sp.GetRequiredService<UserManager<TUser>>();
200200
if (await userManager.FindByEmailAsync(resendRequest.Email) is not { } user)
201201
{
202202
return TypedResults.Ok();
203203
}
204204

205-
await SendConfirmationEmailAsync(user, userManager, resendRequest.Email);
205+
await SendConfirmationEmailAsync(user, userManager, context, resendRequest.Email);
206206
return TypedResults.Ok();
207207
});
208208

@@ -348,7 +348,7 @@ await emailSender.SendEmailAsync(resetRequest.Email, "Reset your password",
348348
});
349349

350350
accountGroup.MapPost("/info", async Task<Results<Ok<InfoResponse>, ValidationProblem, NotFound>>
351-
(ClaimsPrincipal claimsPrincipal, [FromBody] InfoRequest infoRequest, [FromServices] IServiceProvider sp) =>
351+
(ClaimsPrincipal claimsPrincipal, [FromBody] InfoRequest infoRequest, HttpContext context, [FromServices] IServiceProvider sp) =>
352352
{
353353
var userManager = sp.GetRequiredService<UserManager<TUser>>();
354354
if (await userManager.GetUserAsync(claimsPrincipal) is not { } user)
@@ -382,14 +382,14 @@ await emailSender.SendEmailAsync(resetRequest.Email, "Reset your password",
382382

383383
if (email != infoRequest.NewEmail)
384384
{
385-
await SendConfirmationEmailAsync(user, userManager, infoRequest.NewEmail, isChange: true);
385+
await SendConfirmationEmailAsync(user, userManager, context, infoRequest.NewEmail, isChange: true);
386386
}
387387
}
388388

389389
return TypedResults.Ok(await CreateInfoResponseAsync(user, claimsPrincipal, userManager));
390390
});
391391

392-
async Task SendConfirmationEmailAsync(TUser user, UserManager<TUser> userManager, string email, bool isChange = false)
392+
async Task SendConfirmationEmailAsync(TUser user, UserManager<TUser> userManager, HttpContext context, string email, bool isChange = false)
393393
{
394394
if (confirmEmailEndpointName is null)
395395
{
@@ -414,7 +414,7 @@ async Task SendConfirmationEmailAsync(TUser user, UserManager<TUser> userManager
414414
routeValues.Add("changedEmail", email);
415415
}
416416

417-
var confirmEmailUrl = linkGenerator.GetPathByName(confirmEmailEndpointName, routeValues)
417+
var confirmEmailUrl = linkGenerator.GetUriByName(context, confirmEmailEndpointName, routeValues)
418418
?? throw new NotSupportedException($"Could not find endpoint named '{confirmEmailEndpointName}'.");
419419

420420
await emailSender.SendEmailAsync(email, "Confirm your email",

src/Identity/test/Identity.FunctionalTests/MapIdentityApiTests.cs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
using Identity.DefaultUI.WebSite;
1414
using Identity.DefaultUI.WebSite.Data;
1515
using Microsoft.AspNetCore.Builder;
16+
using Microsoft.AspNetCore.Hosting.Server;
1617
using Microsoft.AspNetCore.Http;
1718
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
1819
using Microsoft.AspNetCore.Identity.UI.Services;
@@ -31,8 +32,9 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests;
3132

3233
public class MapIdentityApiTests : LoggedTest
3334
{
34-
private string Email { get; } = $"{Guid.NewGuid()}@example.com";
35-
private string Password { get; } = "[PLACEHOLDER]-1a";
35+
private static string Email { get; } = $"{Guid.NewGuid()}@example.com";
36+
private static string Password { get; } = "[PLACEHOLDER]-1a";
37+
private static Uri BaseAddress { get; } = new Uri("http://example.com");
3638

3739
[Theory]
3840
[MemberData(nameof(AddIdentityModes))]
@@ -1273,7 +1275,10 @@ private async Task<WebApplication> CreateAppAsync<TUser, TContext>(Action<IServi
12731275
where TContext : DbContext
12741276
{
12751277
var builder = WebApplication.CreateSlimBuilder();
1276-
builder.WebHost.UseTestServer();
1278+
builder.WebHost.UseTestServer(options =>
1279+
{
1280+
options.BaseAddress = BaseAddress;
1281+
});
12771282
builder.Services.AddSingleton(LoggerFactory);
12781283
builder.Services.AddAuthorization();
12791284

@@ -1348,7 +1353,9 @@ private static string GetEmailConfirmationLink(TestEmail email)
13481353
Assert.True(confirmationMatch.Success);
13491354
Assert.Equal(2, confirmationMatch.Groups.Count);
13501355

1351-
return WebUtility.HtmlDecode(confirmationMatch.Groups[1].Value);
1356+
var url = WebUtility.HtmlDecode(confirmationMatch.Groups[1].Value);
1357+
Assert.StartsWith(BaseAddress.ToString(), url);
1358+
return url;
13521359
}
13531360

13541361
private static string GetPasswordResetCode(TestEmail email)

0 commit comments

Comments
 (0)