C# による JWT の生成と検証
ここでは JSON Web Token (JWT) を C# で利用する方法について説明します。
C# で利用できる JWT ライブラリ
C# で JWT (JSON Web Token) を利用するためのライブラリとして人気があるのが、その名の通りの JWT パッケージです。 Nuget を利用することで簡単に C# プロジェクトに取り込むことができます。
2019年12月現在、最新安定バージョンは 5.3.1 です。5.3.1 は .NET Framework 4.6 以降、.NET Standard 1.3 以降で利用可能です。
JWT 5.3.1 を Visual Studio プロジェクトにインストールするには、次のコマンドでインストールします。
> Install-Package JWT -Version 5.3.1
バージョン 6 以降で Newtonsoft の JSON ライブラリの依存関係がなくなる予定ですが、バージョン 6 は現在まだベータ版です。このため 5.3.1 を使うことにします。
尚、C# で JWT を使うためのライブラリはマクロソフトの System.IdentityModel.Tokens.Jwt をはじめ、 jose-jwt、jose-rt など各種あります。
私が試した限り、この JWT ライブラリが最も直感的に使いやすく機能的にも十分と思います。
C# による JWT の生成方法
実際に JWT ライブラリを使って、トークンを生成してみましょう。
ここではトークンの有効期限を現在から 3 時間とします。
トークンの追加情報 (JWT ではクレーム (claim) といいます) としてユーザーを識別するため、メールアドレスを設定します。 さらにもうひとつ、任意のクレームとして data1 というのを設定しています。
using JWT.Algorithms;
using JWT.Builder;
using System;
namespace TestApp {
class Program {
const string secret = "secret_goes_here";
static void Main(string[] args) {
var jwtToken = new JwtBuilder()
.WithAlgorithm(new HMACSHA256Algorithm())
.WithSecret(secret)
.AddClaim("exp", DateTimeOffset.UtcNow.AddHours(3).ToUnixTimeSeconds())
.AddClaim("email", "foo@example.com")
.AddClaim("data1", "hello!")
.Build();
Console.WriteLine(jwtToken);
}
}
}
これを実行すると次のように出力されます。
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NzU2MzU0MjcsImVtYWlsIjoiZm9vQGV4YW1wbGUuY29tIiwiZGF0YTEiOiJoZWxsbyEifQ.J5OZHjyGBYfIW0NHV2JzKynU9vmdMstfXQFUFYqVj44
これが JWT トークンです。
典型的な使い方としては、ウェブの JavaScript クライアントアプリケーションで、認証時にトークンを受け取り localStorage に保存しておきます。 その後、サーバー側のバックエンドに接続が必要な場合、Authorization ヘッダにこのトークンをセットして HTTP(S) 要求を送ります。
この文字列が正しいトークンとして出来上がっているかどうか、どうすれば確認できるでしょうか。
C# による JWT の検証方法
JWT Debugger による検証方法
次のサイトに JWT Debugger というページがあります。
ここにトークンを貼り付けます。これでヘッダーとペイロードが入力済みになります。
そして、右側下側の VERIFY SIGNATURE (署名の検証) の中のシークレット (上の例では secret_goes_here という文字列) を入力します。
この結果、サイト上で出来上がったトークン (文字列) と一致すれば意図したトークンが生成できていると言って良いです。
JWT プログラムでの検証方法
JWT ライブラリを使って、トークンの検証を行う方法を説明します。
また、ここではトークンのペイロード部分を MyToken というクラスのオブジェクトとして作成します。
上で JWT を作成したので、引き続き作成した JWT を検証してみましょう。
using JWT;
using JWT.Algorithms;
using JWT.Builder;
using Newtonsoft.Json;
using System;
using System.Threading;
namespace TestApp {
class MyToken {
public long exp { get; set; }
public string email { get; set; }
public string data1 { get; set; }
}
class Program {
const string secret = "secret_goes_here";
static void Main(string[] args) {
// JWT の生成
var jwtToken = new JwtBuilder()
.WithAlgorithm(new HMACSHA256Algorithm())
.WithSecret(secret)
.AddClaim("exp", DateTimeOffset.UtcNow.AddHours(3).ToUnixTimeSeconds())
.AddClaim("email", "foo@example.com")
.AddClaim("data1", "hello!")
.Build();
Console.WriteLine(jwtToken);
// 検証
try {
var json = new JwtBuilder()
.WithSecret(secret)
.MustVerifySignature()
.Decode(jwtToken);
Console.WriteLine(json);
// JSON から MyToken オブジェクトへのデシリアライズ
var token = JsonConvert.DeserializeObject<MyToken>(json);
Console.WriteLine($"exp: {token.exp}");
Console.WriteLine($"email: {token.email}");
Console.WriteLine($"data1: {token.data1}");
}
catch (TokenExpiredException) {
Console.Error.WriteLine("The token has expired.");
}
catch (SignatureVerificationException) {
Console.WriteLine("Invalid signature.");
}
catch (Exception ex) {
Console.WriteLine($"{ex.Source}: {ex.Message}");
}
}
}
}
上のコードで検証が成功すると変数 json に、トークンのペイロード部分を表す JSON 文字列が返ります。
もし検証中に問題が検出されたときには、例外が発生して処理が中断します。
例えば、検証時点でトークンの有効期限が切れている場合、 TokenExpiredException 例外が発生します。
また、署名が正しくない場合 SignatureVerificationException 例外が発生します。
例外が発生しない時は、検証が成功したということです。つまり、改竄されていない、有効期限内のトークンであるということが確認できたということです。
var json = new JwtBuilder()
.WithSecret(secret)
.MustVerifySignature()
.Decode(jwtToken);
Console.WriteLine(json);
// JSON から MyToken オブジェクトへのデシリアライズ
var token = JsonConvert.DeserializeObject<MyToken>(json);
上述のように、この部分で変数 json には、トークン内のペイロード部分の JSON 文字列がセットされます。
C# で JSON 文字列をそのまま読み取るのは面倒なので、JsonConvert クラスの DeserializeObject メソッドで MyToken というクラスのオブジェクトに変換しています。
これによって、メールアドレスフィールドの値や、data1 フィールドにも簡単にアクセスできるようになります。