Skip to content

API 認証への JWT・OAuth 2.0 の追加 ​

第1版作成 最終更新 (日本時間)
確認バージョン1.5.8.1

プリザンターの API に Authorization: Bearer <JWT> での認証を追加する場合の検討です。既存の API キー認証を残したまま、認証スキームを追加する形を想定します。

本体の標準機能ではありません

1.5.8.1 のプリザンターは JWT Bearer 認証に対応していません。以下は現行の実装(1.5.8.1)を前提にした改修案です。

前提にした現行の実装 ​

詳細は 認証基盤(Cookie・API キー・Bearer) にあります。

  • 認証スキームは Cookie(SAML のときは Saml2 も)だけで、AddJwtBearer はありません(Startup.cs)。
  • API キーは本文の JSON の ApiKey で送り、Context.SetUserProperties が Users.ApiKey と照合します。ApiKey が無ければ Cookie のログイン ID を使います。
  • API コントローラーは [AllowAnonymous] と [CheckApiContextAttributes] を付け、アクションの中で context.Authenticated を確かめます。

現行の API キーには次の制約があります。

制約内容
有効期限が無い削除・再発行するまで有効
スコープが無いユーザーの権限すべてを使える
送る場所が本文Authorization ヘッダーで送れるのはファイルのアップロード API だけ
ローテーションの仕組みが無い再発行は本人が画面で行う
外部 IdP を使えないSAML / LDAP の認証は画面ログイン用で、API には使えない
監査API キーの発行・失効の履歴は残らない

方式 1: プリザンターが JWT を発行する ​

API キーでトークン発行エンドポイントを呼び、短い有効期限の JWT を受け取って、以後はそれを Authorization: Bearer で送る方式です。

図を読み込み中…

改修箇所 ​

ファイル変更内容
Implem.ParameterAccessor/Parts/Authentication.csJwtBearerParameters を追加
Implem.ParameterAccessor/Parts/JwtBearer.cs(新規)Enabled・Issuer・Audience・署名鍵(共有鍵または証明書)・アクセストークンの有効期間・リフレッシュトークンの有効期間など
App_Data/Parameters/Authentication.json上記の既定値(Enabled: false)
Startup.csEnabled のときだけ AddJwtBearer を登録。既定のスキームは Cookie のまま
Controllers/Api/AuthController.cs(新規)トークン発行。[AllowAnonymous]・[CheckApiContextAttributes] を付け、context.Authenticated なら JWT を返す
Libraries/Security/JwtTokenService.cs(新規)クレーム(NameIdentifier に UserId、Name に LoginId、TenantId、DeptId)と有効期限を入れて署名
Libraries/Requests/Context.csSetUserProperties に、API キーが無く JWT で認証済みならクレームの UserId で GetUser する分岐を追加
Filters/CheckApiContextAttributes.csJWT で認証したリクエストの扱い(CSRF トークンの確認の要否)を整理
Startup.cs と Context.cs の改修イメージ
csharp
// Startup.cs: Cookie のスキーム登録の後に追加
if (Parameters.Authentication.JwtBearerParameters?.Enabled == true)
{
    var jwt = Parameters.Authentication.JwtBearerParameters;
    authBuilder.AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidIssuer = jwt.Issuer,
            ValidateAudience = true,
            ValidAudience = jwt.Audience,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = GetSigningKey(jwt),
            ClockSkew = TimeSpan.FromMinutes(1)
        };
    });
}

// Context.SetUserProperties: ApiKey の分岐と LoginId の分岐の間に追加
else if (IsJwtAuthenticated())
{
    var userId = GetUserIdFromJwtClaims(); // ClaimTypes.NameIdentifier
    if (userId > 0)
    {
        SetUser(userModel: GetUser(where: Rds.UsersWhere().UserId(userId)));
    }
}

GetUser を通すため、無効化・ロックアウトされたユーザーは JWT の有効期限内でも認証されません。

パッケージ

