Skip to content

認証基盤(Cookie・API キー・Bearer) ​

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

プリザンターは ASP.NET Core の Cookie 認証だけを認証スキームとして登録し、API キーは独自にリクエストボディから読み取ります。どちらの経路でも、最終的には Context のコンストラクタが Users テーブルを検索してユーザーを確定します。

  • Cookie にはログイン ID しか入らず、認証チケット自体はサーバー側(セッションストア)に保存されます。
  • TenantId・UserId・DeptId などはリクエストごとに Users テーブルから読み直します。
  • Authorization: Bearer ヘッダーは JWT ではなく API キー(ファイルのアップロード API)か SCIM トークンの受け渡しに使われるだけです。JWT Bearer 認証は登録されていません。

ログイン時の認証方式(Provider・2 段階認証・パスキーなど)は 認証方式の内部動作 を見てください。

全体像 ​

図を読み込み中…

Startup.cs の設定 ​

Startup.ConfigureServices の要点は次のとおりです。

設定内容
グローバルフィルタAuthorizeFilter(RequireAuthenticatedUser)と CheckContextAttributes を全コントローラーに追加
認証スキームCookieAuthenticationDefaults.AuthenticationScheme("Cookies")のみ。SAML のときは AddSaml2 も追加
ログインパス/users/login
Cookie の有効期限Session.json の RetentionPeriod(分)
認証エラー時SAML 以外で Security.json の ShowLoginPageOnAuthError が false なら、ログイン画面へリダイレクトせず 404 を返す
チケットの保存ITicketStore に AuthenticationTicketStore を登録

グローバルの AuthorizeFilter があるため、[Authorize] を書かなくても全コントローラーで Cookie 認証が必要になります。認証なしで受けたいアクションには [AllowAnonymous] を付けます。本体の API コントローラー(ItemsController など)はすべて [AllowAnonymous] と [CheckApiContextAttributes] を付けたうえで、アクションの中で context.Authenticated を確かめています(ItemsController.cs)。

ログインに成功すると Context.FormsAuthenticationSignIn が呼ばれます。セッション GUID を振り直し(RotateSessionGuid)、クレーム ClaimTypes.Name(値はログイン ID)だけを持つ認証タイプ Forms の ClaimsIdentity で SignInAsync します。「ログインしたままにする」を選んだときは永続 Cookie になります。

図を読み込み中…

TenantId・UserId・DeptId は Cookie に入りません。

認証チケットのサーバー側保存 ​

AuthenticationTicketStore は ITicketStore の実装です。

  • StoreAsync: 新しい GUID を作り、シリアライズしたチケットを Base64 にしてセッションデータのキー AuthenticationTicket に保存し、GUID を返す(Cookie にはこの GUID を元にした値だけが入る)
  • RetrieveAsync / RenewAsync: GUID でセッションデータを読み書きする
  • RemoveAsync: セッションデータから削除し、Session.json の UseKeyValueStore が true(Redis)なら Redis からも消す

セッションデータの保存先は DB(Sessions テーブル)か Redis です。性能面の話は 同時アクセスが遅くなる理由(セッション・Redis) にあります。

Context によるユーザーの解決 ​

new Context() のたびに、次の 3 段階でユーザーが決まります。

  1. SetRequests: HttpContext.User.Identity から LoginId(Name)、AuthenticationType、IsAuthenticated、クレーム、IdentityType(Identity の型名)を取り出す
  2. SetUserProperties: リクエストの本文を Api クラスにデシリアライズし、ApiKey があれば ApiKey で、無ければ LoginId で Users を検索する
  3. SetUser: 見つかれば Authenticated = true にして、TenantId・DeptId・UserId・Language・UserSettings・HasPrivilege などを設定する

Users の検索は GetUser を通り、必ず Disabled = false と Lockout = false が条件に付きます。無効化・ロックアウトされたユーザーは、Cookie でも API キーでも Authenticated = false になります。

