コンテンツにスキップ

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条件):

  1. allow_all: true → 全員OK
  2. 部署コードが一致 → OK
  3. 権限グループが一致 → OK
  4. 個別ユーザー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 初版作成