---
# System prepended metadata

title: ASP.NET Core `AddDefaultIdentity` を開発トンネルで使用した場合メールに記載されるホストは`localhost`となる。2023-06-26
tags: [メール送信, Blazor Server, C#, AddDefaultIdentity, SendGrid, Identity, ASP.NET Core]

---

---
lang: ja-jp
breaks: true
---
# ASP.NET Core `AddDefaultIdentity` を開発トンネルで使用した場合メールに記載されるホストは`localhost`となる。2023-06-26

## 現象

開発トンネルを使用して、パスワード再設定を実行
![](https://hackmd.io/_uploads/BJf4s8Iun.png)

送られてくるメールは、`localhost`となる
![](https://hackmd.io/_uploads/ByBIsUIO3.png)

## `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();
        }
    }
```

## スキャフォールディング してデバッグする。

![](https://hackmd.io/_uploads/H1rA0IUdh.png)


## HTTPリクエストヘッダからだと取得できるはず

```
x-forwarded-proto
x-forwarded-host
x-forwarded-port
x-forwarded-scheme
```