Skip to content

認証方式の内部動作(Provider・フォールバック・2 段階認証・パスキー) ​

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

プリザンターのログイン方式は App_Data/Parameters/Authentication.json の Provider で 1 つだけ選びます。このページでは、方式ごとにログイン処理がどこを通るか、方式どうしのフォールバックの有無、2 段階認証・パスキー・TrustedProxy 認証がどの段階で効くかを、1.5.8.1 のソースに沿ってまとめます。

  • Provider は 1 つしか設定できません。ローカル DB へフォールバックするのは "LDAP+Local" だけです(SAML は AllowOriginalLogin の条件付きでローカルログインを残します)。
  • 2 段階認証はログインフォームを通ったときだけ動きます。SAML の SSO、IIS の Windows SSO、TrustedProxy 認証、パスキーでのログインでは求められません。
  • パスキーは Provider と関係なく、PasskeyParameters.Enabled が true なら使えます。

Cookie・API キー・Bearer ヘッダーなど、ログインした後にリクエストごとにユーザーを特定する仕組みは 認証基盤(Cookie・API キー・Bearer) を見てください。

Provider の一覧 ​

Providerログインフォームでの認証先フォールバック認証成功時のユーザー同期
null(既定)ローカル DBなしなし
"LDAP"LDAPなしあり
"Windows"LDAP(AD)なしあり
"LDAP+Local"LDAP → 失敗したらローカル DBローカル DBLDAP 成功時のみ
"SAML"ローカル DB(AllowOriginalLogin で制限)—(SSO は別の流れ)SSO 時にあり
"SAML-MultiTenant"同上同上同上
"Extension"拡張認証——

Authentication.json のパラメータは Authentication.cs で定義されています。Provider・DsProvider・ServiceId・ExtensionUrl・RejectUnregisteredUser・PasskeyParameters・LdapParameters・SamlParameters・TrustedProxyParameters の 9 項目です。

"Extension" は Extension.Authenticate を呼びますが、中身は NotImplementedException を投げるだけです。本体のままでは使えません。

ログインフォームの処理の流れ ​

ログインフォームの送信は UsersController.Authenticate → Authentications.SignIn → UserModel.Authenticate(returnUrl 版) の順に進みます。チェックの順番は次のとおりです。

図を読み込み中…

  • RejectUnregisteredUser のチェックは、ログイン ID が一致する有効(Disabled = false)なユーザーがちょうど 1 件ではないときに拒否します(RejectUnregisteredUser)。
  • ログイン有効期限(LoginExpired)は、ユーザーの「ログイン有効期限」を過ぎたとき、または「ログイン有効期間(日)」が設定されていて最終ログインからその日数を過ぎたときに該当します(LoginExpired)。

Provider ごとの分岐 ​

Provider ごとの認証は UserModel.Authenticate(Context) の switch です。

図を読み込み中…

どの分岐でも、最後に UpdateLockout でロックアウトカウンタを更新します。

ローカル DB 認証(GetByCredentials) ​

GetByCredentials は、ログイン ID とハッシュ化したパスワード、Disabled = false を条件に Users テーブルを検索し、1 件取れれば成功です。テナント選択(SelectedTenantId)が送られていればテナント ID も条件に加えます。

ロックアウト ​

UpdateLockout は Security.json の LockoutCount が 1 以上のときだけ動きます(既定は 0 で無効)。

  • 認証成功: ロックアウト中でなければ LockoutCounter を 0 に戻す
  • 認証失敗: LockoutCounter を 1 増やし、LockoutCount に達したら Lockout = true にする

ロックアウトの判定は Provider 別の認証の後にあるため、ロックアウト中のユーザーが正しいパスワードを入れると UserLockout、誤ったパスワードなら Deny が返ります。

LDAP 認証 ​

実装の切り替え ​

Ldap.cs が DsProvider と OS で実装を選びます。

条件実装クラスライブラリ
DsProvider が "Novell"LdapNovellDsNovell.Directory.Ldap
DsProvider 未指定で OS が WindowsLdapDsSystem.DirectoryServices
それ以外(Linux など)LdapNovellDsNovell.Directory.Ldap

認証の手順の違い ​

LdapDs(Windows)LdapNovellDs
接続入力されたログイン ID(LdapLoginPattern があれば {loginId} を置換した値)とパスワードで接続LdapSyncUser / LdapSyncPassword で接続してユーザーを検索し、見つかった DN と入力パスワードで Bind
検索フィルタLdapSearchPattern があればそれ({loginId} を置換)、なければ (LdapSearchProperty=ログインID)(LdapSearchProperty=ログインID)
成功時UpdateOrInsert でユーザー情報を同期同左