AddJwtBearer は NuGet パッケージ Microsoft.AspNetCore.Authentication.JwtBearer に含まれ、ASP.NET Core の共有フレームワーク(Microsoft.AspNetCore.App)には入っていません。1.5.8.1 の Implem.Pleasanter.csproj には参照が無いため、追加が必要です。

方式 2: 外部 IdP が発行した JWT を検証する ​

Microsoft Entra ID・Keycloak・Auth0 などの IdP が発行したアクセストークンを、プリザンターは検証だけする方式です。機械間の連携なら Client Credentials Grant、ユーザーの操作なら Authorization Code Grant + PKCE でトークンを取得します。

図を読み込み中…

改修箇所 ​

  • Implem.ParameterAccessor/Parts/OAuthProvider.cs(新規): Enabled・Authority・Audience・ClientId・MetadataAddress・ユーザーを特定するクレーム名・LoginId に対応付けるクレーム名・未登録ユーザーを自動作成するか。Authentication.cs には複数の IdP を持てるよう配列で追加する。
  • Startup.cs: 有効な IdP ごとに AddJwtBearer(Authority と Audience を指定)を登録し、Cookie と JWT のどちらでも認可を通すポリシーにする。
  • Context.cs: クレームのログイン ID で Users を検索する分岐。未登録ユーザーの自動作成をするなら、SAML の Saml.UpdateOrInsert と同じような同期処理が要る。
パラメータの例(Microsoft Entra ID、値はダミー)
json
{
    "OAuthProviders": [
        {
            "Enabled": true,
            "Authority": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/v2.0",
            "Audience": "api://pleasanter-api",
            "ClientId": "11111111-1111-1111-1111-111111111111",
            "MetadataAddress": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/v2.0/.well-known/openid-configuration",
            "UserIdClaim": "oid",
            "LoginIdClaim": "preferred_username",
            "AutoProvision": false
        }
    ]
}

方式の比較 ​

観点方式 1(自前で発行)方式 2(外部 IdP)
トークンの発行プリザンター外部 IdP
ユーザー管理プリザンターの UsersIdP + Users への対応付け
既存の API キー併用できる併用できる
スコープ自前で実装IdP 側で設定できる
多要素認証別途実装IdP が提供
実装の規模中(発行側を作る)小〜中(検証と対応付け)
運用署名鍵・リフレッシュトークンの管理IdP の運用に依存
機械間の連携API キーを JWT に交換Client Credentials Grant

段階的な導入 ​

図を読み込み中…

段階内容
1方式 1 の基本。トークン発行・検証・Context への組み込み
2リフレッシュトークンを保存するテーブルを追加し、ローテーションと失効を実装
3方式 2 の IdP 連携とユーザーの対応付け
4クレームによる操作範囲の制限、トークン操作の SysLogs への記録

既存の仕組みへの影響は次のとおりです。

箇所影響
API キー認証なし(分岐の順番で API キーを優先したまま)
Cookie 認証・SAMLなし(既定のスキームは Cookie のまま、JWT は追加のスキーム)
CheckApiContextAttributesJWT のときの CSRF トークンの扱いを決める必要がある
各 API のアクションなし(context.Authenticated の判定は Context 側で吸収)

セキュリティ上の注意 ​

項目内容
署名アルゴリズムHS256(共有鍵)は発行側と検証側が同じ鍵を持つため、漏えい時の影響が大きい。本番は RS256 / ES256 などの非対称鍵にする
アクセストークンの有効期限短く(60 分以下)する
リフレッシュトークンDB(または Redis)に保存して失効を管理し、使ったら新しいものに交換する
対象(aud)厳密に検証し、別サービス向けのトークンを受け付けない
リプレイ必要なら jti で 1 回限りの使用に制限する
署名鍵ローテーションの手順を用意し、Azure Key Vault などの鍵管理サービスも検討する

関連ページ ​

変更履歴

第1版認証方式(2 段階認証・パスキー・LDAP・フォールバック)と認証基盤の解説、関連する改修・設計メモを追加