ログインユーザーモジュール (AppSettings.CurrentUserModuleDesignName のモジュール) に 1 つ置き、
「このモジュールの行がログインアカウントとしてどう振る舞うか」を宣言する契約フィールド。宣言は 3 種類:
- 役割 (自モジュールのフィールド参照): ログイン ID / 外部 IdP の突き合わせ / 有効フラグ / 表示名 / メール二要素の送信先
- パスワードの入力フィールド (
PasswordField): 指定すると、保存時にサーバーがその平文からハッシュ / ソルトを作って下の列に書く。PasswordHashField は要らない - ログイン用の DB 列: パスワードのハッシュ / ソルト (書き込み専用列)、セッションの印 (書き込み専用列。設定すると発行済みの Cookie をサーバー側で失効できる)、認証アプリ (TOTP) の 3 列 (列名だけの宣言。サーバーのログイン処理が直接読み書きする)
UI は持たない。データはハッシュ / ソルト / 印の列分だけで、保存時にサーバーが差し込む (クライアントは送受信しない)。
テンプレート (Starter の Cookie ホスト) のログインはこの契約でユーザー行を扱う:
- ID/パスワードのログイン:
LoginNameの列で行を探し、契約のハッシュ / ソルト列で照合する - 外部 IdP (Entra ID / Google / AWS Cognito / OIDC): IdP が確認した本人 (Entra = UPN、Google = メール) を
ExternalLoginNameの列 (空ならLoginNameの列) と突き合わせて行に解決する IsActiveが偽の行はどちらの経路でもログインできない (退職・停止)DisplayNameは画面のユーザー表示 (Cookie の Name) と二要素認証の QR のアカウント名TwoFactorEmailを設定すると、メールのワンタイムコードによる二要素認証になる (TOTP の列も設定されていれば認証アプリが優先)DbColumnSessionStampを設定すると、ログインで Cookie にその行の印を積み、リクエストごとに照合する。パスワードの変更・IsActiveを偽にする保存で印が書き換わり、それまでの Cookie は全部その場で使えなくなる
契約が無いとサーバーはユーザー行を解決できず、ログインできない (LoginAccountStore.Create が null)。
パスワードを「書く」のも契約 (PasswordField を指定したとき。ユーザー登録・編集画面の PasswordField から)。契約の無いモジュール (別モジュールのパスワード変更ダイアログなど) では PasswordHashField を使う。
LoginNameは必須 (空だとデザインチェックがエラーにする)。他の役割は空が初期値 (= 使わない)- 必須の役割が空・役割が指す先が無ければデザインチェックがエラーにする。リネームに自動追従する
- 指す先は DB 列を持つ値フィールド (TextField / BooleanField 等) であること
| 役割 (表示名) | 内容 | 必須 |
|---|---|---|
| LoginName (ログイン ID (必須)) | ログイン画面で入力する ID を持つフィールド (ユーザー名・メール等) | ○ |
| ExternalLoginName (外部 IdP の突き合わせ) | 外部 IdP の本人 (UPN・メール) と比較するフィールド。空なら LoginName と同じ列。社内のログイン ID とメールが別のときにメールのフィールドを指定する | - |
| IsActive (有効フラグ) | 偽ならログイン拒否 (Boolean / 0・1 の数値 / "true"・"1" の文字列)。NULL は無効扱い。空なら判定しない | - |
| DisplayName (表示名) | 画面のユーザー表示と TOTP の QR に使う名前。空なら LoginName | - |
| TwoFactorEmail (二要素認証のメール送信先) | 設定するとパスワード成功後にワンタイムコードをこのフィールドのメールアドレスへ送り、入力を求める (メールの体裁・有効期限はサーバー設定 EmailOtpLogin)。TOTP の列も設定されていれば認証アプリが優先。空なら無効 |
- |
| PasswordField (パスワード入力フィールド) | 同じモジュールの PasswordField (平文の DB 列は持たない)。値があれば保存時にサーバーがハッシュ / ソルトを作って DbColumnPasswordHash / DbColumnPasswordSalt に書く (テンプレートの CustomizedModuleDataIO が継承する PasswordHashingModuleDataIO が行を書くときに PasswordHashHelper.ApplyPasswordHash を呼ぶ)。空なら書かない (外部 IdP 専用、または PasswordHashField で書く)。指定するときは列 2 つが必須。同じモジュールに PasswordHashField があるとエラー (同じ列を二重に書く) |
- |
| プロパティ (表示名) | 内容 |
|---|---|
| DbColumnPasswordHash / DbColumnPasswordSalt (パスワードのハッシュ列 / ソルト列) | ID/パスワードのログインで照合する列 (書き込み専用)。PasswordField を指定していれば保存時にここへ書く。両方設定するか両方空 (空 = パスワードログイン無し = 外部 IdP 専用)。デザインチェックで片方だけはエラー |
| DbColumnSessionStamp (セッションの印の列 (発行済み Cookie の失効)) | ログイン済みセッションの失効に使う列 (書き込み専用。TEXT。DDL 生成の対象)。値はサーバーだけが作る (ログイン時に NULL なら採番。パスワードの変更・IsActive を偽にする保存で新しい値に書き換え。管理者が特定のユーザーを全端末からログアウトさせるボタンは SessionRevokeButtonField)。空なら照合しない (Cookie は有効期限まで使える)。列を足した時点で、それまでの Cookie (印を持たない) は無効になり全員が再ログインする。アプリの保存を通さず SQL でパスワードや有効フラグを変えるときは、この列も新しい乱数に書き換える |
| DbColumnTotpSecret / DbColumnTotpConfirmed / DbColumnTotpLastTimestep (認証アプリ: 秘密鍵列 / 確認済み列 / 最終タイムステップ列) | 認証アプリ (TOTP) の二要素認証。3 列そろえて設定すると有効 (初回ログインで QR を登録)。空 = 無効。1〜2 列だけはエラー。列名だけの宣言で、通常の読み書きには乗らない (DDL 生成の対象外なので下の DDL で作る) |
列は実テーブルに存在するかデザインチェックで検証される。
hash TEXT NULL, -- パスワードのハッシュ (base64)
salt TEXT NULL, -- ソルト (base64)
session_stamp TEXT NULL, -- セッションの印 (発行済み Cookie を失効させるときだけ)
totp_secret TEXT NULL, -- 認証アプリを使うときだけ
totp_confirmed INTEGER NULL,
totp_last_timestep INTEGER NULL{
"LoginName": "UserName",
"ExternalLoginName": "Email",
"IsActive": "IsActive",
"DisplayName": "Name",
"TwoFactorEmail": "Email",
"PasswordField": "Password",
"DbColumnPasswordHash": "hash",
"DbColumnPasswordSalt": "salt",
"DbColumnSessionStamp": "session_stamp",
"DbColumnTotpSecret": "totp_secret",
"DbColumnTotpConfirmed": "totp_confirmed",
"DbColumnTotpLastTimestep": "totp_last_timestep",
"Name": "LoginAccount",
"TypeFullName": "Codeer.LowCode.Blazor.Extras.Designs.LoginAccountContractFieldDesign"
}同じモジュールに IdField (ユーザー ID)・役割のフィールド・PasswordField (平文入力欄。DB 列なし) を置く。パスワードのハッシュ化は契約が行うので PasswordHashField は置かない。別モジュールのパスワード変更ダイアログ (同じテーブルを参照) には PasswordField + PasswordHashField。
Codeer.LowCode.Blazor.Extras.Server.Auth.PasswordLogin (ID/パスワードのログインの検証の流れ) と LoginAccountStore (契約から表・列を引く):
var login = new PasswordLogin(designData, dataService.DbAccess, cache, config.LoginLockout, config.TotpLogin, config.EmailOtpLogin, sendMail);
var result = await login.VerifyAsync(loginInfo.Id, loginInfo.Password, loginInfo.TwoFactorCode); // Ok なら result.Account でサインイン
var accounts = LoginAccountStore.Create(designData, dataService.DbAccess); // 契約が無ければ null
var account2 = accounts == null ? null : await accounts.FindByExternalLoginNameAsync(identity.LoginName); // 外部 IdP の解決
// サインインは LoginPrincipal.Create(account.UserId, account.DisplayName, sessionStamp: account.SessionStamp)。account.LoginName → TOTP の QR
// Cookie 認証の OnValidatePrincipal で LoginSessionValidation.ValidatePrincipalAsync(context, accounts) (印の照合)、全端末ログアウトは accounts.RevokeSessionsAsync(userId)ランタイム Field は値・データ系メソッドを公開しない。固有のスクリプト API は無い。