WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

IdentityServer で Web API の認証を サポート

by WebSurfer 2022年4月8日 16:48

先の記事「Duende IdentityServer」で作成した認証サーバーを利用して ASP.NET Core Web API アプリのユーザー認証ができるようにしてみます。

Duende Software のドキュメント Protecting an API using Client Credentials に例が載っていますが、その記事では Machine to Machine で(即ち、個々のユーザー認証なしで)トークンを取得しているところを、IdentityServer に登録済みのユーザーの id と password で認証を受けてトークンを取得するように変更しました。

個々のユーザーのクレデンシャルを使うのは望ましくないというような意味のことがドキュメントに書いてあった気がしますが、せっかく苦労してサンプルを作ったので以下にその手順を書いておきます。

(1) Web API プロジェクトの作成

Visual Studio 2022 の「ASP.NET Core Web API」のテンプレートを使って[フレームワーク(F)]を「.NET 6.0 (長期的なサポート)」とし[認証の種類(A)]を「なし」にして Web API プロジェクトを作成します。

Web API プロジェクトの作成

(2) NuGet パッケージのインストール

Microsoft.AspNetCore.Authentication.JwtBearer を NuGet からインストールします。

NuGet パッケージのインストール

(3) JWT 認証スキーマを登録

Web API プロジェクトに含まれる Program.cs のコードを編集して JWT 認証スキーマを登録します。Duende Software のドキュメントの Adding an APIAuthorization at the API を参考にしました。

.NET 6.0 のプロジェクトの Program.cs では以下のようになります。自動生成されたコードに「// 追加」とコメントしたコードを追加します。

// 追加
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;

var builder = WebApplication.CreateBuilder(args);

// 追加
builder.Services.AddAuthentication(
        JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options =>
        {
            options.Authority = "https://localhost:5001";
            options.TokenValidationParameters = 
                new TokenValidationParameters
                {
                    ValidateAudience = false
                };
        });

// 追加
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("ApiScope", policy =>
    {
        policy.RequireAuthenticatedUser();
        policy.RequireClaim("scope", "scope3");
    });
});

// ・・・中略・・・

// 追加
app.UseAuthentication();

app.UseAuthorization();
app.MapControllers();
app.Run();

上のコードで、options.Authority に設定する URL は先に作成した Duende IdentityServer の URL に合わせてください。デフォルトでは上のように "https://localhost:5001" となっているはずです。

policy.RequireClaim("scope", "scope3") の "scope3" は後で Duende IdentityServer プロジェクトの Config.cs ファイルで設定します。下のステップ (5) のコードを見てください。

(4) [Authorize] 属性を付与

自動生成された WeatherForecastController コントローラの Get() メソッドに [Authorize] 属性を付与します。using Microsoft.AspNetCore.Authorization; の追加を忘れないようにしてください。

ここまでの設定で JWT トークンベースのアクセス制限の実装は完了しており、トークンなしで WeatherForecastController コントローラの Get() メソッドを要求すると HTTP 401 Unauthorized 応答が返ってくるはずですので試してみてください。

(5) IdentityServer に Client を追加

先に作成済の Duende IdentityServer プロジェクトの Config.cs ファイルを開いて上の Web API アプリの認証をサポートするための Client を追加します。以下の「// 追加」とコメントしたコードを既存のファイルに追加します。

using Duende.IdentityServer.Models;

namespace DuendeIdentityServer
{
    public static class Config
    {

        // ・・・中略・・・

        public static IEnumerable<ApiScope> ApiScopes => new ApiScope[] {
            new ApiScope("scope1"),
            new ApiScope("scope2"),
            
            // 追加
            new ApiScope("scope3")
        };

        public static IEnumerable<Client> Clients => new Client[] {

            // ・・・中略・・・
            
            // 追加
            new Client
            {                
                ClientId = "WebApiNet6",
                ClientSecrets = { new Secret("0C86E143-30E0-4FB4-8710-008CD861BF5B".Sha256()) },

                AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
                AllowedScopes = { "scope3" }
            }
        };
    }
}

Duende Software のドキュメントの例では Machine to Machine communication でトークンを取得していますが、そこを Issuing Tokens based on User PasswordsToken Endpoint を参考に id と password を送信してトークンを取得できるように変更しました。AllowedGrantTypes を GrantTypes.ResourceOwnerPassword に設定するのがキモらしいです。

