対象: root/programs(net48 版 / net10.0 版)
配置: root
本書は**「設定がどこにあり、どう読まれ、どう上書きされ、どこで踏むか」**を扱う。 個々のキーが何を意味するかは書かない。 それは値の隣(雛形のコメント)にある。
一次情報は本書ではない。 迷ったら次を見ること。
内容 一次情報 各キーの意味 _appsettings.json/_app.configのコメント秘密の報告方法 ../SECURITY.mdビルド BUILDING.mdテストからの参照 TESTING.md
| ターゲット | 実体 | 雛形 | 行数 |
|---|---|---|---|
| net10.0 | programs/MultiPurposeAuthSiteCore/MultiPurposeAuthSiteCore/appsettings.json |
_appsettings.json |
約 364 |
| net48 | programs/MultiPurposeAuthSite/MultiPurposeAuthSite/app.config |
_app.config |
約 390 |
実体は 2 つとも .gitignore 済み。 実際の資格情報を含むため。
/root/programs/MultiPurposeAuthSite/MultiPurposeAuthSite/app.config
/root/programs/MultiPurposeAuthSiteCore/MultiPurposeAuthSiteCore/appsettings.json
/root/programs/Tests/E2ETests/testsettings.json
設定を変えたら、雛形(_ 付き)にも反映する。 clone した人が見るのは雛形だけである。
3 つのセクションを持つ。
{
"connectionStrings": { ... },
"sessionState": { ... },
"appSettings": { ... } ← ほとんどはここ
}コメント(//)と末尾カンマを含む JSONC である。 素の JsonSerializer では読めない。
機械で読むときは次を指定する。
new JsonDocumentOptions()
{
CommentHandling = JsonCommentHandling.Skip,
AllowTrailingCommas = true
}階層の区切りは __(アンダースコア 2 つ)。
set appSettings__OAuth2AuthorizationServerEndpointsRootURI=https://localhost:44300
net48 版にはこの仕組みが無い。 これは ASP.NET Core の構成の仕組みである。
ただし、次の FxContainerization は両方で使える。
設定ファイルに無いキーは、環境変数だけでは効かない(実測。2026-09-17)。 上書きであって、追加ではない。新しいキーを試すときは、先にファイルへ足すこと。
ただし、「節」として読む設定は例外(実測。2026-09-22、#224)。 クライアント一覧(
OAuth2ClientsInformation)は節ごと読む(GetAnyConfigSection)ので、appSettings__OAuth2ClientsInformation__<client_id>__<項目>でファイルに無いクライアントを足せる。 1 個の値として読むキー(GetConfigValue)は、上のとおり足せない。 net48 はクライアント一覧を 1 個の値(JSON 文字列)として読むので、FxContainerization=ONのうえでOAuth2ClientsInformationを一覧ごと差し替える必要がある。 E2E はこれを使って、テスト専用のクライアントを差し込んでいる(Tests/README.md)。
appSettings の FxContainerization を ON にすると、
Open棟梁 の GetConfigParameter が設定ファイルより環境変数を優先する。
<add key="FxContainerization" value="ON" /> app.config
"FxContainerization": "ON", appsettings.json
キー名がそのまま環境変数名になる。 接頭辞は付かない。
set OAuth2AuthorizationServerEndpointsRootURI=https://localhost:44302
set OAuth2ClientEndpointsRootURI=https://localhost:44302
root/programs/Tests/test.ps1 -Launch は、これを使って
2 つのサイトを別々の URL で同時に立てている(TESTING.md 4 節)。
net48 版を app.config の URL に置く必要がないのは、この仕組みによる。
ONにしただけでは、動きは変わらない。 環境変数が定義されていなければ、設定ファイルの値が使われる。
本番では空のままにする。 設定すると、サーバはプッシュ通知(CIBA、2FA のモバイル アプリ)を FCM に送らず、
このディレクトリに JSON ファイルとして書く。E2E テストが、認証デバイスの代わりにそれを読む(#196)。
送信箱を使うときは、Firebase の資格情報(FirebaseServiceAccountKey)を読まない。
root/programs/Tests/test.ps1 -Launch が、上の FxContainerization の仕組みで、環境変数としてサイトごとに設定する
(Tests/E2ETests/Result/fcm/core・…/netfx)。構成ファイルに書く必要は無い。
ファイルには、device_token や 2FA のコードがそのまま書かれる。
Web.config から取り込まれる外部 appSettings ファイルである。
<!-- Web.config -->
<appSettings file="app.config" />このため app.config のルート要素は <configuration> ではなく <appSettings>。
<configuration><appSettings> を期待して読むと、何も取れない。
<appSettings>
<add key="OAuth2AuthorizationServerEndpointsRootURI" value="https://localhost:44300/MultiPurposeAuthSite" />
...
</appSettings>同じ内容が、2 つの形で入っている。
| ターゲット | 形 |
|---|---|
| net10.0 | 入れ子の JSON オブジェクト |
| net48 | JSON 文字列(value='...' の中に丸ごと) |
"OAuth2ClientsInformation": {
"67d328bfe8604aae83fb15fa44780d8b": {
"client_secret": "...",
"redirect_uri_code": "test_self_code",
"client_name": "TestClient",
// "subject_types" は書かなければ public(既定。下記)
"jwk_rsa_publickey": "..."
},
...
}client_id は環境ごとに違う。 CommandLineTools の CreateClientsIdentity.exe で生成する。
このため、コードやテストに client_id を直書きしない。 client_name から引くこと。
| 値 | sub |
|
|---|---|---|
public |
利用者の内部 ID | 既定(OIDC Core 8 章) |
pairwise |
クライアントごとに違う PPID | OP だけが戻せる(#140 の段階 2) |
扱うのはこの 2 つだけである(#151 の段階 5)。
書かなければ public で、画面(Manage/AddSaml2OAuth2Data)の選択肢も、この 2 つになる。
**
subは「その RP の中で利用者を指す識別子」**で、表示や照合のための属性ではない。 利用者名を渡したいならUserClaimsMappingでpreferred_usernameに対応付ける (#151 の段階 1。下の設定表)。
かつては uname(sub に利用者名を入れる独自値)が在り、それが既定だった。
| 何が問題だったか | 以前は「利用者名=メアド」だったので、sub としてメアドが全ての RP に渡っていた |
| 代わり | preferred_username(#151 の段階 1) |
| 廃止の順序 | 段階 4 で既定を public に、段階 5 で値そのものを廃止 |
設定に "subject_types": "uname" が残っていても、エラーにはならない。
pairwise 以外は public として扱うので、public と同じ振る舞いになる。
subject_types_supported にも出さない。
RP は sub を利用者の主キーとして保存する。 値が変わると、RP 側では全員が別人になる。
そうならないのは、発行した sub を表に記録しているためである(#151 の段階 2。SubjectIdProvider)。
既に sub を発行した(クライアント × 利用者) |
表の値を返し続ける(= 以前と同じ値。昔の利用者名のままのこともある) |
| まだ発行していない組み合わせ | いまの設定(既定は public)で作る |
つまり、既定値の変更が効くのは「これから」だけである。
既存の配備で sub を public に揃えたいなら、表の行を消すことになる
(消すと、その RP から見て別人になる)。
利用者名とメアドは、別の項目である。 サインインはどちらでも通る。
| サインインの入力 | 1 つの欄(「利用者名またはメアド」)。@ を含めばメアドとして引く |
| 利用者名 | @ を使えない(含めると、入力がどちらなのか決まらなくなる) |
| メアド | 常に在って一意(FindByEmailAsync が成り立つ必要がある) |
RequireUniqueEmailの設定は削除した。 以前は「利用者名=メアド」か「メアドを持たない」の二択だった。 両方でサインインできるようにしたので、二択が成り立たない (メアドを外すと、サインインもパスワード再設定もできなくなる)。既存の配備は、そのまま動く。 既存の利用者名は書き換えていないので、 メアド形式の利用者名が残る。 その利用者はメアドとして引かれるが、 値が同じなので同じ利用者に当たる。
@の禁止は、新しく作る・変えるときだけ掛かる。
画面を 2 つ削除した(#151 の段階 3 で引退させ、段階 5 で消した)。
| 消した画面 | なぜ在ったか |
|---|---|
Manage/AddEmail |
メアドを持たない利用者に、後から足すためのもの |
Manage/RemoveEmail |
同様に、外すためのもの |
メアドは常に在って一意(サインインの識別子)になったので、どちらも成り立たない (外すと、サインインもパスワード再設定もできなくなる)。 アクションもビューも無いので、叩くと 404 になる。
メアドの変更は Manage/ChangeEmail(門番は CanEditEmail)、
利用者名の変更は Manage/ChangeUserName(門番は AllowEditingUserName)。
上流が返す sub は、利用者名ではない(既定が public = 利用者 ID)。
そこで、下流が新規に作るときの名前は、この順で決める。
| 順 | 使う値 | いつ |
|---|---|---|
| 1 | preferred_username |
上流が返していて、利用者名として使えるとき |
| 2 | メアドの @ より前 |
返っていないとき |
subは見ない。 段階 4 より前の上流(subject_types=uname)ではsubが利用者名だったが、 下位互換は維持しないと決めてある。結び付ける鍵はメアドなので、名前がどちらになっても、同じ利用者に結び付く。 ここで決まるのは新規に作るときの名前だけである。
上流(相手の OP)に
preferred_usernameを出してもらうには、上流側の設定が要る (UserClaimsMapping)。汎用認証サイト同士なら、上流にこれを入れる。 ID 連携の要求スコープにはprofileが入っているので、対応付けがあれば返る。
client_name |
用途 |
|---|---|
TestClient |
自己テスト用(redirect_uri は test_self_code / test_self_token) |
TestClient1 |
FAPI1 |
TestClient2 |
FAPI2(Request Object を使う) |
TestClient3 |
Device Authorization Grant。client_secret を持たない(パブリック クライアント) |
TestClient4 |
CIBA |
TestClient5 |
登録の scope で、要求してよいスコープを制限した例(#198、E2E テスト用) |
MVC_Sample ほか |
絶対 URL の redirect_uri を持つサンプル |
クライアントごとに、発行してよいスコープを制限する。 RFC 7591 §2 の client metadata と同じく、 スペース区切りで並べる。
"scope": "openid profile email"発行するスコープは、次の 3 つをすべて満たすものになる。
- クライアントが要求した
- Discovery の
scopes_supportedにある(認可サーバが扱う) - 登録の
scopeにある
| 登録 | 扱い |
|---|---|
| 項目が無い | 3 は見ない(scopes_supported の範囲だけ)。既存の登録はこのまま動く |
| 空文字列 | どのスコープも許さない |
要求を拒否(invalid_scope)するのではなく、許されないスコープを外して発行する。
外したときは、トークン応答の scope に実際に発行したものを返す(RFC 6749 §5.1)。
CreateClientsIdentityはこの項目を出力しない。必要なクライアントにだけ、手で足す。
RP からのログアウト(/end_session)の後に、RP へ戻してよい URL。
OpenID Connect RP-Initiated Logout 1.0 §3.1 の post_logout_redirect_uris に当たる
(仕様は配列だが、既存の redirect_uri_* と同じく1 本で持つ)。
"post_logout_redirect_uri": "https://rp.example.com/logged_out"| 登録 | 扱い |
|---|---|
| 項目が無い | ログアウトはできるが、RP へは戻さない(自サイトの画面に戻る) |
| 有る | 完全一致したときだけ戻す(大文字小文字も区別する。§3 は exactly match) |
口(エンドポイント)の URL は、設定キー OAuth2EndSessionEndpoint(既定 /end_session)。
既に配備された設定ファイルに無くても動く(無ければ既定値を使う)。Discovery の
end_session_endpoint にも、この値が出る。
id_token_hint が無い要求では、登録が有っても戻さない(§3 の MUST)。
戻り先の正しさを確かめる手段が無いため。
CreateClientsIdentityはこの項目を出力しない。必要なクライアントにだけ、手で足す。 画面(/Manage/AddSaml2OAuth2Data)から登録したクライアントでも設定できる。
test_self_code / test_self_token は URL ではなく記号である。
サーバが CmnEndpoints.GetRedirectUriFromConstr で実 URL に解決する。
test_self_code → OAuth2ClientEndpointsRootURI + OAuth2AuthorizationCodeGrantClient_Account
test_self_token → OAuth2ClientEndpointsRootURI + OAuth2ImplicitGrantClient_Account
test_self_logout → OAuth2ClientEndpointsRootURI + /Home/Index(post_logout_redirect_uri 用。#232)
OAuth2AuthorizationServerEndpointsRootURI ではなく OAuth2ClientEndpointsRootURI を使う。
既定では同じ値だが、変えるときは両方見ること。
OAuth2AuthorizationServerEndpointsRootURI 認可サーバ側のエンドポイントの根
OAuth2ClientEndpointsRootURI クライアント側(自己テストの受け口)の根
既定はどちらも https://localhost:44300/MultiPurposeAuthSite。
これは IIS Express の仮想ディレクトリを前提とした値である。
アプリ同梱の自己テスト(FAPI2 / CIBA / Device AuthZ)は、 サーバ自身がこの URL へ HTTP で折り返す。
POST /Home/Saml2OAuth2Starters
→ サーバが OAuth2AuthorizationServerEndpointsRootURI + /ros へ POST(Request Object の登録)
→ 返ってきた request_uri で /authorize へリダイレクト
待ち受け URL と食い違うと、この折り返しが接続不能になり HTTP 500 になる。
launchSettings.json の applicationUrl はパスを含むが、
Kestrel は仮想ディレクトリを持たない(UsePathBase も呼んでいない)ので、
/MultiPurposeAuthSite/... は 404 になる。
環境変数で構成側を合わせる。
set ASPNETCORE_ENVIRONMENT=Development
set appSettings__OAuth2AuthorizationServerEndpointsRootURI=https://localhost:44300
set appSettings__OAuth2ClientEndpointsRootURI=https://localhost:44300
dotnet run --urls https://localhost:44300
FxContainerization が ON なら、接頭辞なしの OAuth2... でも上書きできる(2 節)。
net48 版と書き方が揃うので、両方を扱うスクリプトはそちらを使っている。
認証まわりの Cookie は SameSite=None で発行される。
Secure が伴わないため、http では保持されない。
max_age を使うフロー(FAPI2)は auth_time Cookie を見るので、
http で動かすと認可エンドポイントがエラー画面になる。
app.config / appsettings.json の内容を、報告・コミット メッセージ・Issue 本文に転記しない。
含まれるもの。
TestUserPWDOAuth2ClientsInformationの各client_secretconnectionStringsのパスワードRsaPfxPassword/EcdsaPfxPassword/SpRp_*PfxPassword
設定の変更を共有するときは、雛形(_app.config / _appsettings.json)側に書く。
雛形の値はプレースホルダ([password of TestUser] など)である。
E2E テストは、実行時にアプリ自身の構成ファイルから読み出す。
テスト コードにもテスト設定にも、秘密は書かない(TESTING.md 9 節)。
"UserStoreType": "mem", // mem / sql / ora / npg| 値 | 意味 |
|---|---|
mem |
メモリ。再起動で消える。 テスト ユーザは初回アクセスで作られる |
sql |
SQL Server |
ora |
Oracle |
npg |
PostgreSQL |
E2E テストは既定で mem を使う。前後で状態を掃除する必要が無いのが理由。
sql / ora / npg に切り替えても回せる(TESTING.md 1 節「ストアを切り替える」、#207)。
sql/npgを使っている既存環境は、CibaDataにUserId列の追加が要る。 CIBA の返答(/ciba_result)は、要求が誰宛てだったかを照合してから結果を書き込む。 その宛先を持つ列で、Create_UserStore.sqlには入れてあるが、 作成済みのデータベースには自動では増えない。ALTER TABLE [CibaData] ADD [UserId] [nvarchar](128) NULL; -- SQL Server ALTER TABLE CibaData ADD UserId varchar(128) NULL; -- PostgreSQL列が無いと、CIBA の要求の登録(
INSERT)が失敗する。 なお列を足す前の保留中の要求は、承認できない(宛先が記録されていないため)。oraにもDeviceAuthZData/CibaDataを追加した(#206)。oraの既存環境は、列ではなく表ごと足す(元々この 2 表が無かったため)。 実機(gvenzl/oracle-free:23-slim)で E2E を通してある(#208)。NVARCHAR2(800)の列への一意制約も、db_block_size8192・max_string_sizeSTANDARD の 既定のままで作成された。
3 つの DDL がミラーかどうかは、機械的に確かめられる。
cd root
.\CompareDdl.ps1 # 差があれば赤く出て、終了コードが 1 になる
.\CompareDdl.ps1 -Detail # 差の無いテーブルも並べるCreate_UserStore.sql(テーブルと列)と Select_UserStore.sql(SELECT 対象)の両方を見る。
型は比べない(nvarchar(max) と NVARCHAR2(2000) のように、対応はするが同一ではない)。
SQL が実行できるかは分からない。 それは、ストアを切り替えて E2E を回して確かめる
(TESTING.md 1 節「ストアを切り替える」、#207)。
"RsaPfxFilePath": "C:/root/files/resource/X509/SHA256RSA_Server.pfx",
"EcdsaPfxFilePath": "C:/root/files/resource/X509/SHA256ECDSA_Server.pfx",
"SpRp_RsaPfxFilePath": "C:/root/files/resource/X509/SHA256RSA_Client.pfx",
"SpRp_ClientCertPfxFilePath": "C:/root/files/resource/X509/SHA256RSAClientCert.pfx"RsaPfx* はサーバ(トークンの署名)、SpRp_* はクライアント側(Request Object の署名、mTLS)。
絶対パスで書かれている。 リポジトリの root/files/resource/X509 を、
そのパスへ配置するか、値を書き換える。生成用のバッチが同じフォルダにある。
同梱の証明書は、テスト用の自己署名である。 本番では使わないこと(パスワードも雛形に書いてある)。
SpRp_ClientCertPfxFilePath(SHA256RSAClientCert.pfx)は、アプリが外向きに呼ぶときの HttpClient に必ず載る(Extensions/Sts/Helper.cs)。サーバが要求すれば、これを提示する- Subject は、クライアント登録の
tls_client_auth_subject_dnと一致させること。 雛形ではTestClient1/TestClient2の値(CN=MPAS Test Client) - 作り直すバッチは
GenClientCertByOpenSSL.bat(先頭に_の付いたファイルができる。 確かめてから名前を変えて置き換える) - 期限切れにしないこと。 期限切れの証明書を提示すると、要求した相手との TLS がそこで失敗する
fapi2 の登録は、mTLS(RFC 8705 の tls_client_auth)でしか通らない(ANALYSIS-IdP.md C-7)。
使うには、サーバ側で、クライアント証明書を要求させる設定が要る。雛形のままでは要求しない(#226 で実測)。
アプリは、証明書の Subject と、登録の tls_client_auth_subject_dn の一致だけを見る。
発行元(チェーン)と失効の検証は、TLS の層(Kestrel / IIS)に任せている。
したがって、TLS の層で信頼できる発行元だけを受け付けるように設定すること。
| 設定 | 検証 | |
|---|---|---|
| net10.0(Kestrel) | Kestrel:EndpointDefaults:ClientCertificateMode を AllowCertificate(証明書の無いクライアントも通す)または RequireCertificate。appsettings.json でも環境変数(Kestrel__EndpointDefaults__ClientCertificateMode)でもよい |
既定でチェーンと失効を検証する。自己署名など信頼できない証明書は、TLS の段階で切れる |
| net48(IIS) | サイトの <access sslFlags="Ssl, SslNegotiateCert" />(証明書を要求するが、無くても通す) |
信頼できない証明書は、アプリより前で HTTP 403.16 になる |
- 証明書を要求させる口は、
/tokenだけでは足りない。 mTLS で発行したトークンは証明書に紐づく(cnf)ので、そのトークンを受ける口でも照合する(RFC 8705 §3。ANALYSIS-IdP.mdC-19)。 照合には証明書の提示が要るため、/userinfoにも同じ設定が要る。 紐づいたトークンを/ciba_result/SetDeviceToken/2fa_resultにも出すなら、それらにも要る (認証デバイスは証明書を使わないので、通常は不要)。 net48(IIS)は、サイト全体ではなく口ごとに掛けること。サイト全体に掛けると、 サーバが自分自身を呼ぶ経路(FAPI2 の自己テスト)が証明書を求められて止まる(実測) - リバース プロキシで TLS を終端する場合、アプリには証明書が届かない。 今の実装は、プロキシが転送するヘッダ
(
X-ARR-ClientCertなど)を読まない tls_client_auth_subject_dnは JSON の文字列なので、\\は 1 文字の\になる。 照合するのは、.NET が返すX509Certificate2.Subjectの文字列- E2E のテスト専用のフック(
Tests/MtlsTestHook)は、発行元を問わずに受け付ける。本番の構成で読ませないこと (test.ps1 -Launchが、起動したサイトにだけDOTNET_STARTUP_HOOKSで渡す。Development 以外では何もしない)
E2E テストの AppConfig.cs が実際に踏んだもの。同じことをするときは注意する。
XML 1.0 §3.3.3 のとおり、パーサは属性値の改行を空白へ正規化する。
OAuth2ClientsInformation は // コメント付きの JSON なので、
XDocument から取ると 1 行になり、最初の // が以降を全部飲む。
生のファイル テキストから取り直すこと。
雛形のコメントを正規表現で落とそうとすると、https://... の // まで消える。
JSON パーサのコメント処理(JsonCommentHandling.Skip)を使うこと。
| net48 | net10.0 | |
|---|---|---|
| 設定ファイル | app.config(Web.config から file= で取り込み) |
appsettings.json |
| ルート要素 / セクション | <appSettings> |
appSettings |
| コメント | XML コメント + JSON 文字列内の // |
JSONC の // |
| 環境変数で上書き | FxContainerization=ON(キー名そのまま) |
appSettings__<キー> / FxContainerization=ON |
| クライアント登録 | JSON 文字列 | JSON オブジェクト |
| 既定の起動 | IIS Express | IIS Express / Kestrel |
| パッケージ | packages.config + PackageReference |
PackageReference |
| 認証クッキーの設定 | App_Start/StartupAuth.cs |
Startup.cs の ConfigureApplicationCookie(#223) |
両者は共通ライブラリを使う別アプリである。 片方にしか無い問題があり得る。
実例:
AuthCookieExpiresFromHours/AuthCookieSlidingExpirationは、 net10.0 では長らく読まれていなかった(#223)。 設定は書かれていたが、誰も使っていないスキームに対する指定だったため。 雛形の値(336時間)が Identity の既定(14 日)と偶然一致していて、表面化しなかった。 「設定ファイルに在る」ことと「効いている」ことは別である。
雛形の既定は「開発・テストで動く」状態である。 本番へ出す前に、次を確認する。
一覧の目的は、読み落としを減らすこと。 各キーの意味は雛形のコメントが一次情報。
| キー | 雛形の既定 | 本番 | なぜ |
|---|---|---|---|
UserStoreType |
mem |
sql / ora / npg |
mem は再起動で消える。mem のままだと IsDebug が常に true になる(下の注意 1) |
IsDebug |
true |
false |
テスト利用者の生成、メール / SMS の送信の代替、ログの扱いが変わる |
DataProtectionKeyPath |
""(空) |
コンテナでは必須(#251) | DataProtection の鍵の置き場。 空なら %LOCALAPPDATA% 配下(コンテナでは揮発 → 再起動で全員サインアウト)。net48 の machineKey と同じ役割だが、鍵そのものは書かない(置き場を共有する。鍵は自動生成・自動ローテーション)。効くのは画面のセッション(認証 Cookie / AntiForgery / メール確認のリンク)で、access_token・PPID・refresh_token には影響しない。鍵リングは平文の XML。net10.0 版だけ |
OAuth2ContainerizatedAuthSvrFqdnAndPort / OAuth2ContainerizatedAuthSvrEPRootURI |
""(空) |
コンテナ配備で自己テストを使うときだけ | サーバが自分自身を呼ぶときの宛先(#250)。宛先は OAuth2AuthorizationServerEndpointsRootURI から組み立てられるが、コンテナの中からは外向けのホスト名・ポートに届かない(実測 : コンテナ内から localhost:44301 は CLOSED、待ち受けは 8080 / 8081)。Helper.GetContainerizatedAuthZServerUri が差し替える(Windows でないときだけ働く)。FqdnAndPort はホスト名とポートだけ、EPRootURI はスキームごと差し替える。HTTPS のままにすると、コンテナの中で証明書を検証できないので、store/ の上流は EPRootURI に HTTP のループバックを与えている |
CookieNamePrefix |
""(空) |
同じホストに 2 つ立てるときだけ | Cookie の名前に付ける接頭辞(#255)。先頭が . なら、その後ろに入る(.MultiPurposeAuthSite → .upstream_MultiPurposeAuthSite)。名前を決められるものすべてに掛かる — 認証・外部ログイン・2FA(Identity の 4 スキーム)、セッション、auth_time / re_auth_at、TempData。max_age の判定に使うので、混ざると再認証の要否を誤る(サインインは妨げない)。名前そのものは AuthCookieName と sessionState:SessionCookieName で決め、この設定は「どの配備か」を表す(役割が違う)。分けられないのは SessionTimeOut(Open棟梁 の定数)だけだが、雛形は FxSessionTimeOutCheck を off にしているため読まれない。AntiForgery はもともとアプリごとに違う名前になるので対象外。net48 版のセッション Cookie は ASP.NET のもの(system.web/sessionState)で、これも対象外 |
AuthCookieName |
""(空) |
同じホストに 2 つ立てるときだけ | 認証 Cookie の名前(#250 の段階 4)。空なら既定(net10.0 : .AspNetCore.Identity.Application / net48 : .AspNet.ApplicationCookie)。Cookie のスコープにポートは入らない(RFC 6265 §8.5)ので、localhost:44300(下流)と localhost:44301(上流)は Cookie を共有し、後にサインインした側が相手を蹴り出す。 パスが違っても解決しない(仮想ディレクトリ配下と root で同名・別パスの Cookie が 2 つ並ぶ)。ID フェデレーションは毎回この経路を通るので、上流には別名を与えること |
UserClaimsMapping |
{}(空) |
任意 | profile / address で返すクレームの対応付け(#230)。空なら何も返らない。 値の在り処は UnstructuredData の中のパスか、user:UserName / user:Email / user:PhoneNumber。利用者名を RP に渡したいなら {"preferred_username": "user:UserName"}(#151 の段階 1)。sub は利用者を指す識別子なので、そこに載せてはならない。ID 連携の下流は、新規に作る利用者名にこれを使う(#151 の段階 4) |
EnableDebugTraceLog |
true |
false |
冗長なトレースを止める(改名した。旧 EnabeDebugTraceLog。下の 12 節) |
TestUserPWD |
[password of TestUser] |
空にする | 空なら、テスト利用者(super_tanaka@gmail.com / tanaka@gmail.com)を作らない |
AdministratorUID / AdministratorPWD |
[Please fill in this input item.] |
実運用の値 | IsDebug に関係なく作られる(下の注意 2)。既定のまま出さない |
IsLockedDownTestEndpoints |
false |
true |
テスト用の口をまとめて閉じる。 自己テスト画面(/Home/Saml2OAuth2Starters)、テスト用のリダイレクト先、/TestHybridFlow、api/Values(net10.0)。/Ping は閉じない(下の注意 3) |
EnableImplicitGrantType / EnableResourceOwnerPasswordCredentialsGrantType |
false(#220 で変更) |
false のまま |
OAuth 2.1 で廃止されたフロー。 コードは残してあるので、必要なら true に戻せる |
RequirePkce / RequirePkceS256 |
false |
任意(下の注意 5) | OAuth 2.1 に寄せるための締め金(#220)。既定は従来どおり緩い。RequirePkceS256 は Discovery の code_challenge_methods_supported にも効く(#228) |
RequireVerifiedEmailForAccountLinking |
true(#140 の段階 1 で追加) |
true のまま |
外部 ID を既存アカウントに結び付けるとき、上流が email_verified: true と言ったメアドだけを鍵にする。 未設定でも true(他の Require* と既定の向きが違う)。false にすると従来どおり(上流の言い値で結び付ける)。Google は email_verified を写すようにしたので自動リンクできる。Microsoft Account は示せないので、/Manage/ManageLogins での明示的な追加になる |
FacebookAuthentication / TwitterAuthentication |
false |
触らない | サポートを取り下げた(#249)。true にしても有効にならない(アプリ側の登録をコメント アウトしてある)。動かないからではなく、維持コストが便益に見合わないため。 キーを残してあるのは戻せるようにするため(CommonLibrary/ANALYSIS.md 12 節) |
ServiceDocumentation |
""(空) |
任意 | Discovery の service_documentation。空なら出さない(#228)。文書を公開しているなら、その URL |
AuthRequestPushUri |
/par |
既定のまま | PAR(RFC 9126)の口(#229)。独自の /ros(RequestObjectRegUri)とは別。改名した(旧 PushedAuthorizationRequestEndpoint。下の 12 節) |
OAuth2AuthorizationCodeExpireTimeSpanFromSeconds |
600 |
既定のまま(または短く) | 認可コードの寿命(#188)。RFC 6749 §4.1.2 は 10 分以内を推奨 |
RequestObjectExpireTimeSpanFromSeconds |
300 |
既定のまま(または短く) | /ros に預けた Request Object の寿命(#188)。応答の exp にも出る |
OAuth2RefreshTokenExpireTimeSpanFromDays |
14 |
運用に合わせる | #188 で、実際に検証するようになった(以前は事実上の無期限)。短くすると、既存のトークンが失効する |
#188 で
RefreshTokenDictionaryに 2 列を足した(FamilyId/UsedDate。3 方言とも)。 既存のデータベースにはALTERが要る(移行用のスクリプトは用意していない)。 新規に作る場合はCreate_UserStore.sqlのままでよい。詳細はANALYSIS-IdP.mdC-5。 |FcmOutboxDirectory|""(空) | 空のまま | 設定すると、プッシュ通知を FCM に送らずファイルに書く(テスト用。2 節) | |OAuth2ClientsInformation| テスト用が 12 件 | 実運用のものだけ残す |TestClientTestClient1〜5MVC_SampleWebForms_SampleSPA_ApplicationNative_ApplicationAuthenticationDevice_WebIdFederationが登録済みクライアントとして使えるまま |
net48 / net10.0 で、キー名と既定値は同じ。 書き方だけ違う(10 節)。
<!-- app.config -->
<add key="IsDebug" value="false" />
<add key="TestUserPWD" value="" />// appsettings.json
"IsDebug": "false",
"TestUserPWD": "",| 見るもの | 期待 |
|---|---|
/Home/Saml2OAuth2Starters |
自己テスト画面ではなく Index が出る(IsLockedDownTestEndpoints) |
| 雛形のテスト利用者でサインイン | できない(TestUserPWD が空なら作られていない) |
.well-known/openid-configuration |
HTTP 200 で、issuer が本番の URL(5 節) |
ACCESS / OPERATION ログ |
冗長なトレースが出ていない(EnableDebugTraceLog) |
閉じる対象がリダイレクト先だけではなくなったため、名前を実態に合わせた(#219)。
- 旧いキー名も読む。 新しいキー名が無ければ、旧いキー名を使う
- 旧いキー名だけのときは、起動時に警告する(下の「起動時の自動確認」)
- 未設定のときは
false(=開く)。 だから「改名しただけ」だと、 既存の設定ファイル(旧キーしか無い)で本番が黙って開いてしまう。 互換を残したのはこのため
このチェックリストの読み落としを拾うため、起動時にも確かめている(Co/ProductionCheck。両アプリ)。
- 該当すると、
OPERATIONログに[設定の確認] …(CONFIGURATION.md 11 節)が出る - 起動は止めない。 設定を直せない状況で復旧できなくなるため
UserStoreTypeがmemのときは何も言わない(開発・テスト専用の構成なので、雑音にしかならない)。 ただしAdministratorUID/AdministratorPWDが雛形の値のままのときだけは、ストアによらず言うRequirePkce/RequirePkceS256の 1 件だけは、性質が違う(#220)。falseは「開発向けの設定が残っている」ではなく、従来の OAuth 2.0 のままというだけで、 それ自体は誤りではない。本番では意図して選ぶべきなので、選ばれていないことだけを知らせる
ログに出ていないこと=設定が正しいこと、ではない。 確かめているのは上の表のうち、 機械で判る範囲だけ(クライアント登録の中身などは見ていない)。
-
IsDebugはUserStoreType = memのとき、設定を無視して常にtrueを返す (CommonLibrary/Co/Config.cs)。IsDebug=falseと書いても効かない。 本番は DBMS 前提。 -
管理者ユーザ(
AdministratorUID)は、IsDebugに関係なく無条件で作られる。 テスト利用者だけがIsDebugとTestUserPWDで閉じられる。 -
/Pingは閉じない。 セッションのタイムアウト防止に使われているため(#219)。 本番で塞ぐなら、前段(リバース プロキシなど)で行う。/TestHybridFlowとapi/Values(net10.0 のみ)は、IsLockedDownTestEndpointsで閉じる。 -
STS 専用モード(
EnableSignupProcess/EnableEditingOfUserAttribute/EnableAdministrationOfUsersAndRolesを全部 false)にすると、サインアップ・属性の編集・ ユーザ管理が無効になる。利用者ストアへの書き込みも止まるので、切替の影響が大きい。 -
PKCE の 2 つのキーは、別のものを締める(#220。どちらも既定
false)。キー 何を求めるか どこで弾くか 有効にすると通らなくなるもの RequirePkcePKCE 自体( code_challenge)認可エンドポイント( invalid_request)PKCE を使っていない既存クライアント RequirePkceS256使うなら S256に限るトークン エンドポイント plainを使っているクライアント両方
trueが OAuth 2.1 相当。 ただしクライアントが揃っていないと繋がらなくなるので、 既存の登録を確かめてから切り替える。Device AuthZ / CIBA はRequirePkceの対象外 (認可エンドポイントを通らないため)。RequirePkceは、クライアント単位でも指定できる(#221)。 クライアント登録にrequire_pkceを書くと、そのクライアントにだけ必須になる。"c4309326f39b1e0975fddb4bc93b56a0": { "client_secret": "...", "redirect_uri_code": "http://localhost:12347/", "client_name": "TestClient6", "require_pkce": "true" }
判定は
RequirePkce(サーバ全体)との OR。 サーバ側が「全クライアント共通の床」、クライアント側は「個別の引き上げ」で、 クライアント側から床を下げることはできない。 移行では、締められるクライアントから順にrequire_pkceを立て、 全部揃ったらサーバのRequirePkceをtrueにする、という順序が取れる。oauth2_oidc_modeをfapi1にしても PKCE は必須になるが、そちらは重い。 ROPC /client_credentials/refresh_tokenも巻き添えで塞がる(実測。#222)。 「PKCE だけ必須にしたい」ならrequire_pkceを使う。
設定を変えたら、雛形(
_app.config/_appsettings.json)にも反映する(1 節)。 本番の値そのものは書かない。
改名しても、旧いキー名を読み続ける。 配備済みの設定ファイルがあり、
改名だけで黙って既定値に戻ると危ないため(IsLockedDownTestEndpoints は、
既定が「開く」なので特に)。
一覧は Config.RenamedKeys が一次情報で、起動時に ProductionCheck が
「旧いキー名が使われています」と警告する(UserStoreType が mem 以外のとき)。
| 旧いキー名 | 新しいキー名 | なぜ | 旧キーも読む |
|---|---|---|---|
IsLockedDownRedirectEndpoint |
IsLockedDownTestEndpoints |
閉じる対象がリダイレクト先だけではなくなった(#219) | 読む |
EnabeDebugTraceLog |
EnableDebugTraceLog |
綴りの誤り(Enabe) |
読む |
IdFederationAuthorizeEndPoint |
IdFederationAuthorizeEndpoint |
EndPoint の P を、他のキーに揃えた |
読む |
IdFederationRedirectEndPoint |
IdFederationRedirectEndpoint |
同上 | 読む |
IdFederationTokenEndPoint |
IdFederationTokenEndpoint |
同上 | 読む |
IdFederationUserInfoEndPoint |
IdFederationUserInfoEndpoint |
同上 | 読む |
PushedAuthorizationRequestEndpoint |
AuthRequestPushUri |
クライアント側も読む設定なので、RequestObjectRegUri / JwkSetUri と同じ形に寄せた。Open棟梁 側へ移す予定 |
読まない(#229 で入れたばかりで、配備実績が無い) |
IdFederationRedirectEndpoint の値に含まれる Account/IDFederationRedirectEndPoint は、
画面の口(アクション名)なので変えていない。 変えると、委譲先に登録した redirect_uri と
食い違う。
| 接尾辞 | 例 | 理由 |
|---|---|---|
...RootURI |
OAuth2AuthorizationServerEndpointsRootURI |
エンドポイントではなく、その根っこ(種別が違う) |
...Uri |
JwkSetUri / RequestObjectRegUri / AuthRequestPushUri |
クライアント側も読む設定で、実装が Open棟梁 側にある(OAuth2AndOIDCParams)。この実装だけでは改名できない |
...Endpoint |
それ以外 | こちらに揃えた |