The SendConfirmationLinkAsync is sending confirmationLink to non-public address
- Dominant language
- C#
- Stars
- 38.4k
- Forks
- 10.9k
- Avg merge
- 2d 10h
- Merged PRs (30d)
- 281
Description
### Is there an existing issue for this?
- [x] I have searched the existing issues
### Describe the bug
When we host the IdentityApi in a non-public environment and we call an api-method for sending the conformation.
The conformationlink has the host address of the api application.
https://github.com/dotnet/aspnetcore/blob/b52c80fef2b8bf5239c7643d9c79d1029bb6c988/src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs#L390
### Expected Behavior
That an Event is called/raised with the parameters to be processed. (user, userid, code)
For example
```
public interface IIdentityCoreHandler where TUser : class
{
Task LoggedIn(string email);
Task LoggedInFailure(string email, string errorMessage);
Task EmailConfirmed(TUser user);
Task SendConfirmationEmailAsync(TUser user, string? userId, string email, string code, bool isChange = false);
Task SendPasswordResetCodeAsync(TUser user, string email, string resetCode);
}
```
with implementation
```
public class IdentityCoreHandler : IIdentityCoreHandler
where TUser : class
{
/*private readonly IPublisher _eventPublisher;*/
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IEmailSender _emailSender;
private readonly LinkGenerator _linkGenerator;
public IdentityCoreHandler(/*IPublisher eventPublisher,*/ IHttpContextAccessor httpContextAccessor, IEmailSender emailSender, LinkGenerator linkGenerator)
{
/*_eventPublisher = eventPublisher;*/
_httpContextAccessor = httpContextAccessor;
_emailSender = emailSender;
_linkGenerator = linkGenerator;
}
public Task EmailConfirmed(TUser user)
=> Task.CompletedTask;
// we could do extra checks for success login
// Maybe first login from 'unknown' location? Than we could notify the user
public Task LoggedIn(string email)
=> Task.CompletedTask;
public Task LoggedInFailure(string email, string errorMessage)
=> Task.CompletedTask;
public async Task SendConfirmationEmailAsync(TUser user, string? userId, string code, string email, bool isChange = false)
{
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
var routeValues = new RouteValueDictionary()
{
["userId"] = userId,
["code"] = code,
};
if (isChange)
{
// This is validated by the /confirmEmail endpoint on change.
routeValues.Add("changedEmail", email);
}
var confirmEmailUrl = _linkGenerator.GetUriByName(_httpContextAccessor.HttpContext!, ConfirmEmailEndpointStore.ConfirmEmailEndpointName, routeValues)
?? throw new NotSupportedException($"Could not find endpoint named '{ConfirmEmailEndpointStore.ConfirmEmailEndpointName}'.");
// When developer/usecase wants to create his own IIdentityHandler, he can inject IConfiguration
// and set a different host for the confirmEmailUrl by a appsettings.json key/value
await _emailSender.SendConfirmationLinkAsync(user, email, HtmlEncoder.Default.Encode(confirmEmailUrl));
}
public async Task SendPasswordResetCodeAsync(TUser user, string email, string resetCode)
{
//we could publish an event with mediatr, and let the domain solve that to do with it..
//var @event = new Domain.Model.Identity.Events.SendPasswordResetCodeEvent(user, email, resetCode);
//await _eventPublisher.Publish(@event);
await Task.CompletedTask;
}
}
```
than we can change the SendConfirmationEmailAsync in the IdentityApiEndpointRouteBuilderExtensions to
```
async Task SendConfirmationEmailAsync(TUser user, UserManager userManager, HttpContext context, string email, bool isChange = false)
{
var code = isChange
? await userManager.GenerateChangeEmailTokenAsync(user, email)
: await userManager.GenerateEmailConfirmationTokenAsync(user);
var userId = await userManager.GetUserIdAsync(user);
await identityCoreHandler.SendConfirmationEmailAsync(user, userId, code, email, isChange: isChange);
}
```
change line 47
var emailSender = endpoints.ServiceProvider.GetRequiredService>();
into
var identityCoreHandler = endpoints.ServiceProvider.GetRequiredService>();
Now we have full controll what we want to do in every scanario
### Steps To Reproduce
Host the IdentityApi in a non-public environment.
### Exceptions (if any)
_No response_
### .NET Version
_No response_
### Anything else?
_No response_
Contributor guide
Assessment
This issue has not been assessed yet.