(6) クライアントアプリの作成

IdentityServer に登録済みのユーザーの id と password を送信してトークンを取得し、そのトークンを要求ヘッダに設定して Web API の WeatherForecast アクションメソッドを GET 要求して結果を表示する検証用のアプリを作成します。

Duende Software のドキュメント Creating the client を参考に .NET 6.0 のコンソールアプリとして作成しました。

コードは以下の通りです。NuGet で IdentityModel をインストールしないと動かないので注意してください。

using IdentityModel.Client;
using System.Text.Json;

var client = new HttpClient();

var disco = await client
    .GetDiscoveryDocumentAsync("https://localhost:5001");

if (disco.IsError)
{
    Console.WriteLine(disco.Error);
    return;
}

var tokenResponse2 = await client.RequestPasswordTokenAsync(
    new PasswordTokenRequest
    {
        Address = disco.TokenEndpoint,
        ClientId = "WebApiNet6",
        ClientSecret = "0C86E143-30E0-4FB4-8710-008CD861BF5B",

        Scope = "scope3",

        UserName = "alice",
        Password = "Pass123$"
    });

if (tokenResponse2.IsError)
{
    Console.WriteLine(tokenResponse2.Error);
    return;
}
else
{
    Console.WriteLine(tokenResponse2.Json);
    Console.WriteLine("\n\n");
}

client.Dispose();

var apiClient = new HttpClient();
apiClient.SetBearerToken(tokenResponse2.AccessToken);

var response = await apiClient
              .GetAsync("https://localhost:44300/WeatherForecast");

if (!response.IsSuccessStatusCode)
{
    Console.WriteLine(response.StatusCode);
}
else
{
    var doc = JsonDocument
              .Parse(await response.Content.ReadAsStringAsync())
              .RootElement;
    Console.WriteLine(JsonSerializer
          .Serialize(doc, 
              new JsonSerializerOptions { WriteIndented = true }));
}

apiClient.Dispose();

コンソールアプリの実行結果は以下のようになります。事前に IdentityServer と Web API を動かしておく必要がありますので注意してください。

コンソールアプリの実行結果

一応期待通り動くことは検証できましたが、ホントに上記の設定で良いのかは自信がありません。ひょっとしたら、やってはいけないことをやっているのかもしれませんので、コピペして使うのは避けた方が良いと思います。(笑)

Tags: , ,

Authentication

Duende IdentityServer

by WebSurfer 2022年4月7日 12:38

先の記事「VS2022 .NET 6.0 React のユーザー認証」で IdentityServer を勉強しなくてはと書きましたが、そのための第一歩として Duende Software のサイトの Quickstarts を見ながらサンプルを作ってみました。

Duende IdentityServer

上の画像は Visual Studio 2022 のテンプレートで Duende IdentityServer プロジェクトを作成し、それを Visual Studio から実行してブラウザに表示したものです。

以下に、上の画像の Duende IdentityServer プロジェクトの作り方、ASP.NET Core MVC アプリのプロジェクトを作ってその認証を Duende IdentityServer でサポートする方法を備忘録として書いておきます。

(1) テンプレートのインストール

Overview に従って Visual Studio 用のテンプレートをインストールします。コマンドラインから以下のように dotnet コマンド、

dotnet new --install Duende.IdentityServer.Templates

・・・を実行すると、下の画像のように 6 種類のテンプレートが自動的にインストールされます。

テンプレートをインストール

(2) IdentityServer プロジェクトの作成

Visual Studio 2022 を立ち上げて「新しいプロジェクトの作成」画面を開くと以下のように Duende IdentityServer 関係のメニューが表示されます。

Duende IdentityServer

その中の赤枠で囲った ASP.NET Core Identity をユーザー情報のストアに使うテンプレート選択してプロジェクトを作成してみました。プロジェクトを作成する際何故かフレームワークは選択できず自動的に .NET 6.0 になります。

作成したプロジェクトは Quickstarts の中の Using ASP.NET Core Identity に該当します。(記事が少し古いのか、実際に生成されたものと記事の説明が少々異なるのに注意)

中身は ASP.NET Core の Razor Pages アプリで、Visual Studio から実行すると開発サーバーとして Kestrel が立ち上がってアプリが実行され、この記事の一番上の画像のように結果がブラウザに表示されます。

