WebSurfer's Home

Filter by APML

ASP.NET Core Identity の SecurityStamp

by WebSurfer 8. November 2024 18:06

ASP.NET Core Identity の SecurityStamp についていろいろ調べましたので、調べたことを備忘録として書いておきます。

どういう機能かを簡単に書くと、複数のユーザーが同じアカウントで同時にログインしている場合に、あるユーザーがパスワードの変更など重要なセキュリティに関わる変更を行った場合、他のログインユーザーの次回のアクセス時にサインアウトさせるというもので、デフォルトで有効に設定されています。

ASP.NET Core Identity が使うデータベースの AspNetUsers テーブルの SecuriryStamp フィールドと、認証クッキーに含まれる SecuriryStamp の値を使って上記の機能を実現しています。

下の画像はデータベースの AspNetUsers テーブルの SecuriryStamp 列の各ユーザーの値を SQL Server Management Studio で見たものです。

AspNetUsers テーブルの SecurityStamp 列

ユーザーがログインすると認証クッキーが発行されますが、その際データベースの SecurityStamp の値を Claim として認証クッキーに含めます。下の画像の赤枠部分を見てください。上の画像のデータベースの SecurityStamp の値と同じになっています。

認証クッキーに含まれる SecurityStamp

一旦ユーザーがログインに成功すると、それ以降はそのユーザーがサイトにアクセスしてくるたび認証クッキーが送信され、送られてきた認証クッキーをチェックして有効であればログインが継続されるという仕組みになっています。

ログインしたユーザーは自分のアカウントの設定を変更する管理画面にアクセスできます。下の画面は自分のパスワードを変更するページです。

パスワード変更画面

上の画面でパスワードの変更に成功すると、同時にデータベースの SecuriryStamp の値が変更されます。

パスワードを変更した際、ユーザーには認証クッキーが再発行されます。それにより、変更後のデータベースの SecuriryStamp の値と再発行された認証クッキーに含まれる SecuriryStamp の値は同じになります。

一方、同じアカウントでログインしている他のユーザーには認証クッキーは再発行されませんので、認証クッキーに含まれる SecuriryStamp の値は古いまま、すなわちデータベースと認証クッキーの SecurityStamp とは異なった値になります。

なので、あるユーザーによるパスワード変更後、他のユーザーがアクセスして来た時にデータベースと認証クッキーの SecuriryStamp の値を比較すれば異なっているので、そのユーザーをサインアウトさせることが可能です。

ただし、比較するにはデータベースにクエリを投げてデータベースの SecuriryStamp の値を取得してくる必要があります。それをユーザーがアクセスしてくるたびに行うのはサーバーの負担が大きいという問題があります。それゆえインターバル(デフォルトで 30 分)を設けています。

例えば 10,000 人のユーザーが同時にログインしていて、一人当たり 1 分に 5 回アクセスしてくるとします。インターバルを設けないでアクセスのたび毎回 SecurityStamp の比較を行うとすると、1 分間に 50,000 回データベースにクエリを投げることになります。

インターバルを 1 分にすると、ユーザー当たり 1 分に 5 回のリクエストの内 4 回は検証しないので 1/5 に、すなわち全体では 10,000 回/分に減ります。

さらに、インターバルを 30 分に増やすと 1/150 に、すなわち全体では 333 回/分に減ります。その程度であれば問題ないであろうということでデフォルトが 30 分になっているようです。

ただし、インターバルが短いほどセキュリティは高くなるはずなので、ユーザーが少なくアクセスの少ないサイトであれば短くした方が良さそうです。

Program.cs に以下のコードを追加することによりインターバルを変更できます。

// インターバルは FromMinutes(m) の m で設定。下のコードは 30 分
builder.Services.Configure<SecurityStampValidatorOptions>(options =>
        options.ValidationInterval = TimeSpan.FromMinutes(30));

詳しくは Microsoft のドキュメント「ISecurityStampValidator とすべてからのサインアウト」に書いてありますので見てください。


