---
lang: ja-jp
breaks: true
---
# ASP.NET Core `AddDefaultIdentity` を開発トンネルで使用した場合メールに記載されるホストは`localhost`となる。2023-06-26
## 現象
開発トンネルを使用して、パスワード再設定を実行

送られてくるメールは、`localhost`となる

## `ASP.NET Core`のソースを確認
### ForgotPassword.cshtml.cs
:::info
`Url.Page`でURLが構築されている。
:::
```csharp=
public override async Task<IActionResult> OnPostAsync()
{
if (ModelState.IsValid)
{
var user = await _userManager.FindByEmailAsync(Input.Email);
if (user == null || !(await _userManager.IsEmailConfirmedAsync(user)))
{
// Don't reveal that the user does not exist or is not confirmed
return RedirectToPage("./ForgotPasswordConfirmation");
}
// For more information on how to enable account confirmation and password reset please
// visit https://go.microsoft.com/fwlink/?LinkID=532713
var code = await _userManager.GeneratePasswordResetTokenAsync(user);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
var callbackUrl = Url.Page(
"/Account/ResetPassword",
pageHandler: null,
values: new { area = "Identity", code },
protocol: Request.Scheme)!;
await _emailSender.SendEmailAsync(
Input.Email,
"Reset Password",
$"Please reset your password by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
return RedirectToPage("./ForgotPasswordConfirmation");
}
return Page();
}
```
### UrlHelperExtensions.cs
```csharp=
public static string? Page(
this IUrlHelper urlHelper,
string? pageName,
string? pageHandler,
object? values,
string? protocol,
string? host,
string? fragment)
{
ArgumentNullException.ThrowIfNull(urlHelper);
var routeValues = new RouteValueDictionary(values);
var ambientValues = urlHelper.ActionContext.RouteData.Values;
UrlHelperBase.NormalizeRouteValuesForPage(urlHelper.ActionContext, pageName, pageHandler, routeValues, ambientValues);
return urlHelper.RouteUrl(
routeName: null,
values: routeValues,
protocol: protocol,
host: host,
fragment: fragment);
}
```
### UrlHelperBase.cs
:::info
ホストを`ActionContext.HttpContext.Request.Host.Value`から取得している。
開発トンネルは、フォワーディングしているのでこれでは取れないようだ。
:::
```csharp=
protected string? GenerateUrl(string? protocol, string? host, string? virtualPath, string? fragment)
{
if (virtualPath == null)
{
return null;
}
// Perf: In most of the common cases, GenerateUrl is called with a null protocol, host and fragment.
// In such cases, we might not need to build any URL as the url generated is mostly same as the virtual path available in pathData.
// For such common cases, this FastGenerateUrl method saves a string allocation per GenerateUrl call.
if (TryFastGenerateUrl(protocol, host, virtualPath, fragment, out var url))
{
return url;
}
var builder = GetStringBuilder();
try
{
var pathBase = ActionContext.HttpContext.Request.PathBase;
if (string.IsNullOrEmpty(protocol) && string.IsNullOrEmpty(host))
{
AppendPathAndFragment(builder, pathBase, virtualPath, fragment);
// We're returning a partial URL (just path + query + fragment), but we still want it to be rooted.
if (builder.Length == 0 || builder[0] != '/')
{
builder.Insert(0, '/');
}
}
else
{
protocol = string.IsNullOrEmpty(protocol) ? "http" : protocol;
builder.Append(protocol);
builder.Append(Uri.SchemeDelimiter);
host = string.IsNullOrEmpty(host) ? ActionContext.HttpContext.Request.Host.Value : host;
builder.Append(host);
AppendPathAndFragment(builder, pathBase, virtualPath, fragment);
}
var path = builder.ToString();
return path;
}
finally
{
// Clear the StringBuilder so that it can reused for the next call.
builder.Clear();
}
}
```
## スキャフォールディング してデバッグする。

## HTTPリクエストヘッダからだと取得できるはず
```
x-forwarded-proto
x-forwarded-host
x-forwarded-port
x-forwarded-scheme
```