LdapNovellDs の認証では LdapSearchPattern と LdapLoginPattern は使われません(LdapNovellDs.Authenticate)。

複数の LDAP 設定を順に試す ​

LdapParameters は配列で、先頭から順に試します。ただし次の設定へ進む条件は実装で違います。

  • LdapDs: 検索時の例外が「data 52e」(資格情報が無効)なら次の設定へ進み、それ以外の例外はログを出して失敗で終わります。検索結果が無いときも次の設定へ進みます(LdapDs.Authenticate)。
  • LdapNovellDs: LdapException なら(「data 52e」以外はログを出したうえで)次の設定へ進み、それ以外の例外は失敗で終わります。

ユーザー同期 ​

認証に成功すると UpdateOrInsert が呼ばれ、LDAP の属性(LdapDeptCode・LdapDeptName・LdapUserCode・LdapFirstName + LdapLastName・LdapMailAddress・LdapExtendedAttributes など、Ldap.cs のパラメータで指定)が Users・Depts・MailAddresses に反映されます。

Windows 認証 ​

Provider = "Windows" には 2 つの経路があります。

  • IIS の Windows 認証(SSO): IIS で Windows 認証が有効だと、ブラウザと IIS の間で Negotiate / NTLM の認証が行われ、プリザンターには Windows のユーザー名が渡ります。セッション開始時のミドルウェアが WindowsAuthenticated で検出し、Ldap.UpdateOrInsert でユーザーを同期してセッションを作ります(Startup.cs)。RejectUnregisteredUser が true のときは、既に登録済みのユーザーだけが対象です。
  • ログインフォーム: case "Windows": は case "LDAP": と同じ分岐で、Ldap.Authenticate だけを呼びます。

Windows 認証かどうかは Context.AuthenticationsWindows で判定します。Provider が "Windows" のとき、または HttpContext.User.Identity の型名に Windows を含むときに true です。

ローカル DB へはフォールバックしない ​

IIS で匿名認証も有効にすると、Windows 認証に失敗したユーザーもログインフォームに到達できます。しかしフォームから送った資格情報は Ldap.Authenticate(AD)でだけ認証され、ローカル DB では認証されません。AD に居ないローカル専用のユーザー(管理者など)は、Provider = "Windows" のままではログインする手段がありません。SAML の AllowOriginalLogin にあたる仕組みも Windows 認証にはありません。

IIS の設定Windows 認証が通るときWindows 認証が通らないとき
Windows 認証のみSSO でログイン401 でアクセス不可
Windows 認証 + 匿名認証SSO でログインログインフォーム → AD でのみ認証
"Windows""LDAP+Local"
ログイン画面出ない(IIS が透過的に認証)常にフォームで入力
AD の認証IIS の Negotiate / NTLMLdap.Authenticate(Bind)
ローカル DB へのフォールバックなしあり
IIS の Windows 認証モジュール必要不要

SSO とローカル DB へのフォールバックを両立する Provider はありません。SSO が必要なら "Windows"、ローカル DB へのフォールバックが必要なら "LDAP+Local"(LDAP の接続先を AD にする)を選びます。両方を実現する改修案は Windows 認証とローカル認証の併用(Windows+Local) にまとめています。

SAML 認証 ​

SAML の設定手順と属性マッピングは 認証(Google Workspace の SAML SSO / SMTP の OAuth) と Microsoft Entra ID で SAML SSO にあります。ここでは内部の分岐だけを補足します。

  • SSO の応答は Saml.SamlLogin で処理され、ユーザー同期のあと AllowAfterUrl で直接 Cookie を発行します。ログインフォームの流れ(ログイン有効期限・2 段階認証・パスワード有効期限のチェック)は通りません。無効ユーザー・ロックアウト中のユーザーは拒否されます。
  • テナントの決め方は Provider で違います。
"SAML""SAML-MultiTenant"
テナントSamlParameters.SamlTenantId の固定値IdP の Issuer の最後のパス要素(SSO コード)と Tenants.Comments が一致するテナント
テナントが無いときSamlTenantId で DefaultTenant を作成作成しない
ユーザー名(Name 属性)が空ログイン ID を名前にするエラー(EmptyUserName)
  • ログインフォームからのローカルログインは GetByCredentials で認証し、テナントの契約設定 AllowOriginalLogin が 0 で、かつテナント管理者でなければ失敗にします。