以上で分かった気になっていたのですが、よく考えてみると「チェックを 30 分のインターバルで行うとして、そのタイミングと、ユーザーがアクセスするタイミングは当然異なるはずだが、そこはどうしているのか?」が疑問です。そのあたりをさらに調べてみました。

上に紹介したMicrosoft のドキュメントには "Identity の既定の実装では、SecurityStampValidator がメインのアプリケーション cookie と 2 要素 cookie に登録されます。検証コントロールは、各 cookie の OnValidatePrincipal イベントにフックして Identity を呼び出し、ユーザーのセキュリティ スタンプ クレームが cookie に格納されているものから変更されていないことを確認します" と書いてあります。

具体的には、「メインのアプリケーション cookie」の方でいうと、そのために以下のオプション設定がデフォルトでされているということのようです。

builder.Services.ConfigureApplicationCookie(options =>
{
    // cookie 設定

    options.Events = new CookieAuthenticationEvents
    {
        OnValidatePrincipal = SecurityStampValidator.ValidatePrincipalAsync
    };
});

上のコードで行っているのは以下の通りです。

CookieAuthenticationOptionsEvents プロパティCookieAuthenticationEvents (Allows subscribing to events raised during cookie authentication) を設定。

CookieAuthenticationEvents の OnValidatePrincipal プロパティ (Invoked to validate the principal) に SecurityStampValidator クラスValidatePrincipalAsync 静的メソッド (Validates a principal against a user's stored security stamp) を設定

ということで、

  1. ユーザーが認証クッキーを持ってアクセスしてくる
  2. cookie authentication プロセスが行われる
  3. CookieAuthenticationEvents が種々イベントをサブスクライブしている
  4. ValidatePrincipal イベントが発生すると ValidatePrincipalAsync が invoke される
  5. cookie と DB の SecurityStamp が異なっているとユーザーをサインアウトさせる

・・・というプロセスになるようです。

ValidatePrincipal イベントが発生するたび ValidatePrincipalAsync が無条件に呼ばれるようですが、その際上のステップ 5 を行うか否かにインターバルが関係しています。

そのあたりの詳細はググって調べてヒットするドキュメントでは分からなかったので Copilot に聞いてみたところ以下の回答でした。要するにミドルウェアが良しなにやっているとのことです。

  1. Initial Login: When the user logs in, an authentication ticket with the security stamp is issued and stored in the authentication cookie.
  2. Request Handling: When a request comes in, the authentication middleware reads the cookie and the authentication ticket.
  3. Interval Check: The middleware checks if the time since the last validation exceeds the ValidationInterval. This check is done based on the current time and the internally tracked last validation timestamp.
  4. Update: If the validation occurs, the middleware updates its internal record of the last validation time.

AI の回答は間違っていることもあるので、裏を取るため SecurityStampValidator.cs の ValidatePrincipalAsync メソッドが呼び出す ValidateAsync メソッドのコードを調べてました。Copilot の言っていることに間違いはなさそうです。

参考に SecurityStampValidator.cs の ValidateAsync メソッドのコードをコピーして以下に載せておきます

public virtual async Task ValidateAsync(CookieValidatePrincipalContext context)
{
    var currentUtc = TimeProvider.GetUtcNow();
    var issuedUtc = context.Properties.IssuedUtc;
 
    // Only validate if enough time has elapsed
    var validate = (issuedUtc == null);
    if (issuedUtc != null)
    {
        var timeElapsed = currentUtc.Subtract(issuedUtc.Value);
        validate = timeElapsed > Options.ValidationInterval;
    }
    if (validate)
    {
        var user = await VerifySecurityStamp(context.Principal);
        if (user != null)
        {
            await SecurityStampVerified(user, context);
        }
        else
        {
            Logger.LogDebug(EventIds.SecurityStampValidationFailed, 
                  "Security stamp validation failed, rejecting cookie.");
            context.RejectPrincipal();
            await SignInManager.SignOutAsync();
            await SignInManager.Context.SignOutAsync(
                          IdentityConstants.TwoFactorRememberMeScheme);
        }
    }
}

その他気が付いたことも書いておきます。

上に紹介した Microsoft のドキュメントに書いてありますが、データベースの SecurityStamp を変更するには userManager.UpdateSecurityStampAsync(user) を呼び出します。

上に書いた管理画面でのパスワード変更にはそのメソッドが実装されているようで、パスワード変更操作でデータベースの SecurityStamp が変更されます。

ユーザーにアサインされるロールが変更された場合もデータベースの SecurityStamp を変更した方が良さそうですが、先の記事「ASP.NET Identity のロール管理 (CORE)」に書いた自作のメソッド EditRoleAssignment の AddToRoleAsync, RemoveFromRoleAsync ではデータベースの SecurityStamp は変わりません。SecurityStamp を変更するには userManager.UpdateSecurityStampAsync(user) の呼び出しを追加で実装する必要があります。

また、Microsoft のドキュメントに書いてあった sign out everywhere については、Logout.cshtml.cs に以下のように追加すれば実現できます。

public async Task<IActionResult> OnPost(string returnUrl = null)
{
    // ログアウトで SecurityStamp を再生成するため追加
    var user = await _userManager.GetUserAsync(User);
    await _userManager.UpdateSecurityStampAsync(user);

    await _signInManager.SignOutAsync();
    _logger.LogInformation("User logged out.");
    if (returnUrl != null)
    {
        return LocalRedirect(returnUrl);
    }
    else
    {
        // This needs to be a redirect so that the browser performs a new
        // request and the identity for the user gets updated.
        return RedirectToPage();
    }
}

もう一つ気になっていることがあります。それはデフォルトで有効になっている SlidingExpiration の影響です。これが働くと有効期間が延長された認証クッキーが再発行されますが、それによる SecurityStamp への影響が不明です。調べる気力がなくなったのでまだ未確認ですが、分かったら追記します。

Tags: , ,

Authentication

JWT からクレーム情報を取得

by WebSurfer 20. August 2024 16:47

トークンベース (JWT) の認証を実装した ASP.NET Core Web API アプリで、クライアントから送信されてきた JWT から、ユーザー名やトークンの有効期限などの情報を取得する方法を書きます。

デコードされた JWT

実は最近知ったのですが、先の記事「ASP.NET Core Web API に Role ベースの承認を追加」に書いた JWT による認証を実装した ASP.NET Core Web API アプリでは、上の画像の JWT の Payload 部分は ASP.NET Core で言うクレームのコレクションとして取り扱われるようです。

具体的に言うと、JWT の Payload の項目から ClaimsIdentity オブジェクトが作られ、コントローラーに使われる ControllerBase クラスの User プロパティで取得できる ClaimsPrincipal オブジェクトに ClaimsIdentity が含まれるようになります。

なので、上の画像のように Payload に Role を追加すれば、先の記事に書いたように [Authorize(Roles = "Admin")] 属性によって Web API 側でアクセス制限ができるようになります。

さらに、ClaimsIdentity オブジェクトの各クレームの値は ClaimsPrincipal クラスの FindFirst(String) メソッドを使って取得できますので、必要があればサーバー側でユーザー名とかトークンの有効期限などを取得して、その内容に応じて何らかの処置を行うというようなことも可能になります。

以下に具体例を書いておきます。

まず、上の画像のように JWT の Payload に Role、UserId、exp を含める方法ですが、先の記事「Blazor WASM から ASP.NET Core Web API を呼び出し」で紹介したトークンを発行する API の BuildToken メソッドで、JwtSecurityToken コンストラクタの引数の claims に以下のようにロール情報と UserId を追加します。上の画像の JWT の Payload の "exp" は引数 expires に設定した UNIX 時間となります。

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

namespace WebApi.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class TokenController : ControllerBase
    {
        private readonly IConfiguration _config;
        private readonly UserManager<IdentityUser> _userManager;

        public TokenController(IConfiguration config,
                               UserManager<IdentityUser> userManager)
        {
            _config = config;
            _userManager = userManager;
        }

        [AllowAnonymous]
        [HttpPost]
        public async Task<IActionResult> CreateToken(LoginModel login)
        {
            string? id = login.Username;
            string? pw = login.Password;
            IActionResult response = Unauthorized();

            if (!string.IsNullOrEmpty(id) && !string.IsNullOrEmpty(pw))
            {
                var user = await _userManager.FindByNameAsync(id);
                if (user != null && 
                    await _userManager.CheckPasswordAsync(user, pw))
                {
                    // クライアントから送信されてきた id を UserId 
                    // として JWT の Payload に含める
                    var tokenString = BuildToken(id);

                    response = Ok(new { token = tokenString });
                }
            }

            return response;
        }

        private string BuildToken(string userId)
        {
            var key = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(_config["Jwt:Key"]!));

            var creds = new SigningCredentials(
                key, SecurityAlgorithms.HmacSha256);

            var token = new JwtSecurityToken(
                issuer: _config["Jwt:Issuer"],
                audience: _config["Jwt:Issuer"],

                // Role 情報と UserID を追加
                claims: [ 
                    new Claim(ClaimTypes.Role, "Admin"),
                    new Claim("UserId", userId)
                ],

                notBefore: null,
                expires: DateTime.Now.AddMinutes(30),
                signingCredentials: creds);

            return new JwtSecurityTokenHandler().WriteToken(token);
        }
    }

    public class LoginModel
    {
        public string? Username { get; set; }
        public string? Password { get; set; }
    }
}

