PR

JWT を C++ で扱う方法

1. はじめに

JWT(JSON Web Token)は、JSON 形式で情報をやり取りするための仕様です。
JWT は、ヘッダ、ペイロード、署名の 3 つの部分から構成されています。
ヘッダは、アルゴリズムやトークンの種類を示します。
ペイロードは、トークンに含める情報を示します。
署名は、ヘッダとペイロードを結合して暗号化したものです。

JWT を C++ で扱うためには、以下のライブラリが利用できます。
JWT C++ の中で、以下のライブラリを使用します
Thalhammer/jwt-cpp
選んだ理由としては、Visual Studio 2022 の NuGet でインストールできるからです。

2. NuGet で Thalhammer/jwt-cpp をインストールする

Visual Studio 2022 で、NuGet パッケージマネージャーを開きます。
ツールバーの「プロジェクト」→「NuGet パッケージの管理」を選択します。
参照より「jwt-cpp」を検索し、インストールします。
packages フォルダが作成され、依存関係のライブラリもまとめてインストールされます。

3. サンプルコード実装

Thalhammer/jwt-cpp の Getting Started にあるサンプルコードをコピペします。

#include <jwt-cpp/jwt.h>
#include <iostream>

#include <jwt-cpp/jwt.h>
#include <iostream>

int main() {
    std::string token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCIsInNhbXBsZSI6InRlc3QifQ.lQm3N2bVlqt2-1L-FsOjtR6uE-L4E9zJutMWKIe1v1M";
    auto decoded = jwt::decode(token);

    for(auto& e : decoded.get_payload_json())
        std::cout << e.first << " = " << e.second << std::endl;
}

ビルドして実行すると、以下のように出力されます。

iss = "auth0"
sample = "test"

インストールした NuGet パッケージはソリューションとして管理されるためディレクトリの追加などは不要です。便利ですね。

4. サンプルコード実装2

引き続き、Getting Started にあるサンプルコードをコピペします。

    auto verifier = jwt::verify()
        .with_issuer("auth0")
        .with_claim("sample", jwt::claim(std::string("test")))
        .allow_algorithm(jwt::algorithm::hs256{ "secret" });
    verifier.verify(decoded);

こちらは、hs256 で暗号化されたトークンを検証するコードです。
NuGet で openssl-vc141-static-x86_64 がインストールされていますが、バージョンが古いためこちらアンインストールします。
かわりに OpenSSL の最新版をソースからビルドしインストールします。← これは、別途記事にします。

トークンの検証に成功すると、エラーが発生せずにプログラムが終了します。
エラーが発生した場合は、例外がスローされるので、try-catch で囲む必要があります。

#include <jwt-cpp/jwt.h>
#include <openssl/ssl.h>
#include <iostream>
#pragma comment(lib, "libcrypto.lib")
#pragma comment(lib, "libssl.lib")

int main() {
    std::string token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCIsInNhbXBsZSI6InRlc3QifQ.lQm3N2bVlqt2-1L-FsOjtR6uE-L4E9zJutMWKIe1v1M";
    std::string token1 = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCIsInNhbXBsZSI6InRlc3QifQ.lQm3N2bVlqt2-1L-FsOjtR6uE-L4E9zJutMWKIe1v2M";
    auto decoded = jwt::decode(token);
    for (auto& e : decoded.get_payload_json())
        std::cout << e.first << " = " << e.second << std::endl;

    auto verifier = jwt::verify()
        .with_issuer("auth0")
        .with_claim("sample", jwt::claim(std::string("test")))
        .allow_algorithm(jwt::algorithm::hs256{ "secret" });
    try {
        verifier.verify(jwt::decode(token));
        std::cout << "verify OK" << std::endl;
        verifier.verify(jwt::decode(token1));
        std::cout << "verify OK" << std::endl;
    }
    catch (const std::exception& e) {
        std::cout << "verify NG " << e.what() << std::endl;
    }
}

std::string token1 は、エラーを発生させるために、ペイロードの後ろ2文字目を変更しています。(1 → 2)

以下が出力されます。

iss = "auth0"
sample = "test"
verify OK
verify NG invalid signature

一つ目が成功し、二つ目が失敗しています。意図した動作ですね。

5. サンプルコード実装3

最後に自分でトークンを生成してみます。

#include <jwt-cpp/jwt.h>
#include <openssl/ssl.h>
#include <iostream>
#pragma comment(lib, "libcrypto.lib")
#pragma comment(lib, "libssl.lib")

int main() {
    std::string token = jwt::create()
        .set_type("JWT")
        .set_issuer("auth0")
        .set_payload_claim("sample", jwt::claim(std::string("test")))
        .sign(jwt::algorithm::hs256{ "secret" });
    std::cout << token << std::endl;
    auto decoded = jwt::decode(token);
    for (auto& e : decoded.get_payload_json())
        std::cout << e.first << " = " << e.second << std::endl;
}

トークンを生成すると、以下のように出力されます。

token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCIsInNhbXBsZSI6InRlc3QifQ.lQm3N2bVlqt2-1L-FsOjtR6uE-L4E9zJutMWKIe1v1M
iss = "auth0"
sample = "test"

set_type("JWS") としているため、3. サンプルコード実装 のトークンと同じになります。

6. まとめ

JWT を C++ で扱うためのライブラリとして、Thalhammer/jwt-cpp を使用しました。
アルゴリズムやペイロードの情報を設定して、トークンを生成することができました。
また、生成したトークンを検証することもできました。
面倒な暗号化処理をライブラリが提供してくれるため、簡単に JWT を扱うことができます。
参考になれば幸いです。

A. 参考サイト

基本から理解するJWTとJWT認証の仕組み
JWT C++
Thalhammer/jwt-cpp
C++プロジェクトでNuGetを使ってみようよ!(Vol. 1:パッケージ導入編)

B. 参考書籍

コメント