デフォルトではユーザー情報をストアするためのデータベースは SQLite になります。EF Code First の機能を使ってデータベースを生成します。Migration 関係のファイルは Data/Migrations にありますので、パッケージマネージャーコンソールから update-database を実行すればアプリケーションルート直下に AspIdUsers.db というファイルが生成されます。ただしその時点ではユーザーは登録されてないのでログインはできません。

プロジェクトにはユーザーを登録するためのページは含まれてないのでアプリを操作してユーザー登録することはできません。ユーザー情報をシードするために SeedData.cs がプロジェクトに含まれていますが Visual Studio から実行したのではそれは動きません。

dotnet run /seed を実行すれば alice と bob という名前の 2 人のユーザーがパスワード Pass123$ で登録されるようです (未確認です。dotnet コマンドに自信のない自分は Program.cs のコード中の if (args.Contains("/seed")) を true にして実行してユーザー登録しました)。以降は、そのユーザー情報を使ってログインできるようになります。

以上で IdentityServer 側の準備はできました。ただし、後で、認証をサポートする MVC アプリのホスト名などの情報を Config.cs ファイルの設定に反映する必要があることを覚えておいてください。

(3) ASP.NET Core MVC プロジェクトの作成

次に、Duende Software の Interactive Applications with ASP.NET Core の説明に従って ASP.NET Core MVC アプリのプロジェクトを作成し、上で準備した IdentityServer で認証を行うようにします。

Visual Studio 2022 の「ASP.NET Core Web アプリ (Model-View-Controller)」のテンプレートを使って[フレームワーク(F)]を「.NET 6.0 (長期的なサポート)」とし[認証の種類(A)]を「なし」にして MVC アプリのプロジェクトを作成します。

(4) NuGet パッケージのインストール

MVC プロジェクトに Microsoft.AspNetCore.Authentication.OpenIdConnect を NuGet からインストールします。

Microsoft.AspNetCore.Authentication.OpenIdConnect

(5) Program.cs の編集

MVC プロジェクトの Program.cs を開いて以下の「// 追加」とコメントしてあるコードを追加します。options.ClientId と options.ClientSecret は IdentityServer の Config.cs の Client の設定と合わせる必要があるので注意してください。

// 追加
using System.IdentityModel.Tokens.Jwt;

var builder = WebApplication.CreateBuilder(args);

// 追加
JwtSecurityTokenHandler.DefaultOutboundClaimTypeMap.Clear();

// 追加
builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = "Cookies";
    options.DefaultChallengeScheme = "oidc";
})
    .AddCookie("Cookies")
    .AddOpenIdConnect("oidc", options =>
    {
        // IdentityServer の launchSettings.json の設定と合わせる
        // デフォルトで以下の設定になっているはず
        options.Authority = "https://localhost:5001";

        // IdentityServer の Config.cs の Client の設定と合わせる
        options.ClientId = "interactive";
        options.ClientSecret = "49C1A7E1-0C79-4A89-A3D6-A37998FB86B0";

        options.ResponseType = "code";
        options.SaveTokens = true;

        // 以下を追加すると IdentityServer の SeedData.cs で
        // Claims に追加した name, given_name, family_name
        // 情報も User.Claims に含まれるようになる
        options.Scope.Add("profile");
        options.GetClaimsFromUserInfoEndpoint = true;
    });

// ・・・中略・・・

// 追加
app.UseAuthentication();

app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

(6) IdentityServer の Config.cs の修正

IdentityServer プロジェクトの Config.cs を開いて RedirectUris, FrontChannelLogoutUri, PostLogoutRedirectUris のホスト名を MVC プロジェクトのものに変更します。

開発環境では MVC プロジェクトの Properties/launchSettings.json に設定されていますのでそれを見るか、実際にアプリを起動してブラウザのアドレスバーを見てください。IIS Express を使うのか Kestrel を使うのかによって変わるので注意してください。

// interactive client using code flow + pkce
new Client
{
    ClientId = "interactive",
    ClientSecrets = { new Secret("49C1A7E1-0C79-4A89-A3D6-A37998FB86B0".Sha256()) },

    AllowedGrantTypes = GrantTypes.Code,

    // 以下のホスト名の部分を修正
    RedirectUris = { "https://localhost:44351/signin-oidc" },
    FrontChannelLogoutUri = "https://localhost:44351/signout-oidc",
    PostLogoutRedirectUris = { "https://localhost:44351/signout-callback-oidc" },

   AllowOfflineAccess = true,
   AllowedScopes = { "openid", "profile", "scope2" }
},

