Chuyển đến nội dung chính

レッスン 14: 組織 - マルチテナンシーと CIAM

組織機能の有効化と構成、組織、組織ドメイン、組織属性の作成/管理、メンバーの管理 (管理対象、非管理対象)、招待管理 (送信、追跡、再送信、削除)、ID プロバイダーと組織の関連付け、メンバーの認証 (ID 優先ログイン)、組織クレームのトークンへのマッピング、および実際の B2B/B2B2C ユースケース。

🔒 DevSecOps — レッスン 14 レッスン 14: 組織 - マルチテナントと CIAM

基本から上級までの Keycloak

パート 4: ユーザー フェデレーション、組織、および認可

xdev.asia

1. 組織 - 概要

Keycloak Organizations は有効化機能ですマルチテナント管理単一の領域内で。組織 (テナント) ごとに複数のレルムを作成する代わりに、組織レルム内でユーザーをグループ化し、ドメインを管理し、組織ごとにアクセスを制御します。

これはプラットフォームにとって重要な機能ですB2B(企業間) およびB2B2C(企業対企業対消費者)、としても知られています。CIAM(顧客のアイデンティティとアクセス管理)。

1.1 使用例

シナリオ説明する
SaaS マルチテナント各顧客企業は組織であり、ユーザーはその組織に所属します。
B2B ポータルパートナー/ベンダーは独自の組織を持ち、パートナーの従業員は組織を通じてポータルにアクセスします。
子会社を持つ企業グループには多数の子会社があり、各子会社は組織です
教育プラットフォーム各学校/教育機関は組織であり、教師/生徒はメンバーです

2.組織機能をオンにする

組織はKeycloak 25以降で利用できる機能です。有効にするには:

2.1 レルムで有効にする

# Qua Admin Console:
# Realm Settings → General → Organizations → Enabled

# Qua kcadm.sh:
kcadm.sh update realms/my-realm \
  -s organizationsEnabled=true

2.2 ステータスの確認

# Qua REST API
curl -s "http://localhost:8080/admin/realms/my-realm" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" | jq '.organizationsEnabled'
# Output: true

3. 組織の作成と管理

3.1 管理コンソールから組織を作成する

入力管理コンソール → 組織 → 組織の作成:

分野説明する例えば
名前組織名 (必須)アクメ株式会社
エイリアス名前から生成された一意のエイリアスアクメコーポレーション
説明組織の説明Acme Corp - 企業顧客
リダイレクト URLメンバーログイン後の URL リダイレクトhttps://app.acme.com

3.2 REST API経由で組織を作成する

# Tạo organization
curl -X POST "http://localhost:8080/admin/realms/my-realm/organizations" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Acme Corporation",
    "alias": "acme-corporation",
    "description": "Enterprise customer - Acme Corp",
    "redirectUrl": "https://app.acme.com",
    "enabled": true,
    "domains": [
      {
        "name": "acme.com",
        "verified": true
      }
    ],
    "attributes": {
      "plan": ["enterprise"],
      "industry": ["technology"],
      "region": ["asia-pacific"]
    }
  }'

# List organizations
curl -s "http://localhost:8080/admin/realms/my-realm/organizations" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" | jq '.'

# Get organization by ID
ORG_ID="org-uuid-here"
curl -s "http://localhost:8080/admin/realms/my-realm/organizations/${ORG_ID}" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" | jq '.'

# Update organization
curl -X PUT "http://localhost:8080/admin/realms/my-realm/organizations/${ORG_ID}" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Acme Corporation (Updated)",
    "description": "Updated description",
    "enabled": true
  }'

# Delete organization
curl -X DELETE "http://localhost:8080/admin/realms/my-realm/organizations/${ORG_ID}" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}"

4. 組織ドメイン

許可される組織ドメインユーザーを組織に自動的に関連付ける電子メールドメインに基づいています。ユーザーが登録されたドメインに属する電子メールを使用して登録またはログインすると、Keycloakはユーザーを対応する組織に自動的にアタッチできます。

# Thêm domain cho organization
curl -X POST "http://localhost:8080/admin/realms/my-realm/organizations/${ORG_ID}/domains" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "acme.com",
    "verified": true
  }'

# List domains
curl -s "http://localhost:8080/admin/realms/my-realm/organizations/${ORG_ID}/domains" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" | jq '.'

ドメインの状態:

州説明する
確認済み検証済みドメイン — この電子メール ドメインを持つユーザーは、自動的に組織に参加する資格が与えられます。
未検証未検証のドメイン - 照合にのみ使用され、管理者の承認が必要です

重要な注意事項:

  • ドメインはただ属しているだけです単一の組織
  • ドメイン検証は、組織がそのドメインを実際に所有していることを確認するのに役立ちます
  • サブドメインの一致:アクメ.com一致します[email protected]しかしそうではないマッチ。マッチ[email protected]

5. 組織の属性

カスタム属性を使用すると、組織の追加のメタデータを保存できます。

