WebSurfer's Home

Filter by APML

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

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

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

by WebSurfer 31. October 2020 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

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

by WebSurfer 23. October 2020 18:22

先の記事「Forms 認証のタイムアウト判定」の ASP.NET Identity 版です。(Core ではなく .NET Framework 用の ASP.NET Identity です。Core 3.1 版は別の記事「ASP.NET Core Identity タイムアウト判定」に書きました)

有効期限切れの通知

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

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

そこで、一旦認証を受けたユーザーがログオフせずブラウザを立ち上げたまま長時間席を外すなどして、タイムアウトに設定した時間を超えてアクセスしなかった場合を考えてください。

ユーザーが席に戻ってきて再度アクセスした場合、アクセスしたページが匿名アクセスを許可してなければ、ログインページにリダイレクトされます。

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

User.Identity.IsAuthenticated ではダメです。認証されているか否かは判定できますが、認証チケットが期限切れになったのか、それとも最初からログインしてなかったのかはわかりませんから。

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

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

認証クッキーと認証チケットは違うことに注意してください。クッキーはチケットの入れ物に過ぎません。認証チケットの有効期限は認証クッキーの Value の中に入っている情報の一つです。HttpCookie.Expires ではありません。

一般的に、一旦認証クッキーの発行を受ければ、ブラウザを閉じない限り次の要求の時にブラウザはサーバーにクッキーを送ります。(注:認証クッキーを「永続化」している場合は話が違ってきます。詳しくは別の記事「Froms 認証クッキーの永続化」を見てください)

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

しかし、認証クッキーを復号する方法が旧来のフォーム認証の場合とは全く異なっているのが問題でした。そこは、ググって調べた記事 ASP.NET Identity 2.0 decrypt Owin cookieReading Katana Cookie Authentication Middleware’s Cookie from FormsAuthenticationModule を参考にさせていただきました。

記事のコードでは MemoryStream などを使って ClaimsIdentity などを取得していますが、前者の記事のコメント欄に書いてあるように TicketSerializer クラスの Deserialize メソッドを使って AuthenticationTicket オブジェクトを取得し、それから必要な情報を得る方が良さそうです。

と言うより、認証チケットが有効期限切れかどうかは、AuthenticationTicket.Properties で取得できる AuthenticationProperties の ExpiresUtc プロパティをチェックしないと分からないので、そうせざるを得ないようです。

コードは以下のように HTTP モジュールとして実装してみました。上の画像がこのモジュールにより認証チケットの期限切れをチェックして Login 画面上でユーザーに通知したものです。

ただし、ユーザーへの通知を下のコードの最後の方にあるように Response.Write を使って書き込んだ場合、見かけは上の画像のようになりますが、実は <html></html> タグの外(この例では先頭)に書き込まれています。

なので、ユーザーへの通知が必要なら、HTTP モジュールで通知を行うのではなく、Login ページに下のようなコードを含めて判定し、ページの所定の位置に書き込むようにする方がよさそうです。

using System;
using System.Linq;
using System.Web;
using System.Security.Claims;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.DataHandler.Serializer;

namespace Mvc5ProfileInfo.Modules
{
    public class MyHttpModule : IHttpModule
    {
        // IHttpModule に Dispose メソッドが含まれるので定義が必要
        public void Dispose()
        {
            
        }

        public void Init(HttpApplication context)
        {
            context.AuthenticateRequest += Context_AuthenticateRequest;
        }