(7) HomeController の修正

MVC プロジェクトの HomeController を開いて Privacy アクションメソッドに [Authorize] 属性を付与します。これにより匿名ユーザーが Privacy にアクセスしようとすると IdentityServer のログインページにリダイレクトされるようになります。

ログアウトできるように Logout アクションメソッドを追加します。その説明は Adding sign-out に書いてあります。(それを読んでも自分には仕組みは分かりませんでしたが、このアクションメソッドを呼び出せばログアウトできるのは確認しました)

using Microsoft.AspNetCore.Mvc;
using MvcNet6.Models;
using System.Diagnostics;

// 追加
using Microsoft.AspNetCore.Authorization;

namespace MvcNet6.Controllers
{
    public class HomeController : Controller
    {
        // ・・・中略・・・        

        // 追加
        [Authorize]
        public IActionResult Privacy()
        {
            return View();
        }

        // 追加
        public IActionResult Logout()
        {
            return SignOut("Cookies", "oidc");
        }

        // ・・・中略・・・
    }
}

(8) Private.cshtml の書き換え

ステップ (7) で [Auhtorize] 属性を付与したアクションメソッド Privacy のビュー Private.cshtml を以下のように書き換えます。これは参考に Claims 情報と IdentityServer のプロパティ情報を表示するためで、こうしなければならないというものではないです。

@using Microsoft.AspNetCore.Authentication

@{
    ViewData["Title"] = "Privacy Policy";
}
<h2>Claims</h2>

<dl>
    @foreach (var claim in User.Claims)
    {
        <dt>@claim.Type</dt>
        <dd>@claim.Value</dd>
    }
</dl>

<h2>Properties</h2>

<dl>
@{
#nullable disable
    foreach (var prop in (await Context.AuthenticateAsync()).Properties.Items)
    {
        <dt>@prop.Key</dt>
        <dd>@prop.Value</dd>
    }
}
</dl>

(9) _LoginPartial.cshtml の追加

ASP.NET Core MVC プロジェクトを認証に個別のアカウントを選んで作成すると、ログインしたときに画面のメニューバーの右端にユーザー名と Logout リンクが表示されますが、それと同等なものを Views/Shared フォルダに _LoginPartial.cshtml として追加します。

<ul class="navbar-nav">
@{
    string? givenName = User.Claims.FirstOrDefault(u => u.Type == "given_name")?.Value;

    if (givenName != null)
    {
        <li class="nav-item">
            <span class="nav-link">Hello @givenName !</span>
        </li>
        <li class="nav-item">
        <a class="nav-link" asp-action="Logout" asp-controller="Home">Logout</a>
        </li>
    }
    else
    {
        <li><span class="nav-link">ログインしていません</span></li>
    }
}
</ul>

これを <partial name="_LoginPartial" /> というコードで Views/Shared/_Layour.cshtml に追加します。場所は <div class="navbar-collapse ... の div 要素の中の一番下です。

以上で完了です。まず Visual Studio で IdentiryServer プロジェクトを実行した後、MVC プロジェクトを実行します。ブラウザに表示された MVC アプリのメニューバーの Pricacy をクリックすると、以下のように IdentityServer のログイン画面にリダイレクトされます。そこで、IdentiryServer プロジェクトで登録したユーザーの alice または bob でログインできます。

ログイン画面

ログインに成功すると Privacy 画面にリダイレクトされ、下の画像の結果が表示されます。

Privacy 画面

メニューバーの右端の Logout リンクをクリックするとログアウトできます。


さて、ここまでやってみて、それで IdentityServer が十分理解できたのか、先の記事「VS2022 .NET 6.0 React のユーザー認証」で問題となった Authority とか Issuer とはどういうものか分かったのかと聞かれると、正直何も分かってないです。

さらなる勉強が必要なようですが、どうも手に負えそうもないような感じがしています。(汗)


【2022/7/12 追記】

認証サーバー (Duende IdentityServer) のドメインは localhost:5001 で、ASP.NET Core MVC のドメインは localhost:44351 と異なっていますが、それで何故 Cookie ベースの ASP.NET Core MVC のユーザー認証がうまくいくのでしょう?

Fiddler を使って調べてみると、認証プロセスの過程で以下の Fiddler のキャプチャ画像の #40 の通り localhost:44351/signin-odic に要求が出て、応答の Set_Cookie で認証クッキーが返ってきています。