Web API のコントローラーで、ControllerBase.User プロパティから取得できる ClaimsPrincipal オブジェクトの FindFirst(String) メソッドを使って、JWT の Payload の情報を取得できます。

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;

namespace WebApi.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class WeatherForecastController : ControllerBase
    {
        // ・・・中略・・・

        [Authorize(Roles = "Admin")]
        [HttpGet(Name = "GetWeatherForecast")]
        public IEnumerable<WeatherForecast> Get()
        {
            // 2024/8/20
            // JWT の Payload の情報は以下のようにして取得できる
            string? role = User.FindFirst(ClaimTypes.Role)?.Value;
            string? userId = User.FindFirst("UserId")?.Value;
            string? exp = User.FindFirst("exp")?.Value;  // UNIX 時間
            if (exp != null)
            {
                var ticks = long.Parse(exp) * 1000L * 1000L * 10L + 
                            DateTime.UnixEpoch.Ticks;
                var expDateTime = new DateTime(ticks, DateTimeKind.Utc);
                var dateTimeUtcNow = DateTime.UtcNow;
                bool isBeforeExp = expDateTime > dateTimeUtcNow;
            }

            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
                TemperatureC = Random.Shared.Next(-20, 55),
                Summary = Summaries[Random.Shared.Next(Summaries.Length)]
            })
            .ToArray();
        }
    }
}

