--- 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 ```