Tenant設定ガイド(開発者向け)
📍 このドキュメントの位置づけ
対象読者: Phase 1(how-to 01-05)完了済みの開発者
このドキュメントで学べること:
- 本番運用に向けた詳細なTenant設定
- ユースケース別の設定パターン
- 高度な機能(Extension、カスタムスコープ、カスタム クレーム)
- セキュリティとパフォーマンスのベストプラクティス
How-toガイドとの違い:
| ドキュメント | 目的 | 内容 |
|---|---|---|
| How-to | 最小構成で動かす | 実践的な手順(動作確認重視) |
| Developer Guide | 本番設定を理解する | 詳細仕様と設計パターン |
前提知識:
- how-to-01: 組織初期化完了
- how-to-02: OAuth/OIDC認証の最小設定完了
- OAuth 2.0/OpenID Connectの基礎理解
🧭 Tenantアーキテクチャの理解
Tenantとは
Tenant(テナント)は、マルチテナント環境における完全に独立した認証・認可ドメインです。
Organization vs Tenant
┌─────────────────────────────────────────────────────────┐
│ Organization (企業A) │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ Organizer Tenant │ │ Public Tenant │ │
│ │ (組織管理用) │ │ (アプリ用) │ │
│ ├──────────────────┤ ├──────────────────┤ │
│ │ - 組織管理者 │ │ - Client 1 │ │
│ │ - テナント管理 │ │ - Client 2 │ │
│ │ │ │ - Users │ │
│ └──────────────────┘ └──────────────────┘ │
└─────────────────────────────────────────────────────────┘
データ分離の仕組み
各Tenantで完全に分離されるもの:
- ユーザーデータ: 認証情報、プロファイル
- クライアント設定: OAuth/OIDCクライアント
- 認証ポリシー: パスワードポリシー、MFA設定
- トークン設定: 有効期限、署名鍵
- セキュリティイベントログ: 監査ログ
テナント種別
| 種別 | 説明 | 作成方法 | 用途 |
|---|---|---|---|
ADMIN | システム管理用テナント | システム初期化時に自動作成 | システム管理・初期設定用 |
ORGANIZER | 組織管理用テナント | 組織作成(Onboarding API)時に自動作成 | 組織管理者の管理操作用 |
PUBLIC | アプリケーション用テナント | 組織レベルAPIで作成 | 通常のアプリケーション用 |
実装リファレンス:
ADMIN: IdpServerStarterContextCreator.java:78ORGANIZER: OnboardingContextCreator.java:82PUBLIC: TenantManagementRegistrationContextCreator.java:68
重要: 組織レベルAPI(POST /v1/management/organizations/{org-id}/tenants)で作成されるテナントは常にtype: "PUBLIC"です。ADMINとORGANIZERは手動で作成できません。
📖 API仕様リファレンス
テナント作成・更新のAPI詳細仕様(リクエスト/レスポンススキーマ、全パラメータ説明)は、OpenAPI仕様書を参照してください。
📖 OpenAPI仕様書:
- swagger-cp-tenant-ja.yaml - テナント管理API仕様
🎯 シナリオ別設定例
実際のユースケースに応じた認可サーバー設定例を紹介します。
| # | シナリオ | ユースケース | 主なポイント | 詳細 |
|---|---|---|---|---|
| 1 | Webアプリケーション(標準) | ユーザーがWebブラウザから安全にログインし、セッション中は再認証なしでサービスを利用する | • Opaque Token • Access Token: 30分 • Refresh Token: 1時間 | 詳細 |
| 2 | モバイルアプリ(PKCE) | モバイルユーザーが再ログインなしで長期間(30日)アプリを利用し続ける | • PKCE必須 • 長期Refresh Token(30日) • EXTENDS戦略 | 詳細 |
| 3 | 金融グレード(FAPI) | 銀行顧客が口座・取引情報に安全にアクセスし、厳格なセキュリティ基準を満たす | • Private Key JWT / mTLS • Pairwise Subject • Access Token: 10 分 | 詳細 |
| 4 | SaaS型マルチテナント | 企業ユーザーが所属組織・部署情報を含むトークンでSaaSサービスにアクセスする | • JWT Token • カスタムクレーム • M2M通信対応 | 詳細 |
📋 シナリオ詳細設定
1. Webアプリケーション向け(標準)
要件:
- Authorization Code Flow
- Refresh Token使用
- Access Token: 30分
- Refresh Token: 1時間
ユースケース: 一般的なWebアプリケーション、SPA
設定JSON例を表示
{
"tenant": {
"id": "web-app-tenant",
"name": "Web Application Tenant",
"domain": "https://app.example.com",
"authorization_provider": "idp-server"
},
"authorization_server": {
"issuer": "https://app.example.com/web-app-tenant",
"authorization_endpoint": "https://app.example.com/web-app-tenant/v1/authorizations",
"token_endpoint": "https://app.example.com/web-app-tenant/v1/tokens",
"userinfo_endpoint": "https://app.example.com/web-app-tenant/v1/userinfo",
"jwks_uri": "https://app.example.com/web-app-tenant/v1/jwks",
"scopes_supported": ["openid", "profile", "email"],
"grant_types_supported": ["authorization_code", "refresh_token"],
"response_types_supported": ["code"],
"token_endpoint_auth_methods_supported": ["client_secret_post", "client_secret_basic"],
"extension": {
"access_token_type": "opaque",
"access_token_duration": 1800,
"refresh_token_duration": 3600,
"rotate_refresh_token": true
}
}
}
設定ポイント:
access_token_type: "opaque": 高速な不透明トークンrotate_refresh_token: true: セキュリティ向上のためリフレッシュトークンをローテーション
2. モバイルアプリ向け(PKCE対応)
要件:
- Authorization Code Flow + PKCE
- 長期間のRefresh Token(30日)
- カスタムスコープ (プッシュ通知、オフラインアクセス)
ユースケース: iOS/Androidアプリ、ネイティブアプリ
設定JSON例を表示
{
"tenant": {
"id": "mobile-app-tenant",
"name": "Mobile Application Tenant",
"domain": "https://mobile.example.com",
"authorization_provider": "idp-server"
},
"authorization_server": {
"issuer": "https://mobile.example.com/mobile-app-tenant",
"authorization_endpoint": "https://mobile.example.com/mobile-app-tenant/v1/authorizations",
"token_endpoint": "https://mobile.example.com/mobile-app-tenant/v1/tokens",
"userinfo_endpoint": "https://mobile.example.com/mobile-app-tenant/v1/userinfo",
"jwks_uri": "https://mobile.example.com/mobile-app-tenant/v1/jwks",
"scopes_supported": [
"openid",
"profile",
"email",
"offline_access",
"notifications:push"
],
"grant_types_supported": ["authorization_code", "refresh_token"],
"response_types_supported": ["code"],
"token_endpoint_auth_methods_supported": ["none"],
"extension": {
"access_token_type": "opaque",
"access_token_duration": 3600,
"refresh_token_duration": 2592000,
"rotate_refresh_token": true,
"refresh_token_strategy": "EXTENDS"
}
}
}
設定ポイント:
token_endpoint_auth_methods_supported: ["none"]: PKCE専用(Client Secretなし)refresh_token_duration: 2592000: 30日間の長期トークンrefresh_token_strategy: "EXTENDS": リフレッシュの度にトークン有効期限を延長(ユーザー体験向上)
3. 金融グレード(FAPI準拠)
要件:
- FAPI 1.0 Advanced Profile準拠
- 強力なクライアント認証(Private Key JWT, mTLS)
- Pairwise Subject(プライバシー保護)
- カスタムスコープ(OpenBanking)
ユースケース: オンラインバンキング、金融API、機密データアクセス
設定JSON例を表示
{
"tenant": {
"id": "banking-tenant",
"name": "Online Banking Platform",
"domain": "https://banking.example.com",
"authorization_provider": "idp-server"
},
"session_config": {
"use_secure_cookie": true,
"cookie_same_site": "Strict",
"switch_policy": "STRICT"
},
"cors_config": {
"allow_origins": ["https://banking.example.com"]
},
"authorization_server": {
"issuer": "https://banking.example.com/banking-tenant",
"authorization_endpoint": "https://banking.example.com/banking-tenant/v1/authorizations",
"token_endpoint": "https://banking.example.com/banking-tenant/v1/tokens",
"userinfo_endpoint": "https://banking.example.com/banking-tenant/v1/userinfo",
"jwks_uri": "https://banking.example.com/banking-tenant/v1/jwks",
"scopes_supported": [
"openid",
"profile",
"email",
"openbanking:accounts",
"openbanking:transactions",
"openbanking:payments"
],
"grant_types_supported": ["authorization_code", "refresh_token"],
"response_types_supported": ["code"],
"response_modes_supported": ["query", "jwt"],
"token_endpoint_auth_methods_supported": [
"private_key_jwt",
"tls_client_auth"
],
"subject_types_supported": ["pairwise"],
"extension": {
"access_token_type": "jwt",
"access_token_duration": 600,
"refresh_token_duration": 3600,
"authorization_code_valid_duration": 300,
"fapi_baseline_scopes": ["openbanking:accounts", "openbanking:transactions"],
"fapi_advance_scopes": ["openbanking:payments"],
"id_token_strict_mode": true
}
}
}
設定ポイント:
token_endpoint_auth_methods_supported:private_key_jwt,tls_client_authのみ許可subject_types_supported: ["pairwise"]: ユーザー識別子を分離(プライバシー保護)access_token_type: "jwt": JWT形式で署名検証可能access_token_duration: 600: 10分の短い有効期限(セキュリティ向上)fapi_baseline_scopes/fapi_advance_scopes: FAPI検証スコープswitch_policy: "STRICT": 別ユーザー認証を拒否(ログアウト必須)
FAPI準拠の利点:
- 金融機関レベルのセキュリティ
- 国際標準への準拠
- 監査対応の容易さ
4. SaaS型マルチテナント
要件:
- 複数企業の従業員が利用
- カスタムクレーム(企業ID、部署、権限)
- JWT形式のAccess Token
- M2M通信(Client Credentials Grant)
ユースケース: B2B SaaS、企業向けプラットフォーム
設定JSON例を表示
{
"tenant": {
"id": "saas-tenant",
"name": "SaaS Platform Tenant",
"domain": "https://saas.example.com",
"authorization_provider": "idp-server"
},
"authorization_server": {
"issuer": "https://saas.example.com/saas-tenant",
"authorization_endpoint": "https://saas.example.com/saas-tenant/v1/authorizations",
"token_endpoint": "https://saas.example.com/saas-tenant/v1/tokens",
"userinfo_endpoint": "https://saas.example.com/saas-tenant/v1/userinfo",
"jwks_uri": "https://saas.example.com/saas-tenant/v1/jwks",
"scopes_supported": [
"openid",
"profile",
"email",
"claims:organization_id",
"claims:department",
"claims:role"
],
"grant_types_supported": ["authorization_code", "refresh_token", "client_credentials"],
"response_types_supported": ["code"],
"extension": {
"access_token_type": "jwt",
"access_token_duration": 3600,
"custom_claims_scope_mapping": true
}
}
}
設定ポイント:
custom_claims_scope_mapping: true: カスタムクレームをスコープでマッピングclaims:organization_id,claims:department: 企業・部署情報をトークンに含めるgrant_types_supported:client_credentialsを追加(M2M通信)access_token_type: "jwt": クレーム情報をトークン内に含める
⚙️ 高度な設定
Extension設定の詳細
extensionオブジェクトには、idp-server固有の拡張設定を含めます。
トークン設定
| フィールド | デフォルト値 | 説明 | 推奨値 |
|---|---|---|---|
access_token_type | opaque | トークン形式(opaque / jwt) | Web: opaque, SaaS: jwt |
access_token_duration | 1800秒 (30分) | アクセストークン有効期限 | 30分~1時間 |
id_token_duration | 3600秒 (60分) | IDトークン有効期限 | 1時間 |
refresh_token_duration | 3600秒 (60分) | リフレッシュトークン有効期限 | Web: 1時間、Mobile: 30日 |
rotate_refresh_token | true | リフレッシュトークンローテーション | true 推奨 |
refresh_token_strategy | FIXED | 期限戦略(FIXED / EXTENDS) | Mobile: EXTENDS |
認可フロー設定
| フィールド | デフォルト値 | 説明 | 推奨値 |
|---|---|---|---|
authorization_code_valid_duration | 600秒 (10分) | 認可コード有効期限 | 5~10分(RFC 6749推奨) |
oauth_authorization_request_expires_in | 1800秒 (30分) | 認可リクエスト有効期限 | 30分 |
authorization_response_duration | 60秒 (1分) | 認可レスポンス有効期限 | 1分 |
default_max_age | 86400秒 (24時間) | デフォルト最大認証有効期間 | 24時間 |
セキュリティ設定
| フィールド | デフォルト値 | 説明 |
|---|---|---|
id_token_strict_mode | false | IDトークン厳密モード(OIDC仕様準拠、詳細は下記参照) |
redirect_uri_exact_match_required | false | redirect_uri を完全一致で照合する(RFC 9700 §2.1、詳細は下記参照) |
dpop_proof_acceptable_window_seconds | 60 | DPoP proof の iat と現在時刻の差の許容範囲(秒、前後とも。1〜600、範囲外は 400)。受け付けた proof の jti はこの窓が閉じるまで記録し、2 回目は拒否する(RFC 9449 §11.1) |
client_attestation_pop_acceptable_window_seconds | 60 | Client Attestation PoP JWT の iat の許容範囲(秒、前後とも。1〜600、範囲外は 400)。jti の記録は DPoP と同じ |
client_assertion_max_lifetime_seconds | 60 | client assertion(private_key_jwt / client_secret_jwt)を受け入れる最長の期間(秒。1〜600、範囲外は 400)。iat があれば iat からこの秒数まで、無ければ exp が現在からこの秒数以内のものだけを受け入れる(RFC 7523 §3 要件 4)。受け付けた assertion の jti は exp とこの期限の早いほうまで記録し、2 回目は拒否する(RFC 7523 §3 要件 7) |
id_token_strict_mode - IDトークンクレーム制御
目的: IDトークンに含めるクレームの判定ロジックをOIDC仕様に厳密準拠させます。
デフォルト値: false
動作の違い:
| モード | scope=profileのみ指定 | claimsパラメータで明示的要求 | 用途 |
|---|---|---|---|
false(デフォルト) | name, given_name等を全て含める | 明示的に要求されたクレームのみ | 後方互換性・利便性優先 |
true(厳密モード) | クレームを含めない | 明示的に要求されたクレームのみ | OIDC仕様準拠・FAPI準拠 |
OIDC仕様の解釈:
- OpenID Connect Core 1.0 Section 5.4: "
profilescopeはUserInfoエンドポイントでクレームへのアクセスを要求する" - IDトークンへの包含は、
claimsパラメータでの明示的要求が推奨される
実装における挙動:
// GrantIdTokenClaims.java:218-221
if (idTokenStrictMode) {
return idTokenClaims.hasName(); // claimsパラメータでの明示的要求のみ
}
return scopes.contains("profile"); // scopeだけで含める(非厳密モード)
使用例:
非厳密モード(id_token_strict_mode: false、デフォルト):
GET /authorize?scope=openid profile&...
→ IDトークンに name, given_name, family_name 等が含まれる
厳密モード(id_token_strict_mode: true):
GET /authorize?scope=openid profile&claims={"id_token":{"name":null}}&...
→ nameのみIDトークンに含まれる(claimsで明示的要求)
推奨設定:
- 一般的なアプリケーション:
false(利便性優先) - 金融グレード(FAPI):
true(仕様準拠・最小限のデータ公開) - OIDC4IDA:
true(検証済みクレームの厳密制御)
判断基準:
| 設定値 | 選択条件 | 理由 |
|---|---|---|
true | FAPI/OIDC仕様への厳密準拠が必要 | FAPI、OIDC4IDAではIDトークンのクレームを明示的に要求することが求められる |
true | クライアントがclaimsパラメータに対応済み | OIDC仕様に準拠した実装が可能 |
false | クライアントがclaimsパラメータに対応困難 | レガシーシステム、既存実装の改修コストが高い |
false | 開発・テスト環境での利便性を優先 | クレーム取得を簡素化して開発効率を向上 |
移行戦略:
新規プロジ ェクトの場合:
- 最初から
trueで設計: 将来的な規制対応を見据える - クライアント実装時に
claimsパラメータを考慮
既存プロジェクトの場合:
- 段階的移行:
- Phase 1:
falseのまま、クライアントにclaimsパラメータ実装 - Phase 2: 検証環境で
trueに変更してテスト - Phase 3: 本番環境で
trueに変更
- Phase 1:
- 互換性確認: UserInfoエンドポイントで同じクレームが取得できることを確認
関連設定:
custom_claims_scope_mapping: カスタムクレームのclaims:スコープマッピングclaims_supported: サポートするクレームの宣言(Discovery)
実装リファレンス:
redirect_uri_exact_match_required - redirect_uri の照合方法
目的: redirect_uri の照合を RFC 9700 §2.1 の exact string matching に揃えます。
デフォルト値: false
仕様の位置づけ: RFC 6749 §3.1.2.3 は登録済み URI との比較を「RFC 3986 §6 で定義された方法」と規定しており、構文ベース正規化(§6.2.2)も許容範囲でした。RFC 9700(BCP 240、2025年1月、RFC 6749 を更新)はこれを完全一致のみに狭めています。理由は §4.1.3 に「パターンマッチングを正しく実装・運用することの複雑さが、そのままセキュリティ問題を招く」と述べられています。
プロファイルごとの現状:
| プロファイル | 照合 |
|---|---|
| OAuth 2.0 | 完全一致 または構文正規化一致(このフラグの対象) |
| OIDC | 完全一致のみ |
| FAPI Baseline / Advance | 完全一致のみ |
OAuth 2.0 プロファイルは要求スコープで決まる(openid を含まない)ため、同じクライアント・同じ登録 URI でも openid を外すと緩い照合になります。このフラグを true にすると全プロファイルで完全一致に揃います。
false のとき通ってしまう差:
| 差 | 例 |
|---|---|
| scheme / host の大文字小文字 | HTTPS://APP.EXAMPLE.COM/cb |
| 既定ポートの明示 | https://app.example.com:443/cb |
| クエリの付加 | https://app.example.com/cb?foo=bar |
前2つは RFC 3986 §6.2.2 / §6.2.3 の正規化として妥当で、宛先ホストは変わりません。3つ目は正規化ではなく、正規化段がクエリを比較していないために通っているものです。いずれも宛先は変わらないため 、オープンリダイレクトや認可コード漏洩には直結しません。
native app の loopback は例外のまま: RFC 8252 §7.3 のポート無視は、このフラグが true でも維持されます(RFC 9700 §2.1 自身が唯一の例外として認めているため)。
照合失敗は RFC 6749 §3.1.2.4 に従い、クライアントの redirect_uri へは返りません。テナントのエラー画面(ui_config.base_url + /error/)へ 302 するため、エンドユーザーが行き止まりになります。成功時も 302 なので、ステータスコードだけを見ていると気づけません。
特に redirect_uri にクエリを付けて送っている実装は現状通っているため、確認せずに有効化すると停止します。検証環境で有効化して Location の行き先を確認してから本番に適用してください。
推奨設定:
- 新規テナント:
true(後段仕様に準拠) - 既存テナント: 検証環境で確認後に
true
実装リファレンス:
FAPI設定
| フィールド | デフォルト値 | 説明 |
|---|---|---|
fapi_baseline_scopes | [] | FAPI Baseline検証スコープ |
fapi_advance_scopes | [] | FAPI Advanced検証スコープ |
実装リファレンス: AuthorizationServerExtensionConfiguration.java
カスタムスコープ
標準スコープ(openid, profile, email)に加えて、独自のスコープを定義できます。
定義方法
scopes_supportedにカスタムスコープを追加:
{
"scopes_supported": [
"openid",
"profile",
"email",
"identity_verification_application",
"notifications:push",
"api:read",
"api:write"
]
}
スコープ命名規則
| パターン | 例 | 用途 |
|---|---|---|
domain:action | api:read, notifications:push | 機能別アクセス制御 |
claims:field | claims:vip_status | カスタムクレームアクセス |
| 単独名 | offline_access | 標準的な追加スコープ |
Claims(クレーム)設定
OpenID Connectでは、**クレーム(claim)**とはユーザーに関する情報項目(名前、メール、電話番号等)を指します。
claims_supported - サポートするクレームの宣言
目的: 認可サーバーが返却可能なクレーム(ユーザー情報項目)を宣言します。
OpenID Connect Discovery仕様: OpenID Connect Discovery 1.0 Section 3
設定例:
{
"claims_supported": [
"sub",
"name",
"email",
"email_verified",
"preferred_username",
"given_name",
"family_name",
"picture",
"phone_number",
"phone_number_verified"
]
}
標準クレーム(OIDC Core仕様):
| クレーム | 説明 | 例 |
|---|---|---|
sub | Subject(ユーザー識別子) | 248289761001 |
name | フルネーム | Jane Doe |
given_name | 名 | Jane |
family_name | 姓 | Doe |
email | メールアドレス | janedoe@example.com |
email_verified | メール検証済みフラグ | true |
preferred_username | 優先ユーザー名 | jane.doe |
phone_number | 電話番号 | +1 (555) 123-4567 |
phone_number_verified | 電話番号検証済みフラグ | true |
picture | プロフィール画像URL | https://example.com/jane.jpg |
profile | プロフィールページURL | https://example.com/jane |
website | ウェブサイトURL | https://janedoe.com |
gender | 性別 | female |
birthdate | 生年月日 | 1990-01-01 |
zoneinfo | タイムゾーン | Asia/Tokyo |
locale | ロケール | ja-JP |
address | 住所(JSON構造) | {"formatted": "..."} |
参照: OpenID Connect Core 1.0 Section 5.1 - Standard Claims
実装における重要な注意点:
claims_supportedは宣言のみであり、実際に返却されるかはUserInfoエンドポイントやIDトークンの実装に依存します- クライアントは
scope