補足:

  • LoginId での検索では、特権ユーザーがユーザー切り替え中(セッションの SwitchLoginId)ならそのログイン ID を、テナント切り替え中ならその切り替え先を使います。
  • SetUser は、ユーザーのテナントが保護テナントでなく、マルチテナントのライセンスも無い場合に Authenticated を false に戻します。

IsAuthenticated と Authenticated ​

プロパティ意味
IsAuthenticatedASP.NET Core の Cookie(HttpContext.User)が認証済みか
AuthenticatedUsers テーブルで有効なユーザーが見つかったか

Cookie が有効でも、その後にユーザーが削除・無効化・ロックアウトされると Authenticated は false です。この状態の画面リクエストは、CheckContextAttributes がサインアウトさせてログイン画面へリダイレクトします(後述)。

権限情報 ​

Context の生成時には SetPermissions で PermissionHash(サイトごとの権限)と所属グループを読み込みます。HasPrivilege は Security.json の PrivilegedUsers にログイン ID が含まれるかで決まり(Permissions.PrivilegedUsers)、特権ユーザーは権限チェックをバイパスします。権限チェックの詳細は アクセス権限の実装 を見てください。

画面用のフィルタ(CheckContextAttributes) ​

CheckContextAttributes はグローバルフィルタなので、/api/ のコントローラーや拡張ライブラリのコントローラーにも掛かります。上から順に次を確認します。

#内容結果
1パラメータファイルに構文エラーがある(errors コントローラー以外)エラーページへのリダイレクトを設定(処理は続く)
2Security.json の AllowIpAddresses による IP 制限(/api/ と /scim/ は対象外)403
3認証済みでテナントの IP 制限(契約設定)を満たさないサインアウトして InvalidIpAddress へ
4認証済みでテナントの契約期限切れサインアウトしてログイン画面へ(?expired=1)
5認証済み・TokenCheck が有効・フォーム送信で Token が一致しない400
6Cookie は認証済みだが Users に有効なユーザーが無い(samllogin 以外)Windows 認証なら空の応答、それ以外はサインアウトしてログイン画面へ
7アップロードされたファイル名が不正400

API 用のフィルタ(CheckApiContextAttributes) ​

CheckApiContextAttributes はグローバルではなく、API コントローラーに属性として付けて使います。本文を読み取り(EnableBuffering で読み直せるようにする)、その本文で Context を作って次を確認します。

図を読み込み中…

InvalidJsonData は、Content-Type が JSON で本文が空でないのに Api クラスへデシリアライズできなかったときに true になります。CSRF トークンの確認は Cookie で認証済みのリクエストだけが対象で、API キーだけのリクエストでは行いません。

API キー認証 ​

送り方 ​

API キーはリクエスト本文の JSON に ApiKey として入れます。本文は Api クラス(ApiVersion・ApiKey・View・Keys・Offset・PageSize・Token など)にデシリアライズされます。

json
{
    "ApiVersion": 1.1,
    "ApiKey": "(API キー)",
    "Offset": 0
}

本文に ApiKey があればそれが最優先で、無ければ Cookie の LoginId でユーザーを決めます。ブラウザのスクリプトから API を呼ぶときに ApiKey を省略できるのはこのためです(TokenCheck が有効なら Token が必要)。

API 利用の許可 ​

API キーでのユーザー特定の時点では、API の利用可否は確かめません。各 API のバリデータ(Validators.ValidateApi など)が、次のどれかに当たれば 403 を返します。

  • Api.json の Enabled が false
  • テナントの契約設定で API が無効
  • UserSettings.AllowApi が false

AllowApi の判定は次のとおりです。

条件結果
特権ユーザー許可
User.json の DisableApi またはテナントの DisableApi が有効で、ユーザーの「API の利用を許可」(Users.AllowApi)が false拒否
UserSettings.DisableApi が true拒否
それ以外許可

UserSettings.DisableApi はクラスに定義されていますが、DB に書き込む RecordingJson には含まれていないため、保存されません(UserSettings.cs)。

ユーザーの「無効」(Disabled)は、ログインと API キー認証の両方を止めます。「画面にはログインさせず API だけ使わせる」設定は本体にはありません(改修案は API 専用ユーザー)。