2 段階認証 ​

2 段階認証(Secondary Authentication)は Security.json の SecondaryAuthentication で設定します(定義は SecondaryAuthentication.cs)。

項目既定値内容
Mode"None"None(無効)/ DefaultEnable(既定で有効、ユーザーごとに無効化可)/ DefaultDisable(既定で無効、ユーザーごとに有効化可)
NotificationType"Mail"Mail(メールで認証コード)/ Totp(認証アプリ)
CountTolerances1TOTP で何ステップ前(30 秒単位)まで遡って照合するか
NotificationMailBccfalse認証コードのメールを BCC でも送るか
AuthenticationCodeCharacterType"Number"メールのコードの文字種。Number / Letter / それ以外は数字と英字
AuthenticationCodeLength8メールのコードの桁数
AuthenticationCodeExpirationPeriod300メールのコードの有効期間(秒)

有効かどうかの判定 ​

EnabledSecondaryAuthentication は次の順に判定します。

  1. Mode が None なら無効。DefaultEnable でユーザーの「2 段階認証を無効化」(DisableSecondaryAuthentication)が立っていれば無効。DefaultDisable でユーザーの「2 段階認証を有効化」(EnableSecondaryAuthentication)が立っていなければ無効。
  2. 拡張 SQL で OnUseSecondaryAuthentication が true のものが無ければ有効。
  3. 該当する拡張 SQL があれば、@TenantId と @UserId を渡して実行します。どれか 1 つでも結果が 0 行、または 1 列目が false でない行を含めば有効です。すべての SQL が行を返し、その 1 列目がすべて false のときだけ無効になります。

拡張 SQL の書き方は 拡張 SQL の活用 を見てください。

認証コードの入力の流れ ​

図を読み込み中…

  • NotificationType が Mail のときは常にメール方式になります。Totp のときも、コード入力画面の「メールで認証コードを受け取る」リンクからメール方式に切り替えられます(UsersController.Authenticate の isAuthenticationByMail)。
  • TOTP の初回は、コードの検証に成功した時点で EnableSecretKey が立ち、以後は登録画面ではなく入力画面になります(HandlePostSecondaryAuthentication)。

メール方式 ​

UpdateSecondaryAuthenticationCode が指定の文字種・桁数でコードを作り、Users.SecondaryAuthenticationCode に、有効期限を SecondaryAuthenticationCodeExpirationTime に保存します。コードはユーザーのメールアドレス(MailAddresses テーブル)すべてに送られます。検証は、保存したコードとの一致と有効期限内かどうかです(SecondaryAuthentication)。

WARNING

メールの認証コードは Users テーブルに平文で保存されます。TOTP のシークレットキー(Users.SecretKey)も Base32 の平文です。DB を参照できる人はこれらの値を読めます。

TOTP 方式 ​

  • シークレットキーは 20 バイトの乱数を Base32 にしたもので、Users.SecretKey に保存します(UpdateSecretKey)。登録画面の QR コードは otpauth://totp/{ログインID}?secret=...&issuer=Implem%20Pleasanter です。
  • 検証は Otp.NET の Totp(6 桁、30 秒ステップ)で行います。現在時刻で合わなければ 30 秒ずつ過去にずらして再検証し、CountTolerances 回に達したら失敗です(VerifyTotp)。各回の照合には Otp.NET の RfcSpecifiedNetworkDelay の許容幅も加わります。

2 段階認証を通らないログイン ​

2 段階認証の判定は UserModel.Authenticate(ログインフォームの流れ)の中にあります。次のログインはこの流れを通らないため、2 段階認証を有効にしていても求められません。

ログインの経路理由
SAML の SSOSamlLogin が AllowAfterUrl で直接 Cookie を発行する
IIS の Windows 認証(SSO)セッション開始時のミドルウェアでユーザーを確定する
TrustedProxy 認証ミドルウェアで直接 Cookie を発行する
パスキーisAuthenticationByPasskey = true で 2 段階認証の判定を飛ばす

パスキー(WebAuthn) ​

FIDO2 / WebAuthn によるパスワードレスのログインです。サーバー側は Fido2 / Fido2.AspNet 4.0.1、ブラウザ側は navigator.credentials を使います。