クライアントから上の画像の JWT を送信した結果は下の画像の通りとなります。Web API のコントローラーで上の画像の Payload の情報を取得できていることが分かりますでしょうか?

JWT の Payload 情報の取得結果



【オマケ】

本題とは直接関係ないことですが、Microsoft のドキュメント「ASP.NET Core でのクレーム ベースの承認」に ClaimsIdentity というのは何かを、運転免許証を例にとって説明してあって分かりやすかったので、忘れないように以下に抜粋を貼っておきます。

"運転免許証にはあなたの生年月日が記載されています。 この場合、クレーム名は DateOfBirth になり、クレームの値は、たとえば 8th June 1970 となります。そして発行者は、運転免許証機関になります。クレーム ベースの承認では、簡単に言うと、クレームの値がチェックされ、その値に基づいてリソースへのアクセスが許可されます。たとえば、ナイト クラブへの入場 (アクセス) であれば、承認プロセスは次のようになる可能性があります。"

"出入口のセキュリティ責任者が、あなたの生年月日クレームの値と、発行者 (運転免許機関) を信頼するかどうかを評価します。"

ちなみに、ClaimsIdentity クラスの解説は以下のようになっています。

"ClaimsIdentity クラスは、クレームベースの ID の具体的な実装です。つまり、クレームのコレクションによって記述される ID です。クレームは発行者によるエンティティに関する宣言で、プロパティ、権利、またはその他の品質が記述されます。このようなエンティティは、クレームの対象と言われます。クレームは Claim クラスによって表されます。ClaimsIdentity に含まれるクレームは、対応する ID が表すエンティティに記述し、承認と認証の決定を行うために使用できます。"