API キーの発行 ​

API キーは各ユーザーが「API 設定」画面(/users/editapi)で発行・削除します。発行時の値は GUID を SHA-512 でハッシュした文字列で、Users テーブルの ApiKey 列にそのまま保存されます(UserModel.CreateApiKey)。照合は Users.ApiKey との完全一致で、有効期限はありません。

  • 発行・削除の処理(UserUtilities.CreateApiKey / DeleteApiKey)は対象を context.UserId(ログイン中の本人)に固定しています。管理者が他のユーザーの API キーを発行・削除する画面はありません。
  • 「API 設定」メニューは、Api.json の Enabled が true、テナントで API が無効でない、AllowApi が true のときに表示されます(HtmlNavigationMenu.cs)。

Bearer ヘッダー ​

Startup.cs に AddJwtBearer は無く、JWT による認証はできません。Authorization: Bearer ヘッダーを受け取るのは次の箇所です。

箇所トークンの中身検証
ファイルのアップロード API(/api/binaries/upload)プリザンターの API キー通常の API キー認証に合流
SCIM(/scim/)SCIM トークンScimTokens テーブルのハッシュと有効期限で検証
API のレート制限API キー利用者の識別にだけ使う(認証ではない)

ファイルのアップロード API ​

multipart/form-data では本文に JSON を入れられないため、BinariesController.Upload は Authorization ヘッダーから API キーを取り出し、{"ApiKey": "..."} という JSON を組み立てて Context の apiRequestBody に渡します。以降は通常の API キー認証と同じです。ヘッダーの取り出し(AuthorizationHeaderValue)は、小文字にして bearer で始まるかを見て、"bearer " の長さ分を切り取ります。使い方は ファイルのみのアップロード・大容量ファイル にあります。

SCIM ​

CheckScimContextAttributes が Authorization: Bearer のトークンを ScimTokenValidator で検証します。トークンの SHA-256 ハッシュで ScimTokens テーブルを検索し、無効化されていない・期限切れでないものが見つかれば、そのトークンのテナント ID とユーザー ID を Context に設定します。API キーとは別の仕組みです。

外部サービスへの送信 ​

LINE 通知(Line.cs)や AI 連携なども Authorization: Bearer を付けますが、これは外部サービスへ送るリクエストで、プリザンターの認証とは関係ありません。

一般的な JWT Bearer 認証との違い ​

プリザンター(API キー)一般的な JWT Bearer
ASP.NET Core の認証ハンドラーなしAddJwtBearer
トークンSHA-512 の 16 進文字列(中身に意味はない)署名付き JWT
検証Users.ApiKey との一致署名・発行者・対象・有効期限
発行本人が「API 設定」画面で発行トークンエンドポイント
有効期限なし(削除・再発行するまで有効)exp で失効
送る場所本文の ApiKey(アップロード API だけヘッダー)Authorization ヘッダー
権限の範囲ユーザーの権限すべてスコープで絞れる

JWT / OAuth 2.0 を追加する場合の改修案は API 認証への JWT・OAuth 2.0 の追加 にまとめています。

関連パラメータ ​

パラメータ内容
Session.json の RetentionPeriodCookie とセッションの有効期限(分)
Session.json の UseKeyValueStoreセッションを Redis に保存するか
Security.json の AllowIpAddresses / IpRestrictionExcludeMembersサーバー全体の IP 制限と除外メンバー
Security.json の TokenCheckCSRF トークンの確認(既定 false)
Security.json の PrivilegedUsers特権ユーザーのログイン ID
Security.json の SecureCookiesセッション Cookie などに Secure 属性を付けるか
Security.json の ShowLoginPageOnAuthError未認証時にログイン画面へリダイレクトするか(false なら 404。SAML 時は無効)
Api.json の EnabledAPI 全体の有効・無効
User.json の DisableApi既定で API を使わせないか(ユーザーの「API の利用を許可」で個別に許可)

関連ページ ​

変更履歴

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