認証クッキーも受領

そのあたりの詳しい仕組みは全く分かっていませんが、とにかく ASP.NET Core MVC のドメイン localhost:44351 での要求で認証クッキーが返ってくるので、認証サーバーのドメイン (localhost:5001) が異なっても問題ないということでした。

Tags: , ,

Authentication

ASP.NET Core Identity タイムアウト判定

by WebSurfer 2020年10月31日 18:18

先の記事「ASP.NET Identity タイムアウト判定」の Core 3.1 版です。(先の記事は .NET Framework 版です。Core 版とはかなりの部分で異なります)

有効期限切れの通知

ASP.NET Identity の認証システムは、基本的に旧来のフォーム認証と同様で、ログイン後はクッキーに認証チケットを入れて要求毎にサーバーに送信するので認証状態が維持されるという仕組みになっています。

一旦認証を受けたユーザーがログオフせずブラウザを立ち上げたままタイムアウトとなって、再度どこかのページを要求したとすると、要求したページが匿名アクセスを許可してなければ、ログインページにリダイレクトされます。

その時、認証チケットが期限切れになった(認証されているか否かではありません)というのを、どのようすれば判定できるかということを書きます。

Web サーバー側では以下の条件で判定できるはずです。

  • 要求 HTTP ヘッダーに認証クッキーが含まれる。
  • 認証クッキーの中の認証チケットが期限切れ。

Web サーバーが認証クッキーを取得できれば、それを復号して認証チケットを取得し、チケットの中の有効期限の日時の情報を取得できます。それで上記の 2 つの条件を確認できます。

認証クッキーを復号する方法ですが、旧来のフォーム認証の場合とはもちろん、.NET Framework 版の ASP.NET Identity の場合ともかなり異なっています。

そのあたりはググって調べた stackoverflow の記事 How to decrypt .AspNetCore.Identity.Application cookie in ASP.NET Core 3.0?How to manually decrypt an ASP.NET Core Authentication cookie? の回答を参考にさせていただきました。

それを先の記事と同様に HTTP モジュールとして実装してみようと思いましたが、Core には HTTP モジュールも HTTP ハンドラも web.config もないとのこと。

.NET Framework の ASP.NET の HTTP モジュールに代わるものが ASP.NET Core ではミドルウエアになるらしいです。なので、以下の Microsoft のドキュメントを参考に、ミドルウェアを作ってみました。

以下がそのミドルウェアのコードです。上の画像がこのミドルウェアにより認証チケットの期限切れをチェックして Login 画面上でユーザーに通知したものです。

ただし、ユーザーへの通知を下のコードの最後の方にあるように HttpResponse.WriteAsync を使って書き込んだ場合、見かけは上の画像のようになりますが、実は <html></html> タグの外(この例では最後)に書き込まれています。また、上の記事「ASP.NET Core のミドルウェア」の警告に書いてありますが、next.Invoke の前後で応答本文に書き込むというのはそもそもやってはいけないことのようです。

今回はミドルウェアに実装してしまいましたが、ユーザーへの通知を行うならミドルウェアでなく Login ページに下のコードを実装して期限切れを判定し、所定の位置に書き込むようにする方がよさそうです。

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.Extensions.Options;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Authentication;
using System.Security.Claims;
using System.Security.Principal;

namespace MvcCoreApp.Middleware
{
    public class AuthExpireCheckMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly CookieAuthenticationOptions _options;

