> ## Documentation Index
> Fetch the complete documentation index at: https://developers.authlete.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 外部 IdP との連携

> Authlete と外部 IdP を連携させる方法を説明する文書です。

# はじめに

この文書は Authlete ベースの認可サーバー (AS) と外部のアイデンティティプロバイダー (IdP) との連携方法を説明するチュートリアルです。この連携により、[Financial-grade API](https://fapi.openid.net/) (FAPI) のような高いセキュリティの業界標準に準拠した API 認可基盤を構築すると同時に、外部 IdP のユーザー認証・管理機能を利用できるようになります。

この連携のしくみは、ユーザーの認証情報や属性情報などを IDaaS (Identity as a Service) を用いて既に維持管理しているサービス事業者が、[英国オープンバンキング](https://www.openbanking.org.uk/) / [オーストラリア消費者データ権](https://www.cdr.gov.au/) / [ブラジルオープンバンキング](https://openbankingbrasil.org.br/)などのエコシステムが定義する FAPI ベースのセキュリティ要件を実装しなければならない場合に有用です。

このチュートリアルでは、OpenID Connect (OIDC) IdP として [Okta](https://www.okta.com/jp/)、Authlete を利用する AS として [java-oauth-server](https://github.com/authlete/java-oauth-server) を用い、連携方法の例を説明します。

# 前提条件

このチュートリアルを始める前に次のアカウントを用意しておいてください。

* Authlete アカウント (参照→『[サインアップから Authlete サービス作成までの手順](https://www.authlete.com/ja/developers/tutorial/signup/)』)
* Okta アカウント (参照→ [Okta ウェブサイト](https://www.okta.com/jp/))

また、[java-oauth-server](https://github.com/authlete/java-oauth-server) をインストールして実行する環境も必要となります。

# AS と IdP の連携

OAuth 2.0 ([RFC 6749](https://www.rfc-editor.org/rfc/rfc6749.html)) と OIDC ([OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html)) の仕様は、AS がユーザーをどのように認証するかについては意図的に定義していません。そのため AS は、ユーザー認証を外部 OIDC IdP に委譲し、当 IdP からユーザー情報を取得するという手段をとるかもしれません。下図は、そのような AS と外部 OIDC IdP との間の、アイデンティティ (ID) 連携のフローを示しています。

<img src="https://mintcdn.com/authlete/6e8OPufcWwl4fTPw/deployment-and-operations/integration-with-api-gateways/id-federation_ja.png?fit=max&auto=format&n=6e8OPufcWwl4fTPw&q=85&s=fcc7ff528bdf5bb8e87b63ce14d6a4e7" alt="" width="1920" height="1080" data-path="deployment-and-operations/integration-with-api-gateways/id-federation_ja.png" />

図に示したフローの各手順は以下の通りです。

1. **クライアントアプリケーション**がウェブブラウザ経由で **AS** の**認可エンドポイント**に**認可リクエスト**を送る。
2. AS の認可エンドポイントは**認可ページ**を生成してウェブブラウザに返す。AS が外部 IdP との ID 連携をサポートしていれば、認可ページに ID 連携用のリンクが含まれる。
3. 表示された認可ページ内の ID 連携用のリンクをユーザーがクリックする。この操作により、選択された IdP との ID 連携を開始する Web API (図中の「(フェデレーション/開始)」) にウェブブラウザがアクセスする。
4. Web API は **OpenID Connect** に準拠する**認証リクエスト**を作成する。認証リクエストは **IdP** の認可エンドポイントを指す URL で、クエリー部にリクエストパラメーター群を含む。
5. Web API は `Location` ヘッダーをつけて `302 Found` (リダイレクションを促すものであれば何でもよい) をウェブブラウザに返す。これによりウェブブラウザは IdP の認可エンドポイントに認証リクエストを送ることになる。
6. ウェブブラウザが IdP の認可エンドポイントに認証リクエストを送る。
7. IdP の認可エンドポイントは認可ページを生成してウェブブラウザに返す。その認可ページは、「(クライアントではなく) AS が権限を要求している」とユーザーに伝える。
8. ユーザーは IdP で自身のユーザー認証をおこない、AS からの認証リクエストを承認する。
9. IdP は**認可コード**を生成し、`Location` ヘッダーをつけて `302 Found` (リダイレクションを促すものであれば何でもよい) をウェブブラウザに返す。これによりウェブブラウザは (クライアントのものではなく) AS の**リダイレクション URI** に認可コード付きでアクセスする。IdP の視点からは、AS はクライアントアプリケーションであり、当該リダイレクション URI は AS が事前に IdP に登録しておいたものの一つである。
10. AS のリダクレションエンドポイント (図中の「フェデレーション/コールバック」) が認可コードを受け取る。
11. リダイレクションエンドポイントは、IdP の**トークンエンドポイント**に、受け取った認可コードを含む**トークンリクエスト**を送る。
12. IdP のトークンエンドポイントは**アクセストークン**と **ID トークン**を生成して返す。
13. リダイレクションエンドポイントは返却されたアクセストークンを添えて IdP の**ユーザー情報エンドポイント**にアクセスする。
14. IdP のユーザー情報エンドポイントはユーザーの情報を返す。
15. リダイレクションエンドポイントはユーザーが AS にログインしたとみなし、認可ページを再生成してウェブブラウザに返す。
16. ユーザーはクライアントからの当初の認可リクエストを承認する。
17. AS は認可コードを生成し、`Location` ヘッダーをつけて `302 Found` (リダイレクションを促すものであれば何でもよい) をウェブブラウザに返す。これによりウェブブラウザは (AS のものではなく) クライアントのリダイレクション URI に認可コード付きでアクセスする。ここではクライアントが少なくとも一つのリダイレクション URI を事前に AS に登録済みであることを想定している。
18. クライアントのリダクレションエンドポイント (図中の「リダイレクションエンドポイント」) が認可コードを受け取る。
19. クライアントはリダイレクションエンドポイント経由で受け取った認可コードを添えて AS のトークンエンドポイントにアクセスする。
20. AS のトークンエンドポイントはアクセストークンを生成してクライアントに返す。

次の節では、Okta を IdP として設定し、Authlete をバックエンドとして用いる AS  (java-oauth-server) と連動させて上図のフローを実現する方法を紹介します。

<aside class="bg-light p-3 m-3">
  java-oauth-server 自身は IdP に依存しておらず、OIDC 互換であればどの IdP とも連動できます。他の IdP を利用するように設定する方法については[付録](#appendix)を参照してください。
</aside>

# IdP (Okta) の設定

OIDC 互換 IdP のクライアントアプリケーションとして AS が動作できるよう、Okta 上で稼働するサーバーに「**App Integration**」を作成する必要があります。

下表は App Integration 設定の要点を示しています。

| 項目                    | 値                                                    |
| :-------------------- | :--------------------------------------------------- |
| Sign-in method        | OIDC - OpenID Connect                                |
| Application Type      | Web Application                                      |
| Sign-in redirect URIs | `http://localhost:8080/api/federation/callback/okta` |
| Controlled access     | Allow everyone in your organization to access        |

Sign-in redirect URIs の値を見てお気付きかもしれませんが、このチュートリアルの後半でローカルマシン上のポート番号 8080 で AS を動かすことになります。また、その AS は `/api/federation/callback/okta` API を提供します。

次の画像群は App Integration 設定のスクリーンショットです。

<img src="https://mintcdn.com/authlete/6e8OPufcWwl4fTPw/deployment-and-operations/integration-with-api-gateways/okta-app-config-1.png?fit=max&auto=format&n=6e8OPufcWwl4fTPw&q=85&s=1706ac30de0d83235ea1db441b90288d" alt="" width="948" height="828" data-path="deployment-and-operations/integration-with-api-gateways/okta-app-config-1.png" />

<img src="https://mintcdn.com/authlete/6e8OPufcWwl4fTPw/deployment-and-operations/integration-with-api-gateways/okta-app-config-2.png?fit=max&auto=format&n=6e8OPufcWwl4fTPw&q=85&s=b0943b5cd63dd300a7d885cc2dc3381c" alt="" width="904" height="709" data-path="deployment-and-operations/integration-with-api-gateways/okta-app-config-2.png" />

<img src="https://mintcdn.com/authlete/6e8OPufcWwl4fTPw/deployment-and-operations/integration-with-api-gateways/okta-app-config-3.png?fit=max&auto=format&n=6e8OPufcWwl4fTPw&q=85&s=d2e007f1e6ffa73cccbe74709656e79d" alt="" width="904" height="288" data-path="deployment-and-operations/integration-with-api-gateways/okta-app-config-3.png" />

App Integration 生成後、Okta は**クライアント ID** と**クライアントシークレット**の組を生成します。次節でこの組を利用します。

<img src="https://mintcdn.com/authlete/6e8OPufcWwl4fTPw/deployment-and-operations/integration-with-api-gateways/okta-app-config-4.png?fit=max&auto=format&n=6e8OPufcWwl4fTPw&q=85&s=a49473927b0a6a564c6d604a23047c88" alt="" width="724" height="539" data-path="deployment-and-operations/integration-with-api-gateways/okta-app-config-4.png" />

# AS  (java-oauth-server) の設定

このチュートリアルでは [java-oauth-server](https://github.com/authlete/java-oauth-server) を AS として用います。java-oauth-server はオープンソースの AS サンプル実装で、Authlete をバックエンドとして利用します。

まず初めに java-oauth-server をダウンロードして `authlete.properties` を編集してください。少なくとも同ファイル内の `service.api_key` と `service.api_secret` の値を変更する必要があります。

```
$ git clone https://github.com/authlete/java-oauth-server
$ cd java-oauth-server
$ vi authlete.properties
```

次に `federations.json` をテキストエディタで開いてください。このファイルに ID 連携の設定を記述します。内容の初期値は下記のようになっています。`YOUR_COMPANY`、`YOUR_CLIENT_ID`、`YOUR_CLIENT_SECRET` を Okta から割り当てられた実際の値で置き換えてください。`federations.json` の詳細については「付録 / [federations.json](#federations-json)」を参照してください。

```json theme={null}
{
  "federations": [
    {
      "id": "okta",
      "server": {
        "name": "Okta-hosted IdP",
        "issuer": "https://YOUR_COMPANY.okta.com"
      },
      "client": {
        "clientId": "YOUR_CLIENT_ID",
        "clientSecret": "YOUR_CLIENT_SECRET",
        "redirectUri": "http://localhost:8080/api/federation/callback/okta",
        "idTokenSignedResponseAlg": "RS256"
      }
    }
  ]
}
```

必要な設定は以上です。ローカルマシン上で java-oauth-server を起動してください。

```
$ docker-compose up
    # もしくは mvn jetty:run
```

次のようなログが確認できれば、java-oauth-server は準備完了です。

```
app_1  | [INFO] Started ServerConnector@7c5ae5c3{HTTP/1.1, (http/1.1)}{0.0.0.0:8080}
app_1  | [INFO] Started @41702ms
app_1  | [INFO] Started Jetty Server
```

# 動作確認

「[ AS と IdP の ID 連携](#id-federation)」で示した図では、クライアントは OIDC 認可コードフロー (`response_type=code`) を用いています。しかしここでは、Okta との ID 連携が機能していることを素早く確認するため、ID トークンのみを発行する OIDC インプリシットフロー (`response_type=id_token`) を使用します。

次の URL は認可リクエストです。`CLIENT_ID` をAuthlete から発行されたクライアント ID (Okta から発行されたものではない)、`SERVICE_API_KEY` を Authlete から発行されたサービス API キーで置き換え、URL をウェブブラウザのアドレスバーに貼り付けてください。

```
http://localhost:8080/api/authorization?client_id=CLIENT_ID&response_type=id_token&scope=address+email+openid+phone+profile&redirect_uri=https://api.authlete.com/api/mock/redirection/SERVICE_API_KEY&nonce=mynonce&state=mystate&prompt=login
```

次のような認可ページが表示されます。Authorize ボタンの上に表示されている「Okta-hosted IdP」リンクをクリックしてください。

<img src="https://mintcdn.com/authlete/6e8OPufcWwl4fTPw/deployment-and-operations/integration-with-api-gateways/authorization-page-1.png?fit=max&auto=format&n=6e8OPufcWwl4fTPw&q=85&s=53b2b35da01bf139d6d1e0643a0e3140" alt="" width="1576" height="1980" data-path="deployment-and-operations/integration-with-api-gateways/authorization-page-1.png" />

ウェブブラウザは Okta が生成した次のような認可ページを表示します。Okta 上で稼働しているサーバーにログインする際に利用するログイン ID とパスワードを入力してください。

<img src="https://mintcdn.com/authlete/6e8OPufcWwl4fTPw/deployment-and-operations/integration-with-api-gateways/authorization-page-2.png?fit=max&auto=format&n=6e8OPufcWwl4fTPw&q=85&s=a05a2d2d41a00a792572b5b805aeb1f0" alt="" width="1574" height="1526" data-path="deployment-and-operations/integration-with-api-gateways/authorization-page-2.png" />

ウェブブラウザは java-oauth-server が生成した元々の認可ページを再表示します。この際、java-oauth-server はあなたがログイン済みと認識しています。Authorize ボタンを押してください。

<img src="https://mintcdn.com/authlete/6e8OPufcWwl4fTPw/deployment-and-operations/integration-with-api-gateways/authorization-page-3.png?fit=max&auto=format&n=6e8OPufcWwl4fTPw&q=85&s=0f7afdc6e99fc49adce890627e17b44d" alt="" width="1574" height="1788" data-path="deployment-and-operations/integration-with-api-gateways/authorization-page-3.png" />

java-oauth-server は ID トークンを発行し、ブラウザをリダイレクションエンドポイントにリダイレクトします。Authlete サーバー上で提供されているリダイレクションエンドポイントのモック実装 (`/api/mock/redirection/サービスAPIキー`) は受け取ったパラメーター群を表示します。

リクエストに暗号化されていない ID トークンが含まれていれば、リダイレクションエンドポイントはその ID トークンのペイロード部をデコードし、次のように表示します。

<img src="https://mintcdn.com/authlete/6e8OPufcWwl4fTPw/deployment-and-operations/integration-with-api-gateways/redirection-endpoint.png?fit=max&auto=format&n=6e8OPufcWwl4fTPw&q=85&s=5813e80a3c5b6c14242b1360acc9350c" alt="" width="1574" height="1912" data-path="deployment-and-operations/integration-with-api-gateways/redirection-endpoint.png" />

次の表は、発行された ID トークン内のクレーム群のデータ提供元を示しています。ユーザー属性に関する属性は全て Okta 由来です。

| クレーム                    | データ提供元                                                                                     |
| :---------------------- | :----------------------------------------------------------------------------------------- |
| `email`                 | Okta                                                                                       |
| `email_verified`        | Okta                                                                                       |
| `family_name`           | Okta                                                                                       |
| `given_name`            | Okta                                                                                       |
| `locale`                | Okta                                                                                       |
| `name`                  | Okta                                                                                       |
| `phone_number_verified` | Okta                                                                                       |
| `preferred_username`    | Okta                                                                                       |
| `updated_at`            | Okta                                                                                       |
| `zoneinfo`              | Okta                                                                                       |
| `iss`                   | Authlete / Service 設定, `issuer`                                                            |
| `sub`                   | java-oauth-server / Authlete の `/api/auth/authorization/issue` API に渡された `subject` パラメーター  |
| `aud`                   | Authlete / Client 設定, `clientId` (変更不可)                                                    |
| `exp`                   | Authlete / ID トークンの有効期間終了時刻                                                                |
| `iat`                   | Authlete / ID トークンの発行時刻                                                                    |
| `auth_time`             | java-oauth-server / Authlete の `/api/auth/authorization/issue` API に渡された `authTime` パラメーター |
| `nonce`                 | クライアント / `nonce` リクエストパラメーターの値                                                             |
| `s_hash`                | Authlete / `state` リクエストパラメーターの値に基づいて計算                                                    |

`sub` クレームの値 (例内の `"00ucabhbjLVphPrXF696@okta"`) がどこから来たのか気になられるかもしれません。この値は、Okta が返した `sub` クレームの値と文字列 `"@okta"` を連結した結果に過ぎません。連結は [FederationEndpoint.java](https://github.com/authlete/java-oauth-server/blob/master/src/main/java/com/authlete/jaxrs/server/api/FederationEndpoint.java) の `createUserEntity()` メソッドで実行されています。これに大きな意味はなく、`sub` クレームの値をどのように決めるかはあなた次第です。

# まとめ

本チュートリアルでは、Authlete ベースの認可サーバー (AS) と外部のアイデンティティプロバイダー (IdP) との連携パターンを、java-oauth-server と Okta を用いて、実際の動作を確認しました。

# 付録

## 実装の詳細

ID 連携の実装レベルの詳細については [java-oauth-server](https://github.com/authlete/java-oauth-server) の [Federation.java](https://github.com/authlete/java-oauth-server/blob/master/src/main/java/com/authlete/jaxrs/server/federation/Federation.java) と [FederationEndpoint.java](https://github.com/authlete/java-oauth-server/blob/master/src/main/java/com/authlete/jaxrs/server/api/FederationEndpoint.java) を調べることで理解できます。

OIDC IdP のユーザー情報エンドポイントからユーザー情報を受け取ったあと、java-oauth-server の現実装はメモリ上のユーザーデータベースにユーザーレコードを登録します。しかし、商用実装では他の選択肢もありうるでしょう。

例えば、OIDC IdP から得たユーザーデータを手元のデータベースに複製することを避け、かわりに IdP から発行されたアクセストークンを覚えておき、後ほど好きな時に IdP のユーザー情報エンドポイントから最新のユーザー情報を取り出せるようにするかもしれません。

そのような動作を実装するため、AS は IdP から発行されたアクセストークンを覚えておくデータベースを用意するかもしれません。その他にも、アクセストークンに任意のキー・バリュー群を関連付ける Authlete 独自の **[Extra Properties](https://www.authlete.com/developers/definitive_guide/extra_properties/)** 機能を用い、IdP が発行したアクセストークンを Authlete に覚えておかせるという方法も考えられます (参照→『[トークンに任意のプロパティをひもづける方法](https://kb.authlete.com/ja/s/oauth-and-openid-connect/a/extra-properties)』)。

## federations.json

`federations.json` は ID 連携の設定を記述するための設定ファイルです。[java-oauth-server](https://github.com/authlete/java-oauth-server) は最初に認可リクエストを受け取ったときに当ファイルを読み込みます。ファイル内に列挙された ID 連携は java-oauth-server の認可ページ内に表示されます。

ファイルのデフォルトの位置は `"fderations.json"` です。環境変数 `FEDERATIONS_FILE` およびシステムプロパティー `federations.file` を用いて他の場所を指定することができます。

`federations.json` の内容は、`"federations"` を最上位プロパティーとして持つ JSON オブジェクトでなければなりません。

```json theme={null}
{
  "federations": [
    "(ID 連携の設定群)"
  ]
}
```

`"federations"` 配列の各要素は ID 連携の設定を表し、`"id"`、`"server"`、`"client"` を最上位プロパティーとして持つ JSON オブジェクトです。

```json theme={null}
{
  "federations": [
    {
      "id": "(設定群内で一意となる識別子)",
      "server": {
        "(サーバー設定)"
      },
      "client": {
        "(クライアント設定)"
      }
    }
  ]
}
```

`"id"` の値は、java-oauth-server の次の API パス内の `federationId` 部で使用されます。

* `/api/federation/initiation/federationId`
* `/api/federation/callback/federationId`

`"server"` には対象の IdP の設定を記述します。値は JSON オブジェクトで、下表のプロパティー群を持ちます。

| プロパティー   | 説明                                                                                                                                                                                      |
| :------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`   | IdP の表示名。認可ページで利用されます。                                                                                                                                                                  |
| `issuer` | IdP の発行者識別子。この値は IdP のディスカバリードキュメント内の `"issuer"` の値と一致していなければなりません。java-oauth-server の ID 連携を機能させるためには、IdP が `{issuer}/.well-known/openid-configuration` でディスカバリードキュメントを公開していなければなりません。 |

`"client"` はクライアントの設定を記述するもので、ここでクライアントは常に java-oauth-server を指します。外部 IdP から見ると java-oauth-server はクライアントアプリケーションであることに留意してください。`"client"` の値は JSON オブジェクトで、下表のプロパティー群を持ちます。

| プロパティー                     | 説明                                                                                                                                                                                              |
| :------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `clientId`                 | IdP が発行したクライアント ID。                                                                                                                                                                             |
| `clientSecret`             | IdP が発行したクライアントシークレット。このプロパティーがセットされている場合、IdP のトークンエンドポイントに送られるトークンリクエストにクライアント認証用の `Authorization` ヘッダーが含まれるようになります。この動作はトークンエンドポイントがクライアント認証方式として `client_secret_basic` をサポートしていることを前提としています。 |
| `redirectUri`              | IdP に事前登録してあるリダイレクション URI。                                                                                                                                                                      |
| `idTokenSignedResponseAlg` | IdP が ID トークンに署名する際に使用するアルゴリズム。このプロパティーが省略された場合、デフォルト値として `"RS256"` が使用されます。                                                                                                                    |