json
{
    "PasskeyParameters": {
        "Enabled": false,
        "ServerName": "Pleasanter",
        "ServerDomain": "localhost",
        "Origins": [ "https://localhost:44331" ],
        "UserVerificationRequirement": "Preferred"
    }
}
項目内容
Enabledfalse のときパスキーの API はすべて Not Found を返す
ServerDomainRelying Party ID
ServerNameRelying Party の表示名
Origins許可するオリジン
UserVerificationRequirementRequired / Preferred / Discouraged。それ以外や未指定は Preferred。登録時と認証時の両方に使う(PasskeyUserVerificationRequirement)

登録と認証の設定 ​

PasskeysController が Fido2 に渡す値は次のとおりです。

設定値意味
ResidentKeyRequiredDiscoverable Credential が必須。ログイン時にユーザー名を入れずに認証器からユーザーを特定する
AttestationPreferenceNone認証器の証明を求めない(機種を制限しない)
AuthenticatorAttachment指定なしプラットフォーム認証器(Windows Hello、Touch ID など)と外部の認証器(USB・NFC・BLE のセキュリティキー)のどちらも使える
ExcludeCredentials登録済みのパスキー同じ認証器の二重登録を防ぐ
認証時の AllowedCredentials空Discoverable Credential でユーザーを特定する

ブラウザ側(passkey.ts)でも、authenticatorAttachment が null なら undefined に置き換え、認証器の種類を制限しません。

登録の流れ ​

ログイン済みのユーザーが自分のパスキーを登録します。

図を読み込み中…

保存先は Passkeys テーブル(PasskeyId・CredentialId・UserId・Title・PasskeyData)で、PasskeyData には公開鍵、署名カウンタ、Transports、バックアップ可否・状態、Attestation の情報、AAGUID が JSON で入ります(PasskeyData.cs)。

ログインの流れ ​

図を読み込み中…

getassertionoptions と makeassertion は [AllowAnonymous] で、レート制限 AnonymousIp の対象です(PasskeysController.cs)。

ほかの認証方式との関係 ​

isAuthenticationByPasskey = true の UserModel.Authenticate は、Provider 別の認証(パスワードの照合)、2 段階認証、パスワード有効期限のチェックを飛ばします。無効ユーザー(RevealUserDisabled 時)、RejectUnregisteredUser、ログイン有効期限、テナントの IP 制限、ロックアウトのチェックは通常どおり行います。このため、どの Provider でも、パスキーを有効にすればパスキーでログインできます。

ブラウザでのエラー表示は次のとおりです。

例外名表示するメッセージ
NotAllowedErrorPasskeyOperationTimeoutOrAbort
AbortErrorPasskeyOperationAborted
SyntaxError・TypeError・DataError・InvalidStateErrorPasskeyResponseInvalid
その他の ErrorPasskeyOperationTimeoutOrAbort
Error 以外PasskeyServerUnavailable

TrustedProxy 認証 ​

リバースプロキシが付けたヘッダーのログイン ID で、プリザンターにログインさせる仕組みです(TrustedProxyAuthenticationMiddleware)。

json
{
    "TrustedProxyParameters": {
        "Enabled": true,
        "Header": "X-Forwarded-User"
    }
}

(X-Forwarded-User は例です。プロキシが付けるヘッダー名にします。)

  1. Enabled が true のときだけミドルウェアが組み込まれます(UseAuthentication の直後)。
  2. Header が空、既に Cookie で認証済み、送信元がプロキシとして信頼できない、ヘッダーが空、のいずれかなら何もしません。
  3. ヘッダーの値をログイン ID として、Disabled = false・Lockout = false のユーザーを検索します。
  4. 見つかれば認証タイプ TrustedProxy の ClaimsIdentity で Cookie を発行します(永続 Cookie)。

送信元の判定(IsTrustedProxy)には Security.json の ForwardedHeaders.KnownProxies(IP アドレス)と KnownNetworks(CIDR)を使います。比べる IP は、UseForwardedHeaders より前に記録した接続元(Connection.RemoteIpAddress)です。

WARNING

KnownProxies と KnownNetworks がどちらも空だと、どの送信元も信頼せず TrustedProxy 認証は動きません。起動時に SysLogs へ「[TrustedProxy] KnownNetworks/KnownProxies are empty.」の警告が出ます(Startup.cs)。プロキシを経由せず直接プリザンターに届く経路があると、ヘッダーを偽装されるおそれがあるため、信頼するプロキシは必ず絞ってください。

関連ページ ​

変更履歴

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