Skip to content

Latest commit

 

History

History
365 lines (273 loc) · 20.8 KB

File metadata and controls

365 lines (273 loc) · 20.8 KB

Tests

MultiPurposeAuthSite のビルド確認E2E テスト

このリポジトリには CI が無く、動作確認は手作業だった。 そのため「コードを読んだ結論」と「実際の動作」がずれても気づけない。 ここは、仕様への適合を実測で確かめるための場所。

TESTCASES.md テストケースの原本。 何を・何を根拠に確かめるのか(生成物)
test.ps1 サイトを起動して E2E テストを実行する
E2ETests/ xUnit のテスト プロジェクト(net10.0)

通しで回すときは root のスクリプトを使う。 OpenTouryo が root/programs/*.ps1 から CS/*.bat を呼ぶのと同じ構造で、 root/*.ps1programs/ のビルド bat と、この test.ps1 を呼ぶ。

..\..\..\0_RunAll.ps1 ビルド → テストの通し
..\..\..\1_BuildAll.ps1 ビルド bat を呼び、エラー・警告を集約する
..\..\..\2_RunAllTests.ps1 この test.ps1 を呼び、TRX を読んで集約する

方針

ブラックボックスで測る

テストは、アプリを HTTP で外から叩くCmnEndpointsHelper といった実装側のクラスは参照しない。

JWT のデコードも Request Object の署名も、テスト側で独立に実装している (Infrastructure/Jwt.cs / Infrastructure/RequestObject.cs)。 同じコードで作って同じコードで読むと、型や値の誤りを検出できないため。

net10.0 版と net48 版に、同じテストを流す

このリポジトリはクロスコンパイルで下位互換版を維持している。 「片方だけ直っている」状態を検出できることを最優先にしているので、 テストは [SkippableTheory]AllTargets で両方に流す。

起動していない対象は Skip する(失敗にしない)。 net48 版は IIS Express での手動起動が前提で、常に動いているとは限らないため。

秘密情報をリポジトリに置かない

TestUserPWD / client_secret / pfx のパスワードは、 テスト実行時にアプリ自身の構成ファイルから読み出すappsettings.json / app.config.gitignore 済み)。

client_id も直書きしない。環境ごとに違う(CreateClientsIdentity.exe で生成する)ので、 client_nameTestClient / MVC_Sample など)から引く。

雛形にクライアントを足したときは、実設定にも足す。 実設定は各自のものなので、 雛形(_appsettings.json / _app.config)を当て直すまでは登録されていない。 その間、そのクライアントを使うテストは Skip する(Flows.SkipIfClientNotRegistered)。

client_name 何のために登録してあるか
TestClient5 登録の scope で、要求してよいスコープを制限(#198)
TestClient6 クライアント単位で PKCE を必須require_pkce。#221)

テストの出力にトークンや秘密情報を書かないこと。 JsonResponse.ToString() はキー名とエラーだけを出す。

実行

いちばん簡単な方法

cd root\programs\Tests
.\test.ps1 -Launch

net10.0 版と net48 版の両方を起動し、テストを流して、停止する。

対象 待ち受け 立て方
net10.0 https://localhost:44300 Kestrel(-Url で変えられる)
net48 https://localhost:44302 IIS Express(-NetFxUrl で変えられる)

net48 版を測らないときは -NoNetFx。その分は Skip される。

すでにサイトが動いている場合

.\test.ps1

叩き先は、既定では構成ファイルの OAuth2AuthorizationServerEndpointsRootURI。 つまり、Visual Studio(IIS Express)で起動していれば、そのまま繋がる。

通しで回す

cd root
.\0_RunAll.ps1           # ビルド → テスト
.\1_BuildAll.ps1         # ビルドだけ
.\1_BuildAll.ps1 -List   # ビルドの対象一覧
.\2_RunAllTests.ps1 -Launch

起動する URL について(重要)

サイトは、構成ファイルに書かれた URL で待ち受けている必要がある。

アプリ同梱の自己テスト(FAPI2 / CIBA / Device AuthZ)は、 サーバ自身が OAuth2AuthorizationServerEndpointsRootURI へ HTTP で折り返す。 叩き先と構成が食い違うと、その折り返しが接続不能になり HTTP 500 になる。

https で動かすこと。 認証まわりの Cookie は SameSite=None で発行されるため、http では保持されない。 max_age を使うフロー(FAPI2)は auth_time Cookie を見るので、http だとエラー画面になる。

test.ps1 -Launch は、この 2 つを環境変数で揃えてから起動する。

OAuth2AuthorizationServerEndpointsRootURI
OAuth2ClientEndpointsRootURI

あわせて、プッシュ通知の送信箱 FcmOutboxDirectory を設定する(Result/fcm/coreResult/fcm/netfx)。 サイトは FCM に送らずここへファイルを書き、テストが認証デバイスの代わりに読む(EX-8)。

appSettingsFxContainerizationON のとき、Open棟梁 は 設定ファイルより環境変数を優先する(net48 / net10.0 の両方)。 キー名がそのまま環境変数名になる。 接頭辞は付かない。

このため 2 つのサイトを別々の URL で同時に立てられる。

対象 既定
net10.0(Kestrel) https://localhost:44300
net48(IIS Express) https://localhost:44302

設定

E2ETests/_testsettings.json が雛形。 変えたいときは testsettings.json にコピーして編集する(.gitignore 済み)。

環境変数でも上書きできる。

環境変数 意味
MPAS_CORE_BASEURL / MPAS_NETFX_BASEURL 叩き先の URL
MPAS_CORE_CONFIG / MPAS_NETFX_CONFIG 構成ファイルのパス(root/programs からの相対)
MPAS_TESTUSER テスト ユーザ名
MPAS_CORE_FCM_OUTBOX / MPAS_NETFX_FCM_OUTBOX プッシュ通知の送信箱(-Launch が設定する。無ければ CIBA の EX-8 は Skip)
MPAS_CONNSTR_SQL / MPAS_CONNSTR_ODP / MPAS_CONNSTR_NPS -UserStoreTypesql / ora / npg に切り替えるときの接続文字列(#207)

UserStoreType の既定は mem。テスト ユーザは初回アクセスで作られ、 再起動で消えるので、テストの前後で状態を掃除する必要が無い。

利用者は 2 人いる

認証サイトは IsDebug のとき、同じ TestUserPWD で 2 人作る(AccountControllerCreateData)。

利用者 使い道
super_tanaka@gmail.comTestEnv.TestUserName 既定。SignedInClientAsync が何も指定しなければこちら
tanaka@gmail.comTestEnv.SecondUserName 「別の利用者」が要るとき(EX-8.4)と、「端末が無い利用者」が要るとき(RT-210.1

別の利用者でサインインするには、利用者名を渡す。

using (IdPClient other = await this.SignedInClientAsync(targetKey, TestEnv.SecondUserName))

2 人目には、端末(device_token)を登録しないこと。 RT-210.1(#210)が「端末が登録されていない利用者」として使っているため、 登録すると、実行順によってそのテストが失敗するようになる。

sql / ora / npg に切り替えられるtest.ps1 -UserStoreType、#207)。 設定ファイルは書き換えず、環境変数で上書きする(FxContainerization=ON のため、 GetConfigValueGetConnectionString のどちらも環境変数が優先される)。 切り替えると状態が残るので、作り直したいときはデータベースを作り直す。 手順と前提は ../../TESTING.md 1 節「ストアを切り替える」。

テストの構成

Tests/SmokeTests.cs が土台。 サイトに届いているか、サインインできるか、 認可コード フローが通るか。ここが倒れていたら、他の合否は読む意味がない。

ファイル 識別子 対象
Tests/SmokeTests.cs SM-n Discovery / JWK Set / サインイン / 認可コード フロー

次が Tests/Basic/ OAuth 2.0 / OIDC の基本的な検証項目を、 仕様の根拠つきで並べたもの(TC-1 〜 TC-6)。

ファイル 対象
Tests/Basic/CommonSecurityTests.cs TC-1 state / redirect_uri / スコープ / 有効期限
Tests/Basic/AuthorizationCodeFlowTests.cs TC-2 正常系 / code 使い捨て / クライアント認証 / PKCE
Tests/Basic/TokenResponseTests.cs TC-3.2 トークン応答の約束(キャッシュ制御。フローに依らない)
Tests/Basic/ClientCredentialsTests.cs TC-5 クライアント資格情報(OAuth 2.1 でも有効
Tests/Basic/OidcTests.cs TC-6 id_token の中身と署名 / alg:none の拒否 / UserInfo

その次が Tests/Extended/ 基本テストケースに含まれない、追加の仕様・拡張仕様(EX-1 〜 EX-8)。

ファイル 識別子 対象
Tests/Extended/RefreshTokenTests.cs EX-1 refresh_token の更新・ローテーション・発行先との結び付け(RFC 6749 §6 / RFC 9700)
Tests/Extended/RevocationTests.cs EX-2 トークンの失効(RFC 7009)
Tests/Extended/IntrospectionTests.cs EX-3 トークンの問い合わせ(RFC 7662)
Tests/Extended/DeviceAuthorizationTests.cs EX-4 Device Authorization Grant(RFC 8628)
Tests/Extended/HybridFlowTests.cs EX-5 OIDC Hybrid フロー(c_hash / at_hash)
Tests/Extended/ResponseModeTests.cs EX-6 response_mode(fragment / form_post / JARM)
Tests/Extended/JwtBearerTests.cs EX-7 JWT Bearer グラント(RFC 7523)
Tests/Extended/CibaTests.cs EX-8 CIBA(認証デバイスとプッシュ通知は、テストで置き換える)

PAR / JAR は、拡張仕様としては扱っていない(request_uri の経路は RT-197 で測っている)。

CIBA(EX-8)は、認証デバイス(authentication_device)とプッシュ通知を、テストで置き換える。 サイトは FCM に送らず送信箱(FcmOutboxDirectory)にファイルを書き、テストはそれを読んで、 認証デバイスと同じ要求(/SetDeviceToken/ciba_result)を送る。送信箱は -Launch のときだけ設定されるので、 それ以外では EX-8 は Skip する。認証リクエストのエラーの返し方は RT-196 で測っている(ES256 で署名した要求を /ros に登録する)。

Tests/Obsolete/ は、OAuth 2.1 で廃止されたフロー(#220)。

ファイル 識別子 対象
Tests/Obsolete/ImplicitFlowTests.cs TC-3.1 Implicit(フラグメント返却)
Tests/Obsolete/PasswordTests.cs TC-4 ROPC

消さずに残す。 -Launch では有効にして起動するので、これまでどおり測る(後述)。 一覧(報告書・TESTCASES.md)では最後尾に置く。

最後が Tests/Issues/ 個別の Issue に対応する回帰テスト(RT)。

ファイル 識別子 対象
Tests/Issues/TokenClaimTests.cs RT-182 RT-184 expires_in、JWT のクレーム型
Tests/Issues/NonceTests.cs RT-183 RT-190 RT-191 nonce の要否と扱い
Tests/Issues/ErrorResponseTests.cs RT-185 RT-187 エラー応答
Tests/Issues/RedirectUriBindingTests.cs RT-186 redirect_uri の照合
Tests/Issues/HttpStatusTests.cs RT-196 エラー応答の HTTP ステータス(OAuth2 / OIDC の各エンドポイントと、認証デバイスの口)
Tests/Issues/RequestObjectTests.cs RT-197 request_uri(JAR)経路の redirect_uri / PKCE の紐付け
Tests/Issues/ScopeTests.cs RT-198 宣言外のスコープ、登録の scope に無いスコープを発行しない
Tests/Issues/CacheControlTests.cs RT-218 トークンを返す口の Cache-Control: no-store / Pragma: no-cache
Tests/Issues/PkceTests.cs RT-220 PKCE : client_secret との併用、plaincode_challenge の要否

Tests/Fapi/ は、クライアント登録(oauth2_oidc_mode)ごとに通る経路(#222)。

ファイル 識別子 対象
Tests/Fapi/ClientModeTests.cs FA-1 fapi1(PKCE の経路だけが通る/使えない refresh_token
FA-2 fapi2client_secret も PKCE も通らない。x509 が要る)
FA-3 device(PKCE で通る。CheckClientMode の例外措置)

今の振る舞いを記録するためのテスト。 望ましくないと考える点(fapi1 が使えない refresh_token を発行する等)は**「観測」として書き、合否には影響させない**。 permittedLevel を作り直すとき(#222 の 3)に、壊していないことを確かめる土台になる。

Tests/OAuth21/ は、OAuth 2.1 が許さない経路の抑止(#222)。

ファイル 識別子 対象
Tests/OAuth21/ProfileTests.cs 21-1 締めた登録では Implicit / ROPC / PKCE 無しが塞がる
21-2 アクセス トークンは Authorization ヘッダでのみ受け付ける

サーバ全体の設定は変えない。 -Launch は Implicit / ROPC を有効にして起動し、 RequirePkcefalse のまま。締めるのはクライアント単位の登録oauth2_oidc_mode / require_pkce)なので、 **「サーバは開いているのに、このクライアントでは塞がる」**という対照が効く。

サーバ全体の RequirePkce / RequirePkceS256true にしたときの挙動は測れない。 設定ファイルを変えて起動し直す必要があるため(CONFIGURATION.md 11 節)。

フォルダは、識別子の群に合わせているBasic = TC、Extended = EX、Issues = RT、 Fapi = FA、OAuth21 = 21、Obsolete = 廃止されたフロー)。 ただし厳密な一対一ではない。 回帰テストが既存のケースを対照として使うことがあり、 Tests/Issues/HttpStatusTests.cs には EX が、Tests/Extended/IntrospectionTests.cs には RT が混ざっている。対照は近くに置いたほうが読めるので、そこは揃えていない。

すべてのテストが TestReport で記録を残す。 識別子の体系は ../../TESTING.md を参照。

Infrastructure/ は、テストから使う道具。

ファイル 役割
TestEnv.cs テスト対象(net10.0 版 / net48 版)と、その到達性
AppConfig.cs appsettings.json / app.config の読み取り
IdPClient.cs サインイン、認可、トークン、UserInfo、失効・問い合わせ、デバイス認可、CIBA、認証デバイスの代わりの要求、自己テストの起動
FcmOutbox.cs プッシュ通知の送信箱(テスト用)の読み取り。認証デバイスの代わりに受け取る
Flows.cs 認可コード フローの組み立て、トークンの更新・失効・問い合わせ、クライアントの解決
RequestObject.cs Request Object(CIBA の認証リクエストを含む)の組み立てと PAR への登録
JwtBearerAssertion.cs JWT Bearer グラント(RFC 7523)の assertion の組み立て
JwsSigner.cs RS256 / ES256 の署名(Request Object・assertion・CIBA の要求で共用)
Jwt.cs JWT のデコード(検証はしない)と、c_hash / at_hash の計算
Base64Url.cs BASE64URL の変換
Jwks.cs JWK Set での署名検証、alg:none 化・改竄(テスト用)
TestReport.cs テストの内容と結果を、それ自体で読める形に書き出す
Html.cs リダイレクトしなかったときの画面の要約、form_post のフォームの解析
Responses.cs 各エンドポイントの応答
TargetTestBase.cs 両ターゲットに同じテストを流す基底クラス

テストを足したり変えたりしたら、原本を作り直すこと。

cd root
.\2_RunAllTests.ps1 -Launch -UpdateTestCases

既定で無効な機能のテスト(Tests/Obsolete/

OAuth 2.1 で廃止されたフロー(Implicit / ROPC)のテストは Tests/Obsolete/ に置く(#220)。

  • 消さない。 設定で有効にしている環境のために残す
  • -Launch では、必ず測る。 test.ps1 がサイトを起動する直前に、 EnableImplicitGrantType / EnableResourceOwnerPasswordCredentialsGrantType を 環境変数で true にする(FxContainerization = ON なので、キー名がそのまま環境変数名になる。 RootURI の渡し方と同じ)。設定ファイルは書き換えない。 無効のまま Skip にすると、廃止したフローの回帰が効かなくなるため
  • テスト自身も discovery を見て Skip するFlows.SkipIfGrantTypeNotSupportedAsync)。 -Launch を付けず、無効な環境へ向けて回したときのための保険
  • Tests/Issues/ にも、同じ理由で Skip するテストがあるRT-190.1 / RT-190.2 / RT-198.2)。 置き場所ではなく、依存する機能で決まる
  • 一覧では最後尾に出る。 2_RunAllTests.ps1 が、報告書と TESTCASES.md の並びで *.Tests.Obsolete.* を後ろへ回す。実行順は変えていない(xUnit の既定のまま)。 識別子は TC-3.1 / TC-4 のままなので、位置だけが動く

未修正の項目

未修正だと分かっている項目は、期待する動作を書いたうえで Skip にしている。 消さずに残すのは、直したときに Skip を外すだけで検証できるようにするため。 Skip の理由に、実測した日付と結果を書く。

現在、未修正を理由に Skip にしているものは無い(2026-09-17 時点)。

最後まで残っていた RT-187.4ErrorResponseTests。未知の response_type が、リダイレクトではなく エラー画面になる)は解消した。Skip を外して、両ターゲットで通ることを確かめてある。

対象ごとの Skip(「そのサイトが起動していない」)は、これとは別。 -Launch を付けずに片方だけで回せば出る。

分かっていること(実測)

request_uri 経路について測った結果(#197)。

修正前(2026/09/09, net10.0) 修正後(2026/09/11, net10.0 / net48)
認可コードの発行 できる できる
redirect_uri の照合 効いていない。 誤った値でもトークンが出る 誤った値は invalid_grant
PKCE(code_challenge 記録されないため、code_verifier を送ると invalid_client素通りではなく拒否 正しい検証子で通り、誤った検証子は invalid_client

修正前は、AuthorizationCodeProvider.Create がこれらの値を Request Object ではなくクエリ文字列から読んでいたことによる。 #197 で、request_uri 経路では Request Object の値を使うように直した。

制約

  • FAPI2 のクライアントは、client_secret だけのトークン要求を受け付けないunsupported_grant_type)。mTLS / private_key_jwt が要る。 このため redirect_uri の照合は、normal モードのクライアントに 自前の Request Object を渡して測っている。
  • net48 版の起動には IIS Express が要る%ProgramFiles%\IIS Express)。 無い場合・ビルドされていない場合は、理由を出してその分を Skip する(失敗にしない)。
  • 構成ファイルの既定では、net10.0 版と net48 版は同じ URL を指している。 -Launch は環境変数で別のポートへ寄せるので同時に測れるが、 手で立てるときは片方を別の URL にすること。
  • CIBA のテスト(RT-196.16196.18EX-8)は、構成ファイルの SpRp_EcdsaPfxFilePath(ES256 の秘密鍵)で要求に署名する。 CIBA のクライアント(TestClient4)が登録している jwk_ecdsa_publickey と対になっていること。 取り違えは検出して Skip する(../../TESTING.md 5 節)。