免許証の例を読んでからでないと分かりにくいかも。何を隠そう、自分はさっぱり分かりませんでした。(笑)

Tags: , , ,

Authentication

認証チケットの期限切れをユーザーに通知

by WebSurfer 11. May 2024 16:15

ASP.NET Core Identity を使ったユーザー認証システムで認証チケットの有効期限が切れたとき、下の画像のようにユーザーにその旨通知する方法を書きます。

認証チケット有効期限切れの通知

先の記事「ASP.NET Core Identity タイムアウト判定」で書いたこととほぼ同じで、違うのは先の記事ではミドルウェアを使っていたのを Login ページで行うようにし、ターゲットフレームワークを .NET Core 3.1 から .NET 8.0 に変更した点です。

Visual Studio 202 のテンプレートを使って ASP.NET Core Web アプリのプロジェクトを作る際、認証を「個別のユーザーアカウント」に設定すると ASP.NET Core Identity を利用したクッキーベースのユーザー認証が実装されます。

ユーザーがログインに成功すると、サーバーからは応答ヘッダの Set-Cookie に認証チケットを入れて認証クッキーとしてクライアント(ブラウザ)に送信します。

その後は、ブラウザは要求の都度サーバーに認証クッキーを送信するので認証状態が継続されるという仕組みになっています。

認証チケットには有効期限があります (デフォルトで 5 分、SlidingExpiration で延長あり)。一旦認証を受けたユーザーがログオフせずブラウザを立ち上げたまま長時間席を外すなどして、タイムアウトに設定した時間を超えてアクセスしなかった場合を考えてください。

タイムアウトとなった後でユーザーが席に戻ってきて再度どこかのページを要求したとすると、要求したページが匿名アクセスを許可してなければ、ログインページにリダイレクトされます。

その際、ユーザーに認証チケットが期限切れとなっていることを知らせるためには、どのようにできるかということを書きます。

一旦認証を受けたが、認証チケットが期限切れになっているというのは、Web サーバー側では以下の条件で判定できるはずです。

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

一旦認証クッキーの発行を受ければ、ブラウザを閉じない限り要求の都度サーバーにクッキーを送り続けます。(下の「注 1」に書きましたが、[Remember me?]チェックボックスにチェックを入れて認証を受けた場合は話が違ってくるので注意してください)

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

ログインページで条件を確認して、認証チケットが期限切れになっていた場合はこの記事の一番上の画像のようにメッセージを表示してみました。

以下がログインページのコードです。スキャフォールディング機能を利用して自動生成した Login.cshtml.cs, Login.cshtml のコードに手を加えています。(スキャフォールディング方法の詳細は別の記事「ASP.NET Core MVC プロジェクトに Identity 実装」を見てください)

Login.cshtml.cs

コメントで「2024/5/11 追加」としたコードを自動生成された Login.cshtml.cs に追加しています。

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#nullable disable

using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Authentication;
// ・・・中略・・・

namespace MvcNet8App2.Areas.Identity.Pages.Account
{
    public class LoginModel : PageModel
    {
        private readonly SignInManager<ApplicationUser> _signInManager;
        private readonly ILogger<LoginModel> _logger;

        // 2024/5/11 追加(下の「注 2」参照)
        private readonly CookieAuthenticationOptions _options;