        // コンストラクタの引数を以下のように設定しておくことにより
        // CookieAuthenticationOptions を DI 機能により取得できる
        public AuthExpireCheckMiddleware(
            RequestDelegate next,
            IOptions<CookieAuthenticationOptions> options)
        {
            _next = next;
            _options = options.Value;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            // ここでの処理は必要ないので即後続のミドルウェアに処理を渡す。
            // 応答がクライアントに送信された後に _next.Invoke を呼び出すと
            // 例外がスローされるそうなので注意(ここで Response.WriteAsync
            // は書けない)

            await _next.Invoke(context);

            // 認証チケットの期限が切れた後、認証が必要なページにアクセス
            // して Login ページにリダイレクトされた時のみ対応。
            // "/Identity/Account/Login" は Visual Studio のテンプレートで
            // プロジェクトを作った際のデフォルト。必要あれば変更可
            if (context.Request.Path.ToString().
                                     Contains("/Identity/Account/Login"))
            {
                // 認証クッキーが送られてくる時のみ対応。クッキー名
                // .AspNetCore.Identity.Application はデフォルト。
                // 必要あれば変更可
                string cookie 
                    = context.Request.Cookies[".AspNetCore.Identity.Application"];
                if (!string.IsNullOrEmpty(cookie))
                {
                    IDataProtectionProvider provider = 
                                        _options.DataProtectionProvider;

                    // 下の「注2」参照
                    IDataProtector protector = provider.CreateProtector(
                        // CookieAuthenticationMiddleware のフルネームらしい
                        "Microsoft.AspNetCore.Authentication.Cookies." +
                        "CookieAuthenticationMiddleware",
                        // クッキー名から .AspNetCore. を削除した文字列
                        "Identity.Application",
                        // .NET Framework 版は "v1"
                        "v2");

                    // 認証クッキーから暗号化された認証チケットを復号
                    TicketDataFormat format = new TicketDataFormat(protector);
                    AuthenticationTicket authTicket = format.Unprotect(cookie);

                    // ユーザー名を取得
                    ClaimsPrincipal principal = authTicket.Principal;
                    IIdentity identity = principal.Identity;
                    string userName = identity.Name;

                    // 認証チケットの有効期限の日時を取得
                    AuthenticationProperties property = authTicket.Properties;
                    DateTimeOffset? expiersUtc = property.ExpiresUtc;

                    // ExpiresUtc と現在の時刻を比較して期限切れか否かを判定
                    if (expiersUtc.Value < DateTimeOffset.UtcNow)
                    {    
                        // ユーザーに通知
                        // 以下のコードでは <html></html> 外に書き込まれるので注意
                        await context.Response.WriteAsync("<h3><font color=red>" +
                            userName +
                            " さん、認証チケットの有効期限が切れています" +
                            "</font></h3>");
                    }
                }
            }
        }
    }
}

注1

上のコードのように DI 機能により CookieAuthenticationOptions オブジェクトを取得できます。それをベースに復号に必要な TicketDataFormat オブジェクトを取得し、認証クッキーから認証チケットを復号しています。

Visual Studio のテンプレートを使って「個別のユーザーアカウント」を認証に選んで作成したプロジェクトでは以下のコードが Startup.cs 含まれています。その場合、コンストラクタの引数に IOptions<CookieAuthenticationOptions> options を含めるだけで DI 機能が働きます。コントローラーのコンストラクタでも同じです。

services.AddDefaultIdentity<IdentityUser>(options => 
    options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();

上の設定が Startup.cs に含まれない場合、上に紹介した stackoverflow の記事 How to decrypt .AspNetCore.Identity.Application cookie in ASP.NET Core 3.0? にあるようなサービスの登録が必要です、

注2

IDataProtectionProvider.CreateProtector の引数ですが、これは .NET Framework の MachineKey.Protect / MachineKey.Unprotect の第 2 引数と同様に暗号化テキストを特定の目的にロックするためのもののようです。

上の紹介した stackoverflow の記事に少し記述があります。Microsoft のドキュメントでは「ASP.NET アプリ間での認証の共有」に記載があるのを見つけました。

注3

上に書いたミドルウェアが動くようにするには、Startup.cs の Configure メソッドでの登録が必要です。その具体例は以下の通りです。場所は既存の app.UseEndpoints の直前で良いです。

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{

    // ・・・中略・・・

    // これを追記
    app.UseMiddleware<MvcCoreApp.Middleware.AuthExpireCheckMiddleware>();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");

        endpoints.MapRazorPages();
    });
}

注4

上にも書きましたが、next.Invoke の前後で応答本文に書き込むというのはそもそもやってはいけないことのようですし(特に next.Invoke の前はエラーになるとのこと)、 <html></html> タグの外に書き込まれてしまうので、ユーザーへの通知を行うならミドルウェアではなく Login ページ内で行うべきと思います。

認証に「個別のユーザーアカウント」を選んでプロジェクトを作成すると Login, Register 他の認証関係の機能は Razor Class Library (RCL) として提供され、ソースコードはプロジェクトには含まれないというところが問題ですが、スキャフォールディング機能を利用して Login ページをオーバーライドすることで対応できそうです。

Tags: , , ,

Authentication

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年4月  >>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar