WordPress-RAG SSO連携 要件定義書
| 項目 |
内容 |
| 作成日 |
2024年12月24日 |
| バージョン |
1.0 |
| ステータス |
承認済み |
1. 概要
1.1 目的
WordPress PWAアプリケーションにログイン済みのユーザーが、別ドメインで稼働するRAGシステム(KnowledgeYard)に自動的にログインできるSSO(シングルサインオン)連携を実現する。
1.2 スコープ
| 機能 |
優先度 |
担当 |
| SSO連携(トークン発行・受け取り) |
P1 |
WordPress + RAG |
| ユーザー一括インポート |
P1 |
RAG |
| 部署ベース権限制御 |
P2 |
RAG |
| 文書権限管理UI |
P2 |
RAG |
1.3 システム構成
┌─────────────────────────────────┐ ┌─────────────────────────────────┐
│ WordPress PWA │ │ RAGシステム (KnowledgeYard) │
│ (別ドメイン) │ │ (別ドメイン) │
├─────────────────────────────────┤ ├─────────────────────────────────┤
│ │ │ │
│ ・社員番号でログイン │ │ ・Azure Functions API │
│ ・1600名(関連会社含む) │ │ ・Azure AI Search │
│ ・本社280名がRAG利用対象 │ │ ・Azure Table Storage │
│ ・部署情報を管理・調整 │ │ ・React フロントエンド │
│ │ │ │
└─────────────────────────────────┘ └─────────────────────────────────┘
│ │
│ SSO連携フロー │
│◀─────────────────────────────────────▶│
│ │
2. SSO連携仕様
2.1 認証フロー
[WordPress] [RAGシステム]
│ │
│ ① ユーザーがWordPressにログイン済み │
│ │
│ ② 「RAG検索」ボタンをクリック │
│ ↓ │
│ ③ PHP: 署名付きリクエストを生成 │
│ - user_id (社員番号) │
│ - timestamp (UNIX時間) │
│ - signature (HMAC-SHA256) │
│ ↓ │
│ ④ POST /api/auth/sso-token ──────────→ │
│ │ ⑤ 署名を検証
│ │ ⑥ タイムスタンプ有効期限チェック(5分)
│ │ ⑦ ユーザー存在確認
│ │ - 存在しない → エラー返却
│ │ - 存在する → JWTトークン生成
│ ⑧ JWTトークン受信 ←─────────────────── │
│ ↓ │
│ ⑨ 新規タブでRAGを開く │
│ URL: https://rag.example.com#sso_token=xxx
│ ※ハッシュ(#)を使用(セキュリティ向上)
│ │ ⑩ ハッシュからトークン取得
│ │ ⑪ トークンをlocalStorageに保存
│ │ ⑫ URLからハッシュを削除
│ │ ⑬ 認証済み状態でアプリ表示
2.2 トークン渡し方式(ハッシュ方式)
URLパラメータではなくハッシュ(#fragment)を使用する理由:
| 項目 |
URLパラメータ ?token=xxx |
ハッシュ #token=xxx |
| サーバーログ |
⚠️ 残る |
✅ 残らない |
| Refererヘッダー |
⚠️ 漏れる可能性 |
✅ 漏れない |
| 実装難易度 |
同等 |
同等 |
2.3 共有シークレット
| 項目 |
値 |
| 名称 |
SSO_SHARED_SECRET |
| 形式 |
64文字以上のランダム文字列 |
| 設定場所(WordPress) |
wp-config.php または環境変数 |
| 設定場所(RAG) |
Azure Functions アプリケーション設定 |
重要: 本番環境では安全な方法でシークレットを共有してください。
2.3 署名生成アルゴリズム
signature = HMAC-SHA256(
key: SSO_SHARED_SECRET,
message: "{user_id}:{timestamp}"
)
user_id: 社員番号(文字列)
timestamp: UNIX時間(秒)
- 署名は16進数文字列(小文字)で送信
3. API仕様
3.1 SSOトークン発行 API
【RAG側で実装】
| 項目 |
内容 |
| エンドポイント |
POST /api/auth/sso-token |
| 認証 |
不要(署名で認証) |
リクエスト
{
"user_id": "12345",
"timestamp": 1703404800,
"signature": "a1b2c3d4e5f6..."
}
| フィールド |
型 |
必須 |
説明 |
| user_id |
string |
✅ |
社員番号 |
| timestamp |
integer |
✅ |
UNIX時間(秒) |
| signature |
string |
✅ |
HMAC-SHA256署名(16進数) |
レスポンス(成功: 200)
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"user": {
"user_id": "12345",
"display_name": "山田太郎",
"role": "user",
"department": "総務部",
"email": "yamada@example.com"
},
"expires_in": 10800
}
有効期限について:
- デフォルト: 3時間(10800秒)
- 環境変数 JWT_EXPIRES_HOURS で変更可能
- 期限切れ時はWordPressから再度RAGを開けば自動的に新しいトークンが発行される
レスポンス(エラー)
| HTTPステータス |
エラーコード |
説明 |
| 400 |
INVALID_REQUEST |
リクエスト形式が不正 |
| 401 |
INVALID_SIGNATURE |
署名が一致しない |
| 401 |
EXPIRED_TIMESTAMP |
タイムスタンプが期限切れ(5分超過) |
| 404 |
USER_NOT_FOUND |
ユーザーが登録されていない |
{
"error": {
"code": "USER_NOT_FOUND",
"message": "指定されたユーザーは登録されていません"
}
}
3.2 ユーザー一括インポート API
【RAG側で実装】
| 項目 |
内容 |
| エンドポイント |
POST /api/manage/users/bulk |
| 認証 |
Bearer Token(adminロール必須) |
リクエスト
{
"users": [
{
"user_id": "12345",
"display_name": "山田太郎",
"department": "総務部",
"email": "yamada@example.com",
"role": "user"
},
{
"user_id": "12346",
"display_name": "鈴木花子",
"department": "人事部",
"email": "suzuki@example.com",
"role": "user"
}
],
"update_existing": true
}
| フィールド |
型 |
必須 |
説明 |
| users |
array |
✅ |
ユーザー情報の配列(最大100件) |
| users[].user_id |
string |
✅ |
社員番号 |
| users[].display_name |
string |
✅ |
表示名(氏名) |
| users[].department |
string |
❌ |
部署名 |
| users[].email |
string |
❌ |
メールアドレス |
| users[].role |
string |
❌ |
ロール(デフォルト: "user") |
| users[].password |
string |
❌ |
パスワード(省略時: user_idと同じ) |
| update_existing |
boolean |
❌ |
true: 既存ユーザーを更新(デフォルト: false) |
レスポンス(成功: 200 または 207)
{
"created": 45,
"updated": 5,
"skipped": 0,
"errors": [
{
"index": 50,
"user_id": "99999",
"error": "display_name is required"
}
],
"total_requested": 51
}
4. WordPress側 実装要件
4.1 SSO連携機能
4.1.1 設定ファイル
// wp-config.php に追加
define('RAG_SSO_SECRET', 'your-64-character-secret-key-here...');
define('RAG_API_URL', 'https://rag.example.com/api');
define('RAG_APP_URL', 'https://rag.example.com');
4.1.2 SSOトークン取得関数
<?php
/**
* RAGシステム用のSSOトークンを取得する
*
* @param string $user_id 社員番号
* @return array|WP_Error トークン情報またはエラー
*/
function get_rag_sso_token($user_id) {
$timestamp = time();
$message = $user_id . ':' . $timestamp;
$signature = hash_hmac('sha256', $message, RAG_SSO_SECRET);
$response = wp_remote_post(RAG_API_URL . '/auth/sso-token', [
'headers' => [
'Content-Type' => 'application/json',
],
'body' => json_encode([
'user_id' => $user_id,
'timestamp' => $timestamp,
'signature' => $signature,
]),
'timeout' => 30,
]);
if (is_wp_error($response)) {
return $response;
}
$status_code = wp_remote_retrieve_response_code($response);
$body = json_decode(wp_remote_retrieve_body($response), true);
if ($status_code !== 200) {
return new WP_Error(
$body['error']['code'] ?? 'UNKNOWN_ERROR',
$body['error']['message'] ?? 'RAGシステムへの接続に失敗しました'
);
}
return $body;
}
4.1.3 RAG起動ボタン/リンクの実装
<?php
/**
* RAGシステムを開くURLを生成する
* ログインユーザーの社員番号を使用
*/
function get_rag_launch_url() {
if (!is_user_logged_in()) {
return null;
}
$current_user = wp_get_current_user();
$user_id = $current_user->user_login; // 社員番号
$result = get_rag_sso_token($user_id);
if (is_wp_error($result)) {
// エラーログ記録
error_log('RAG SSO Error: ' . $result->get_error_message());
return null;
}
$token = $result['token'];
return RAG_APP_URL . '?sso_token=' . urlencode($token);
}
/**
* RAG起動ボタンのショートコード
* 使用例: [rag_button text="RAG検索を開く"]
*/
function rag_button_shortcode($atts) {
$atts = shortcode_atts([
'text' => 'RAG検索',
'class' => 'rag-launch-button',
], $atts);
$url = get_rag_launch_url();
if (!$url) {
return '<span class="rag-error">RAGシステムを利用できません</span>';
}
return sprintf(
'<a href="%s" target="_blank" class="%s" rel="noopener noreferrer">%s</a>',
esc_url($url),
esc_attr($atts['class']),
esc_html($atts['text'])
);
}
add_shortcode('rag_button', 'rag_button_shortcode');
4.1.4 JavaScript版(AJAX対応)
/**
* RAGシステムを新規タブで開く
* トークンを取得してからURLにパラメータ付きで開く
*/
async function openRAGSystem() {
try {
const response = await fetch('/wp-json/rag/v1/get-token', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': wpApiSettings.nonce,
},
});
if (!response.ok) {
const error = await response.json();
alert(error.message || 'RAGシステムへの接続に失敗しました');
return;
}
const data = await response.json();
const ragUrl = `${RAG_APP_URL}?sso_token=${encodeURIComponent(data.token)}`;
window.open(ragUrl, '_blank', 'noopener,noreferrer');
} catch (error) {
console.error('RAG SSO Error:', error);
alert('RAGシステムへの接続に失敗しました');
}
}
4.2 ユーザーCSVエクスポート
月次インポート用に、本社280名のユーザー情報をCSV出力する機能が必要です。
4.2.1 CSVフォーマット
社員番号,氏名,部署,メールアドレス
12345,山田太郎,総務部,yamada@example.com
12346,鈴木花子,人事部,suzuki@example.com
...
| カラム |
必須 |
説明 |
| 社員番号 |
✅ |
ユーザーID兼パスワード |
| 氏名 |
✅ |
表示名 |
| 部署 |
❌ |
権限制御に使用 |
| メールアドレス |
❌ |
任意 |
4.2.2 エクスポート対象
- 本社社員のみ(関連会社1600名のうち280名程度)
- WordPressで調整済みの部署情報を使用
4.3 ユーザー同期機能(ハイブリッド方式)
WordPressをマスタデータとし、RAG側にユーザー情報を同期する機能。
4.3.1 同期方式
| 方式 |
用途 |
トリガー |
| 一括同期 |
月次メンテナンス |
管理者が「全員を同期」ボタン押下 |
| 個別同期 |
新入社員、異動対応 |
管理者が社員番号を指定して同期 |
| 自動同期(将来) |
完全自動化 |
ユーザー情報更新時にフック |
4.3.2 管理画面UI
┌─────────────────────────────────────────────────────────┐
│ RAGユーザー同期 │
├─────────────────────────────────────────────────────────┤
│ 📊 同期状況 │
│ 最終同期: 2024-12-24 10:30 │
│ RAG登録済み: 278名 / 対象: 280名 │
│ │
│ [🔄 全員を同期] [📁 CSVプレビュー] │
│ │
│ ── 個別同期 ── │
│ 社員番号: [ ] [🔄 この社員を同期] │
└─────────────────────────────────────────────────────────┘
4.3.3 同期データ
{
"user_id": "12345",
"display_name": "山田太郎",
"department_code": "HR001",
"department_name": "人事部",
"permission_groups": ["management"]
}
| フィールド |
用途 |
| user_id |
社員番号(必須) |
| display_name |
表示名(必須) |
| department_code |
権限判定用(内部) |
| department_name |
参照用(権限判定には使わない) |
| permission_groups |
カスタム権限グループ |
5. RAG側 実装要件
5.1 SSO連携
| 実装対象 |
ファイル |
| SSOトークン発行API |
app/api/routes/auth.py |
| Azure Functions ルーティング |
app/api/function_app.py |
| フロントエンド トークン受け取り |
app/web/src/context/authContext.tsx |
| CORS設定更新 |
app/api/function_app.py |
5.2 ユーザー管理拡張
| 実装対象 |
ファイル |
| 一括インポートAPI |
app/api/routes/admin.py |
| UserEntityに部署・権限追加 |
app/api/services/table_storage.py |
| JWTに権限情報追加 |
app/api/services/auth.py |
5.2.1 ユーザー表示仕様
RAG画面上でのユーザー表示はシンプルに保つ:
| 表示項目 |
例 |
| 社員番号 |
12345 |
| 氏名 |
山田太郎 |
表示しない項目: 部署名、権限グループ(内部処理のみ)
5.2.2 ユーザーデータ構造
{
"user_id": "12345",
"display_name": "山田太郎",
"department_code": "HR001",
"permission_groups": ["management", "hr"],
"individual_permissions": ["doc-confidential-001"],
"role": "user",
"is_active": true
}
| フィールド |
型 |
説明 |
| department_code |
string |
部署コード(権限判定用) |
| permission_groups |
array |
カスタム権限グループ |
| individual_permissions |
array |
個別許可されたドキュメントID |
5.3 環境設定
Azure Functions に以下の設定を追加:
| 設定キー |
説明 |
デフォルト値 |
SSO_SHARED_SECRET |
WordPress共有シークレット |
(必須) |
SSO_TIMESTAMP_TOLERANCE |
タイムスタンプ許容秒数 |
300 |
JWT_EXPIRES_HOURS |
JWTトークン有効期限(時間) |
3 |
WORDPRESS_DOMAIN |
WordPressドメイン(CORS許可用) |
(必須) |
有効期限の変更方法:
# Azure Functions の アプリケーション設定 で変更
JWT_EXPIRES_HOURS=4 # 4時間に変更する場合
6. 権限制御仕様(P2)
6.1 設計方針
| 方針 |
説明 |
| 表示はシンプル |
ユーザーには社員番号と氏名のみ表示 |
| 権限は内部で柔軟に |
部署コード/グループ/個人の3レイヤーで管理 |
| WordPressが信頼できるソース |
RAGのユーザー情報は読み取り専用のコピー |
6.2 権限チェックフロー
① ロールチェック
├─ admin / developer → 全文書アクセス可 ✓
└─ user → ②へ進む
② 権限チェック(userロールのみ)
├─ 文書が全員公開(allow_all: true) → アクセス可 ✓
├─ ユーザーのdepartment_code ∈ allowed_department_codes → アクセス可 ✓
├─ ユーザーのpermission_groups ∩ allowed_groups ≠ ∅ → アクセス可 ✓
├─ ユーザーのuser_id ∈ allowed_users → アクセス可 ✓
└─ それ以外 → 検索結果から除外 ✗
6.3 文書権限データ構造
{
"document_id": "doc-001",
"title": "人事評価マニュアル",
"access_rules": {
"allow_all": false,
"allowed_department_codes": ["HR001", "GA001"],
"allowed_groups": ["management"],
"allowed_users": ["99999"]
}
}
| フィールド |
型 |
説明 |
| allow_all |
boolean |
true: 全員アクセス可(デフォルト) |
| allowed_department_codes |
array |
許可する部署コード |
| allowed_groups |
array |
許可する権限グループ |
| allowed_users |
array |
個別許可するユーザーID |
6.4 権限の優先順位
上記のいずれか1つでも条件を満たせばアクセス可(OR条件):
allow_all: true → 全員OK
- 部署コードが一致 → OK
- 権限グループが一致 → OK
- 個別ユーザーIDが一致 → OK
6.5 部署コード管理
表記揺れ防止のため、部署名ではなく部署コードで管理する。
| 部署コード |
部署名(参照用) |
| HR001 |
人事部 |
| GA001 |
総務部 |
| ACC001 |
経理部 |
| ... |
... |
- 部署マスタはWordPressで一元管理
- 部署名が変更されてもコードが同じなら権限に影響なし
6.6 既存文書の扱い
- 既存の全文書は
allow_all: true として扱う
- 管理画面から個別に権限を変更可能
7. セキュリティ要件
7.1 SSO連携
| 項目 |
対策 |
| 署名検証 |
HMAC-SHA256で改ざん防止 |
| リプレイ攻撃防止 |
タイムスタンプ有効期限5分 |
| トークン漏洩対策 |
URLパラメータは即座に削除、履歴に残さない |
| HTTPS必須 |
全通信をHTTPS化 |
7.2 シークレット管理
| 環境 |
管理方法 |
| 開発 |
環境変数または設定ファイル |
| 本番(WordPress) |
wp-config.php(.gitignore対象) |
| 本番(RAG) |
Azure Key Vault推奨 |
7.3 CORS設定
許可オリジン: https://wordpress.example.com
許可メソッド: POST, OPTIONS
許可ヘッダー: Content-Type, Authorization
8. 運用手順
8.1 月次ユーザー同期
1. WordPress管理画面から本社社員CSVをエクスポート
2. CSV→JSON変換スクリプトを実行
3. POST /api/manage/users/bulk でRAGにインポート
4. 結果を確認(created/updated/errors)
8.2 新規社員の追加
- 月次同期で自動的に追加される
- 即時対応が必要な場合は個別登録
8.3 退職者の対応
is_active: false に設定(論理削除)
- または月次同期から除外
9. 実装スケジュール
Phase 1(P1): SSO連携・ユーザー管理
| 担当 |
タスク |
見積もり |
| RAG |
SSOトークン発行API |
0.5日 |
| RAG |
ユーザーテーブル拡張(部署追加) |
0.5日 |
| RAG |
一括インポートAPI |
1日 |
| RAG |
フロントエンド SSOトークン受け取り |
0.5日 |
| RAG |
CSV変換スクリプト |
0.5日 |
| WordPress |
SSO連携機能(トークン取得・RAG起動) |
1日 |
| WordPress |
ユーザーCSVエクスポート機能 |
0.5日 |
| 共通 |
結合テスト |
1日 |
合計: 約5.5日
Phase 2(P2): 部署権限制御
| 担当 |
タスク |
見積もり |
| RAG |
検索フィルタリング実装 |
1日 |
| RAG |
文書権限管理API |
1日 |
| RAG |
管理画面UI |
2日 |
| 共通 |
テスト |
1日 |
合計: 約5日
10. 付録
10.1 エラーコード一覧
| コード |
HTTPステータス |
説明 |
INVALID_REQUEST |
400 |
リクエスト形式が不正 |
INVALID_SIGNATURE |
401 |
署名が一致しない |
EXPIRED_TIMESTAMP |
401 |
タイムスタンプ期限切れ |
USER_NOT_FOUND |
404 |
ユーザー未登録 |
UNAUTHORIZED |
401 |
認証トークンが無効 |
FORBIDDEN |
403 |
権限不足 |
10.2 ロール定義
| ロール |
説明 |
文書アクセス |
| admin |
管理者 |
全文書 |
| developer |
開発者 |
全文書 |
| user |
一般ユーザー |
部署制限あり |
10.3 連絡先
| 担当 |
連絡先 |
| RAG開発 |
(記入してください) |
| WordPress開発 |
(記入してください) |
更新履歴
| 日付 |
バージョン |
変更内容 |
| 2024-12-24 |
1.0 |
初版作成 |