        public LoginModel(SignInManager<ApplicationUser> signInManager, 
                          ILogger<LoginModel> logger,

                          // 2024/5/11 追加(下の「注 2」参照)
                          IOptions<CookieAuthenticationOptions> options)
        {
            _signInManager = signInManager;
            _logger = logger;

            // 2024/5/11 追加(下の「注 2」参照)
            _options = options.Value;
        }

        // ・・・中略・・・

        public async Task OnGetAsync(string returnUrl = null)
        {
            // 2024/5/11 追加・・・
            // .AspNetCore.Identity.Application はデフォルトのクッキー名
            string cookie = Request.Cookies[".AspNetCore.Identity.Application"];
            if (!string.IsNullOrEmpty(cookie))
            {
                IDataProtectionProvider provider = 
                                      _options.DataProtectionProvider;

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

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

                // 下の「注 3, 4」参照
                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)

                {
                    ViewData["AuthTicket"] = $"{userName} さん、"+
                      $"認証チケットの有効期限 {expiersUtc.Value} が切れています。";
                }
            }
            // ・・・追加はここまで

            // ・・・中略・・・
        }

        public async Task<IActionResult> OnPostAsync(string returnUrl = null)
        {
            
            // ・・・中略・・・

        }
    }
}

Login.cshtml

認証ケットが期限切れの場合は cshtml から ViewData で情報をもらって期限切れのメッセージを表示するための一行を追加したのみです。

@page
@model LoginModel

@{
    ViewData["Title"] = "Log in";
}

<h1>@ViewData["Title"]</h1>

@* 2024/5/11 以下の一行を追加 *@
<p><font color=red>@ViewData["AuthTicket"]</font></p>

<div class="row">
    <div class="col-md-4">
        <section>

・・・中略・・・

</div>

@section Scripts {
    <partial name="_ValidationScriptsPartial" />
}

注 1 : デフォルトのログインページには[Remember me?]チェックボックスがあって、これにチェックを入れて認証を受けると Set-Cookie ヘッダに expires 属性が追加され認証クッキーが HDD / SSD に保存されます。expires 属性に指定される期限は認証チケットの有効期限と同じになります。期限を過ぎるとブラウザ側で認証クッキーを削除してしまいますので、条件 1 の「要求 HTTP ヘッダーに認証クッキーが含まれる」ことの判定はできなくなります。

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

Visual Studio で「個別のユーザーアカウント」を認証に選んで作成したプロジェクトまたはスキャフォールディング機能を使って ASP.NET Core Identity を追加したプロジェクトでは以下のコードが Program.cs 含まれるはずです。その場合、コンストラクタの引数に IOptions<CookieAuthenticationOptions> options を含めるだけで DI 機能が働きます。

builder.Services.AddDefaultIdentity<ApplicationUser>(options => 
    options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>()

注 3 : IDataProtectionProvider.CreateProtector メソッドの引数ですが、これには暗号化するときに使用した値 (暗号化を特定の目的にロックするためのものらしい)と同じものを設定する必要があります。それが CookieAuthenticationMiddleware の名前、認証クッキー名、ASP.NET Identity のバージョンになるようです。

暗号化するときに使用した値と、上の CreateProtector メソッドの引数に指定したものが違うと復号に失敗し、上のコードで authTicket が null になり、次の行の authTicket.Principal で NullReferenceException がスローされます。

注 4 : 別の記事「ASP.NET Core でのデータ保護キーの管理」で書きましたがデータ保護キーがリサイクルで失われることがあります。その場合は復号に失敗し、上のコードで authTicket が null になり、次の行の authTicket.Principal で NullReferenceException がスローされます。

リサイクルで失われるようなことは無くても、データ保護キーの有効期限はデフォルトで 90 日だそうですので、有効期限を過ぎると復号に失敗すると思われます (未検証・未確認です)。

Tags: , , , ,

Authentication

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。ブログ2はそれ以外の日々の出来事などのトピックスになっています。

Calendar

<<  February 2025  >>
MoTuWeThFrSaSu
272829303112
3456789
10111213141516
17181920212223
242526272812
3456789

View posts in large calendar