# Set attributes khi tạo hoặc update organization
curl -X PUT "http://localhost:8080/admin/realms/my-realm/organizations/${ORG_ID}" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Acme Corporation",
    "attributes": {
      "plan": ["enterprise"],
      "max_users": ["500"],
      "contract_end_date": ["2027-12-31"],
      "sla_tier": ["platinum"],
      "billing_email": ["[email protected]"]
    }
  }'

属性は以下で使用できます。

  • トークンの要求— 組織メタデータをアクセス/ID トークンに追加します
  • 認可ポリシー— 属性に基づく認可
  • カスタムロジック— アプリケーションでビジネス ロジックを処理する

6. メンバーの管理

6.1 管理対象メンバーと非管理対象メンバー

タイプ説明する例えば
管理されたユーザーは組織によって完全に管理されます - ライフサイクルは組織に関連付けられています会社員: 組織を離れると、アカウントは無効になります
管理されていない無料ユーザー、組織に「参加」するだけ - アカウントは独立して存在しますフリーランサー、請負業者: 多くの組織に所属できる

6.2 既存のユーザーを組織に追加する

# Thêm user vào organization
USER_ID="user-uuid-here"
curl -X PUT "http://localhost:8080/admin/realms/my-realm/organizations/${ORG_ID}/members/${USER_ID}" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}"

# List members của organization
curl -s "http://localhost:8080/admin/realms/my-realm/organizations/${ORG_ID}/members" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" | jq '.[].username'

# Get membership info cho user
curl -s "http://localhost:8080/admin/realms/my-realm/organizations/${ORG_ID}/members/${USER_ID}" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" | jq '.'

# Remove member
curl -X DELETE "http://localhost:8080/admin/realms/my-realm/organizations/${ORG_ID}/members/${USER_ID}" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}"

6.3 メンバーの役割

組織のメンバーには、組織のコンテキストで役割を割り当てることができます。

役割説明する
メンバー。メンバーデフォルトの役割 — 標準アクセス
管理者。管理者組織管理者 — メンバーと設定を管理できます

7. 招待状の管理

Keycloak組織によるサポートユーザーを招待する招待メールを通じて組織に参加してください。

7.1 招待状の送信

# Gửi invitation qua REST API
curl -X POST "http://localhost:8080/admin/realms/my-realm/organizations/${ORG_ID}/invitations" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]",
    "firstName": "John",
    "lastName": "Doe",
    "redirectUrl": "https://app.example.com/welcome"
  }'

7.2 招待状態

州説明する
保留中招待状を送信しましたが、まだ受け入れられていません
期限切れ招待の有効期限 (構成可能な有効期限)

7.3 招待状の管理

# List pending invitations
curl -s "http://localhost:8080/admin/realms/my-realm/organizations/${ORG_ID}/invitations" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" | jq '.'

# Resend invitation
INV_ID="invitation-uuid-here"
curl -X POST "http://localhost:8080/admin/realms/my-realm/organizations/${ORG_ID}/invitations/${INV_ID}/resend" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}"

# Delete/cancel invitation
curl -X DELETE "http://localhost:8080/admin/realms/my-realm/organizations/${ORG_ID}/invitations/${INV_ID}" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}"

電子メールのテンプレート:Keycloakは、カスタマイズ可能なテンプレートを使用してメールを送信します。レルム設定 → 電子メール → テンプレート。デフォルトのテンプレートには、ユーザーが招待を受け入れてアカウントを作成するためのリンクが含まれています (アカウントを持っていない場合)。

8. ID プロバイダーを組織に関連付ける

各組織が持つことができるのは、プライベートアイデンティティプロバイダー、メンバーは組織の IdP 経由でログインできるようになります (たとえば、Acme Corp は Google Workspace を使用し、Beta Inc は Okta を使用します)。

8.1 IdP を組織に関連付ける

# Liên kết Identity Provider với organization
IDP_ALIAS="acme-google-workspace"
curl -X POST "http://localhost:8080/admin/realms/my-realm/organizations/${ORG_ID}/identity-providers" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  -H "Content-Type: application/json" \
  -d "\"${IDP_ALIAS}\""

# List linked Identity Providers
curl -s "http://localhost:8080/admin/realms/my-realm/organizations/${ORG_ID}/identity-providers" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" | jq '.'

# Unlink Identity Provider
curl -X DELETE "http://localhost:8080/admin/realms/my-realm/organizations/${ORG_ID}/identity-providers/${IDP_ALIAS}" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}"

8.2 仕組み

  1. ユーザーがログイン ページに電子メールを入力します (ID 優先ログイン)
  2. Keycloakはメールドメインを特定 → 対応する組織を見つける
  3. 組織にリンクされた IdP がある場合 → ユーザーをその組織の IdP にリダイレクトします
  4. ユーザーは組織の IdP で認証します
  5. Keycloakは応答を受信し、ユーザーを組織に自動的にアタッチします。

9. ID 優先ログイン

ID 優先ログインは、ユーザーが最初にメールアドレスを入力してください, 次に、Keycloak は組織のメンバーシップに基づいて適切な認証方法を決定します。

9.1 IDファーストログインの構成

組織が有効になっている場合、Keycloakは自動的にフローを作成します"組織"ブラウザ認証用。このフローは次のように機能します。

