外部パスワード認証委譲
パスワード認証を外部サービス(HTTP API)に委譲するユースケース。
idp-server 内蔵のパスワード検証を使わず、authentication-configurations の execution.function = "http_request" を使って外部 API にユーザー名/パスワードを転送し、レスポンスからユーザー情報をマッピングする。
ユースケース: 既存の認証基盤(社内認証 API 等)を持つ組織が、idp-server を OIDC レイヤーとして導入するケース。idp-server は HTTP API で外部サービスと連携するため、外部サービスが HTTP エンドポイントを提供している必要がある。
テンプレート実行
テンプレート: config/templates/use-cases/external-password-auth/
bash
1# 基本実行
2bash config/templates/use-cases/external-password-auth/setup.sh
3
4# カスタマイズ例
5ORGANIZATION_NAME="my-company" \
6EXTERNAL_AUTH_URL="https://auth.internal.example.com/api/authenticate" \
7EXTERNAL_PROVIDER_ID="ldap-wrapper" \
8SESSION_TIMEOUT_SECONDS=3600 \
9ACCESS_TOKEN_DURATION=1800 \
10REFRESH_TOKEN_DURATION=604800 \
11bash config/templates/use-cases/external-password-auth/setup.sh
12
13# ドライラン(実際には作成しない)
14bash config/templates/use-cases/external-password-auth/setup.sh --dry-run
細かい設定 Q&A(逆引き)
「やりたいこと -> 設定」の対応表: qa.md
ユーザーが具体的にやりたいことを言った場合は、qa.md を参照して該当するQ&Aの設定キー+値を提示すること。
設定変更 x 挙動確認(ハンズオン)
「設定を変えて → 挙動が変わることを体験する」実験ガイド:
config/templates/use-cases/external-password-auth/EXPERIMENTS.md - http_request(単数)の基本実験
config/templates/use-cases/external-password-auth/EXPERIMENTS-http-requests.md - http_requests(複数形)の複数API チェーン実験
設定の効果を手元で確認したい場合はこれらのガイドを案内すること。
自動検証スクリプト: config/templates/use-cases/external-password-auth/verify.sh
ヒアリング項目
| # | 決めること | 選択肢 | 影響する設定 |
|---|
| 1 | 外部認証サービスURL | 実サービスURL / モック(localhost:4001) | 認証メソッド設定 http_request.url |
| 2 | 外部プロバイダーID | 任意の識別子(例: ldap-wrapper, legacy-auth) | 認証メソッド設定 user_mapping_rules[].static_value |
| 3 | アカウントロック条件 | 失敗回数(デフォルト: 5回) | 認証ポリシー failure_conditions / lock_conditions |
| 4 | セッション有効期限 | 秒数(デフォルト: 86400 = 24時間) | テナント session_config.timeout_seconds |
| 5 | トークン有効期限(AT) | 秒数(デフォルト: 3600 = 1時間) | 認可サーバー extension.access_token_duration |
| 6 | トークン有効期限(IDT) | 秒数(デフォルト: 3600 = 1時間) | 認可サーバー extension.id_token_duration |
| 7 | トークン有効期限(RT) | 秒数(デフォルト: 86400 = 24時間) | 認可サーバー extension.refresh_token_duration |
ヒアリング結果 -> 環境変数マッピング
外部認証サービス
| ヒアリング項目 | 環境変数 | デフォルト値 |
|---|
| 外部認証サービスURL | EXTERNAL_AUTH_URL | http://host.docker.internal:4001/auth/password |
| 外部プロバイダー識別子 | EXTERNAL_PROVIDER_ID | external-auth |
セッション設定
| ヒアリング項目 | 環境変数 | デフォルト値 |
|---|
| セッション有効期限(秒) | SESSION_TIMEOUT_SECONDS | 86400 |
トークン有効期限
| ヒアリング項目 | 環境変数 | デフォルト値 |
|---|
| AT有効期限(秒) | ACCESS_TOKEN_DURATION | 3600 |
| IDT有効期限(秒) | ID_TOKEN_DURATION | 3600 |
| RT有効期限(秒) | REFRESH_TOKEN_DURATION | 86400 |
設定対象と手順
1. 外部パスワード認証設定(認証メソッド設定) -- 核心
API: POST /v1/management/organizations/{org-id}/tenants/{tenant-id}/authentication-configurations
この設定が外部パスワード認証委譲の核心。execution.function = "http_request" で外部サービスにリクエストを転送し、user_mapping_rules でレスポンスをユーザー属性にマッピングする。
json
1{
2 "type": "password",
3 "metadata": {
4 "description": "External password authentication via HTTP request"
5 },
6 "interactions": {
7 "password-authentication": {
8 "execution": {
9 "function": "http_request",
10 "http_request": {
11 "url": "https://auth.example.com/api/authenticate",
12 "method": "POST",
13 "header_mapping_rules": [
14 { "static_value": "application/json", "to": "Content-Type" }
15 ],
16 "body_mapping_rules": [
17 { "from": "$.request_body.username", "to": "username" },
18 { "from": "$.request_body.password", "to": "password" }
19 ]
20 }
21 },
22 "user_resolve": {
23 "user_mapping_rules": [
24 { "from": "$.execution_http_request.response_body.user_id", "to": "external_user_id" },
25 { "from": "$.execution_http_request.response_body.email", "to": "email" },
26 { "from": "$.execution_http_request.response_body.name", "to": "name" },
27 { "static_value": "external-auth", "to": "provider_id" }
28 ]
29 },
30 "response": {
31 "body_mapping_rules": [
32 {
33 "from": "$.execution_http_request.response_body.user_id",
34 "to": "user_id",
35 "condition": { "operation": "exists", "path": "$.execution_http_request.response_body.user_id" }
36 },
37 {
38 "from": "$.execution_http_request.response_body.email",
39 "to": "email",
40 "condition": { "operation": "exists", "path": "$.execution_http_request.response_body.email" }
41 },
42 {
43 "from": "$.execution_http_request.response_body.error",
44 "to": "error",
45 "condition": { "operation": "exists", "path": "$.execution_http_request.response_body.error" }
46 },
47 {
48 "from": "$.execution_http_request.response_body.error_description",
49 "to": "error_description",
50 "condition": { "operation": "exists", "path": "$.execution_http_request.response_body.error_description" }
51 }
52 ]
53 }
54 }
55 }
56}
外部 API の契約:
| 項目 | 仕様 |
|---|
| リクエスト | POST {"username": "...", "password": "..."} |
| 成功 (HTTP 200) | {"user_id": "...", "email": "...", "name": "..."} |
| 失敗 (HTTP 401) | {"error": "invalid_credentials", "error_description": "..."} |
マッピングルール:
| 外部レスポンス | idp-server 属性 | 説明 |
|---|
user_id | external_user_id | 外部サービスのユーザーID |
email | email | メールアドレス |
name | name | 表示名 |
| (static) | provider_id | 外部サービス識別子 |
1b. 複数API チェーン設定(http_requests)
複数の外部APIを順番に呼び出し、結果を統合するパターン。例: 認証API → ユーザー詳細API。
| http_request(単数) | http_requests(複数形) |
|---|
| リクエスト数 | 1つ | 複数(順番に実行) |
| 設定キー | execution.http_request (object) | execution.http_requests (array) |
| 結果パス | $.execution_http_request.response_body.* | $.execution_http_requests[0].response_body.* |
| チェーン | 不可 | 前のレスポンスを次のリクエストで使える |
| エラー時 | 即座に返却 | 失敗した時点で中断、それまでの結果を保持 |
複数API呼び出し時のエラーハンドリング:
成功フィールドとエラーフィールドが共存しないよう、allOf + missing 複合条件を使う:
json
1{
2 "from": "$.execution_http_requests[0].response_body.user_id",
3 "to": "user_id",
4 "condition": {
5 "operation": "allOf",
6 "value": [
7 { "operation": "exists", "path": "$.execution_http_requests[0].response_body.user_id" },
8 { "operation": "missing", "path": "$.execution_http_requests[1].response_body.error" }
9 ]
10 }
11}
exists のみだと、API 2 が失敗してもAPI 1 の成功フィールドが返ってしまう
allOf で「自身が存在 AND 後続APIのエラーが missing」を両方満たす場合のみ出力
functions(ヘッダー動的生成):
json
1"header_mapping_rules": [
2 {
3 "from": "$.unused",
4 "to": "x-request-id",
5 "functions": [
6 { "name": "random_string", "args": { "length": 8 } },
7 { "name": "format", "args": { "template": "trace-{{value}}" } }
8 ]
9 }
10]
使える functions: format(テンプレート変換)、random_string(ランダム文字列)、now(現在時刻)、exists(boolean化)。from: "$.unused" は入力不要な functions 用の特殊パス。
詳細と実験手順は EXPERIMENTS-http-requests.md を参照。
2. 認証ポリシー(パスワードのみ、failure/lock_conditions あり)
API: POST /v1/management/organizations/{org-id}/tenants/{tenant-id}/authentication-policies
login-password-only との主な差分:
available_methods は ["password"] のみ(initial-registration なし = ユーザー自己登録なし)
failure_conditions / lock_conditions を設定(5回失敗でアカウントロック)
json
1{
2 "flow": "oauth",
3 "enabled": true,
4 "policies": [
5 {
6 "description": "external_password_only",
7 "priority": 1,
8 "conditions": {},
9 "available_methods": ["password"],
10 "success_conditions": {
11 "any_of": [
12 [
13 {
14 "path": "$.password-authentication.success_count",
15 "type": "integer",
16 "operation": "gte",
17 "value": 1
18 }
19 ]
20 ]
21 },
22 "failure_conditions": {
23 "any_of": [
24 [
25 {
26 "path": "$.password-authentication.failure_count",
27 "type": "integer",
28 "operation": "gte",
29 "value": 5
30 }
31 ]
32 ]
33 },
34 "lock_conditions": {
35 "any_of": [
36 [
37 {
38 "path": "$.password-authentication.failure_count",
39 "type": "integer",
40 "operation": "gte",
41 "value": 5
42 }
43 ]
44 ]
45 }
46 }
47 ]
48}
3. クレーム設定(claims_supported)
API: PUT /v1/management/organizations/{org-id}/tenants/{tenant-id}/authorization-server
重要: この設定が無いと UserInfo / ID Token が sub のみしか返さない。
テンプレートでは public-tenant-template.json に以下が含まれている:
json
1{
2 "claims_supported": [
3 "sub", "iss", "auth_time", "acr",
4 "name", "given_name", "family_name", "nickname", "preferred_username", "middle_name",
5 "profile", "picture", "website",
6 "email", "email_verified",
7 "gender", "birthdate", "zoneinfo", "locale", "updated_at",
8 "address", "phone_number", "phone_number_verified"
9 ],
10 "claims_parameter_supported": true
11}
login-password-only との差分
| 項目 | login-password-only | external-password-auth |
|---|
| パスワード検証 | idp-server 内蔵 | 外部サービス(http_request) |
| パスワードポリシー | テナントに設定 | なし(外部サービスが管理) |
| ユーザー登録 | initial-registration | なし(外部サービスが管理) |
| ユーザー識別 | EMAIL | EMAIL_OR_EXTERNAL_USER_ID |
| 認証ポリシー | password + initial-registration | password のみ |
| ブルートフォース防止 | なし | failure_conditions + lock_conditions |
設定確認チェックリスト
| # | 確認観点 | 設定箇所 | よくあるミス |
|---|
| 1 | claims_supported が設定済み | 認可サーバー | 未設定で UserInfo/ID Token が sub のみ返却 |
| 2 | EXTERNAL_AUTH_URL が idp-server コンテナから到達可能 | 認証メソッド設定 | localhost を指定(Docker 内から到達不可)→ host.docker.internal を使う |
| 3 | EXTERNAL_PROVIDER_ID が一意 | 認証メソッド設定 | 複数テナントで同じ provider_id を使うと衝突の可能性 |
| 4 | 外部サービスのレスポンスに user_id フィールドがある | 外部サービス | フィールド名の不一致で external_user_id が null になる |
| 5 | identity_unique_key_type が EMAIL_OR_EXTERNAL_USER_ID | テナント identity_policy_config | EMAIL のままだと外部ユーザーIDでの識別が機能しない |
| 6 | failure_conditions / lock_conditions が設定済み | 認証ポリシー | 未設定だと認証失敗でアカウントロックされない |
| 7 | ui_config.base_url が認証UIのオリジン | テナント ui_config | APIサーバーURLを設定してしまう |
| 8 | パスワードポリシーが未設定であること | テナント identity_policy_config | 外部サービス管理なのに idp-server 側にもポリシーを設定してしまう |
設定例ファイル参照
- テンプレート:
config/templates/use-cases/external-password-auth/
- 認証メソッド設定:
config/templates/use-cases/external-password-auth/authentication-config-password-template.json
- 認証ポリシー:
config/templates/use-cases/external-password-auth/authentication-policy.json
- テナント設定:
config/templates/use-cases/external-password-auth/public-tenant-template.json
- モックサーバー:
config/templates/use-cases/external-password-auth/mock-server.js
関連ドキュメント
config/templates/use-cases/external-password-auth/README.md - テンプレートの詳細説明
documentation/docs/content_05_how-to/phase-1-foundation/07-authentication-policy.md - 認証ポリシー設定
- スキル
spec-external-integration - 外部サービス連携の詳細(HTTP Request Executor, MappingRule)
- スキル
use-case-login - login-password-only との共通設定(セッション、トークン等)
$ARGUMENTS