        private void Context_AuthenticateRequest(object sender, EventArgs e)
        {
            HttpRequest request = HttpContext.Current.Request;

            // 認証チケットの期限が切れた後、認証が必要なページにアクセス
            // して Login ページにリダイレクトされた時のみ対応します。
            // "/Account/Login" は実際に合わせて適宜変更してください
            if (request.CurrentExecutionFilePath != "/Account/Login")
            {
                return;
            }

            // 認証クッキーが送られてくる時のみ対応します。クッキー名の
            // .AspNet.ApplicationCookie はデフォルト。必要あれば変更
            HttpCookie cookie = request.Cookies.Get(".AspNet.ApplicationCookie");
            if (cookie == null) 
            { 
                return; 
            }

            // (注1)・・・下の注記参照
            string ticket = cookie.Value;
            ticket = ticket.Replace('-', '+').Replace('_', '/');

            int padding = 3 - ((ticket.Length + 3) % 4);
            if (padding != 0)
            {
                ticket = ticket + new string('=', padding);
            }

            byte[] bytes = Convert.FromBase64String(ticket);

            // (注2)・・・下の注記参照
            bytes = System.Web.Security.MachineKey.Unprotect(bytes,
                "Microsoft.Owin.Security.Cookies.CookieAuthenticationMiddleware",
                "ApplicationCookie", "v1");

            TicketSerializer serializer = new TicketSerializer();
            AuthenticationTicket authTicket = serializer.Deserialize(bytes);

            // 以下いろいろ書いてあるが必要なのは AuthenticationProperties
            // オブジェクトの ExpiresUtc プロパティで取得できる有効期限。
            // 他はその他にもいろいろ情報が取得できることを示したのみ
            ClaimsIdentity identity = authTicket.Identity;
            AuthenticationProperties property = authTicket.Properties;

            if (identity != null && property != null)
            {
                ClaimsPrincipal principal = new ClaimsPrincipal(identity);

                // クッキーが送られてくると有効期限が切れていても
                // ClaimsIdentity.IsAuthenticated は true になる
                bool isAuthenticated = identity.IsAuthenticated;
                string UserName = identity.Name;
                string authType = identity.AuthenticationType;

                // 追加したプロファイル情報 HandleName の取得
                Claim claim = identity.Claims.
                        FirstOrDefault(c => c.Type == ClaimTypes.GivenName);
                string handleName = claim.Value;

                // ExpiresUtc と現在の時刻を比較して期限切れか否かを判定
                DateTimeOffset? expiersUtc = property.ExpiresUtc;
                if (expiersUtc.Value < DateTimeOffset.UtcNow)
                {
                    // 知らせるための処置を書く
                    // 以下のコードでは <html></html> 外に書き込まれるので注意
                    HttpContext.Current.Response.Write("<h1><font color=red>" +
                      handleName +
                      " さん、認証チケットの有効期限が切れています" +
                      "</font></h1><hr>");
                }
            }
        }
    }
}

(注 1) 認証チケットのバイト列をクッキーに設定する際 Base64 でエンコードされますが、その際 URL アプリケーションのための変形が行われ '+' は '-' に、'/' は '_' に変換され、パディング '=' は除去されます。エンコードには ITextEncoder.Encode Method (Byte[]) が使われているようです。なので、そのデコードには ITextEncoder.Decode Method (String) を使えば文字列を変換するコードを書かなくてもバイト列を取得できます。具体的には以下の通りです(検証済み)。

byte[] bytes = Microsoft.Owin.Security.DataHandler.Encoder.
               TextEncodings.Base64Url.Decode(cookie.Value);

(注 2) 認証チケットのバイト列は MachineKey.Protect(Byte[], String[]) メソッドを使って暗号化されているようです。復号するには MachineKey.Unprotect(Byte[], String[]) メソッドを使いますが、その際、第 2 引数には暗号化するときに Protect メソッドで使用した値と同じものを設定する必要があります(違うと CryptographicException がスローされます)。

では、その第 2 引数は何になるかですが、自分が探した限りでは Microsoft の公式文書は見つかりませんでした。ググって見つかった記事の中で一番詳しかったのが「ASP.NET Cookie是怎么生成的」で、それによると以下のようにするのと同じになるようです(検証済み)。

string purpose1 = typeof(Microsoft.Owin.Security.Cookies.
                         CookieAuthenticationMiddleware).FullName;
string purpose2 = Microsoft.AspNet.Identity.
                  DefaultAuthenticationTypes.ApplicationCookie;
string purpose3 = "v1";
bytes = System.Web.Security.MachineKey.Unprotect(bytes, 
                                    purpose1, purpose2, purpose3);

(注 3)上に書いた HTTP モジュールを動かすには web.config での登録が必要です。その具体例は以下の通りです。

<system.webServer>
  <modules>
    <add name="MyHttpModule" type="Mvc5ProfileInfo.Modules.MyHttpModule"/>
  </modules>
</system.webServer>

Tags: , ,

Authentication

About this blog

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

Calendar

<<  January 2025  >>
MoTuWeThFrSaSu
303112345
6789101112
13141516171819
20212223242526
272829303112
3456789

View posts in large calendar