Organization Browser Flow
├── Cookie (Alternative)
├── Organization Identity-First (Alternative)
│   ├── Username Form (Required)           → User nhập email
│   └── Organization (Conditional)
│       ├── Condition - Organization Member → Kiểm tra user thuộc organization
│       └── Organization Identity Provider  → Redirect đến org's IdP
└── Forms (Alternative)
    ├── Username Password Form (Required)
    └── Conditional OTP (Conditional)

9.2 組織の既存のフローを構成する

# Bind Organization flow cho Browser
kcadm.sh update realms/my-realm \
  -s 'browserFlow=organization browser'

10. 組織のクレームをトークンにマッピングする

Keycloak は追加する可能性があります組織情報をアクセストークンとIDトークンに変換を使用すると、アプリケーションはユーザーがどの組織に属しているかを知ることができます。

10.1 組織メンバーシップ マッパー

Keycloakが自動的に追加されました組織組織機能が有効になっている場合に要求します。請求の形式:

{
  "sub": "user-uuid",
  "email": "[email protected]",
  "organization": {
    "acme-corporation": {
      "name": "Acme Corporation",
      "roles": ["member"]
    }
  },
  "iss": "http://localhost:8080/realms/my-realm",
  "aud": "my-app"
}

10.2 カスタムマッパー構成

追加できます組織メンバーシップ プロトコル マッパークライアント スコープに移動してカスタマイズします。

# Thêm Organization Membership Mapper vào client scope
kcadm.sh create clients/${CLIENT_ID}/protocol-mappers/models -r my-realm \
  -s name="organization-membership" \
  -s protocol=openid-connect \
  -s protocolMapper=oidc-organization-membership-mapper \
  -s 'config."claim.name"=organization' \
  -s 'config."id.token.claim"=true' \
  -s 'config."access.token.claim"=true' \
  -s 'config."userinfo.token.claim"=true'

10.3 アプリケーションでのクレームの使用

// Ví dụ: Express.js middleware kiểm tra organization
import { Request, Response, NextFunction } from 'express';

interface OrganizationClaim {
  [alias: string]: {
    name: string;
    roles: string[];
  };
}

function requireOrganization(orgAlias: string) {
  return (req: Request, res: Response, next: NextFunction) => {
    const token = req.user; // decoded JWT
    const orgs: OrganizationClaim = token.organization || {};

    if (!orgs[orgAlias]) {
      return res.status(403).json({
        error: `User is not a member of organization: ${orgAlias}`
      });
    }

    // Attach org info to request
    req.organization = orgs[orgAlias];
    next();
  };
}

// Usage
app.get('/api/dashboard',
  requireOrganization('acme-corporation'),
  (req, res) => {
    res.json({ message: `Welcome to ${req.organization.name}` });
  }
);

11. B2B および B2B2C の使用例

11.1 B2B パートナーポータル

Scenario: Platform cung cấp portal cho partners

Realm: platform-realm
├── Organization: "Partner A" (partner-a.com)
│   ├── IdP: Partner A's Okta
│   ├── Members: 50 employees
│   └── Attributes: { plan: "gold", api_quota: "10000" }
├── Organization: "Partner B" (partner-b.com)
│   ├── IdP: Partner B's Azure AD
│   ├── Members: 200 employees
│   └── Attributes: { plan: "platinum", api_quota: "unlimited" }
└── Organization: "Partner C" (partner-c.com)
    ├── IdP: Partner C's Google Workspace
    ├── Members: 30 employees
    └── Attributes: { plan: "silver", api_quota: "5000" }

Flow:
1. Partner employee truy cập portal
2. Nhập email → Keycloak detect organization từ domain
3. Redirect đến IdP của partner
4. Authenticate → token chứa organization claim
5. Application phân quyền dựa trên organization + plan

11.2 B2B2C SaaS プラットフォーム

Scenario: SaaS platform bán cho businesses, businesses mời end-users

Realm: saas-realm
├── Organization: "School A"
│   ├── Domain: school-a.edu.vn
│   ├── Members (Managed): Teachers, Admin staff
│   ├── Members (Unmanaged): Students (self-registered)
│   └── IdP: School A's LDAP → federated via Keycloak IdP
├── Organization: "School B"
│   ├── Domain: school-b.edu.vn
│   ├── Members (Managed): Teachers
│   └── Members (Unmanaged): Students
└── Individual users (no organization)
    └── Free tier users

12. ベストプラクティス

  • マルチレルムの代わりに組織を使用する— 管理オーバーヘッドを削減し、構成を共有します
  • ドメインの検証— ユーザーを自動割り当てる前に、組織が実際にドメインを所有していることを確認してください。
  • 管理対象/非管理対象を区別する— ライフサイクル管理が必要な従業員向けに管理され、外部ユーザー向けには管理されません
  • ID 優先ログインを使用する— 異なる IdP を持つ多くの組織が存在する場合、UX は向上します
  • 安全な招待リンク— 適切な有効期限を設定し、保留中の招待を監視します
  • 認可のための組織属性— 属性を使用してプラン、階層、リージョンごとに権限を割り当てる
  • メンバー数を監視する— 組織が割り当てを超過した場合にアラートを設定する