> ## 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.

# Native SSO

> OpenID Connect Native SSO for Mobile Apps 1.0 仕様と Authlete の実装の説明

## コンセプト

**[OpenID Connect Native SSO for Mobile Apps 1.0](https://openid.net/specs/openid-connect-native-sso-1_0.html)**
(以下**Native SSO**) は、同一ベンダーの管理下にある複数のモバイルアプリケーション間でシングルサインオン (SSO)
を実現する仕組みを標準仕様として定義します。この仕様が実装されていると、ユーザはモバイルアプリケーション毎にユーザ認証を行う必要はなく、Native
SSOで連携したアプリケーション群に対して一回のユーザ認証で済みます。

<img alt="" class="mx-auto d-block" src="https://mintcdn.com/authlete/WGQFc11Y4wUplGrc/ja/protocols-and-flows/advanced-flows/nativesso_concept.png?fit=max&auto=format&n=WGQFc11Y4wUplGrc&q=85&s=f3167149dfab910817bb1d48ae98c11d" width="1920" height="1080" data-path="ja/protocols-and-flows/advanced-flows/nativesso_concept.png" />

<br />

## 仕様概要

詳細に踏み込む前に、仕様の概要を紹介します。

まず、一つ目のアプリケーションが認可コードフローを用いて下記のトークン群を取得します。注目すべき点は、IDトークンがNative
SSO仕様に準拠していること、および、**デバイスシークレット**という新しいタイプのトークンが含まれていることです。

1. アクセストークン
2. リフレッシュトークン (任意)
3. ID トークン (Native SSO仕様準拠)
4. デバイスシークレット

<img alt="" class="mx-auto d-block" src="https://mintcdn.com/authlete/WGQFc11Y4wUplGrc/ja/protocols-and-flows/advanced-flows/nativesso_spec_overview_0.png?fit=max&auto=format&n=WGQFc11Y4wUplGrc&q=85&s=55161978627322d5f2ddbc5fc536f3f8" width="1920" height="1080" data-path="ja/protocols-and-flows/advanced-flows/nativesso_spec_overview_0.png" />

一つ目のアプリケーションは、二つ目のアプリケーションがアクセスできる共有ストレージにIDトークンとデバイスシークレットを保存します。

<img alt="" class="mx-auto d-block" src="https://mintcdn.com/authlete/WGQFc11Y4wUplGrc/ja/protocols-and-flows/advanced-flows/nativesso_spec_overview_1.png?fit=max&auto=format&n=WGQFc11Y4wUplGrc&q=85&s=c10df4e8df8811baa6655e3183fde8f8" width="1920" height="1080" data-path="ja/protocols-and-flows/advanced-flows/nativesso_spec_overview_1.png" />

次に、二つ目のアプリケーションが共有ストレージからIDトークンとデバイスシークレットを取り出し、それらをパラメータとしてトークン交換リクエストを送信します。

<img alt="" class="mx-auto d-block" src="https://mintcdn.com/authlete/WGQFc11Y4wUplGrc/ja/protocols-and-flows/advanced-flows/nativesso_spec_overview_2.png?fit=max&auto=format&n=WGQFc11Y4wUplGrc&q=85&s=4506d5df56dfd6c9c2598232e683d041" width="1920" height="1080" data-path="ja/protocols-and-flows/advanced-flows/nativesso_spec_overview_2.png" />

応答として、二つ目のアプリケーション用のトークン群を含むトークン交換レスポンスが返却されます。

<img alt="" class="mx-auto d-block" src="https://mintcdn.com/authlete/WGQFc11Y4wUplGrc/ja/protocols-and-flows/advanced-flows/nativesso_spec_overview_3.png?fit=max&auto=format&n=WGQFc11Y4wUplGrc&q=85&s=8df46dda1dc786edf1034abec9fa89fa" width="1920" height="1080" data-path="ja/protocols-and-flows/advanced-flows/nativesso_spec_overview_3.png" />

まとめると、Native SSO仕様の概要図は次のようになります。

<img alt="" class="mx-auto d-block" src="https://mintcdn.com/authlete/WGQFc11Y4wUplGrc/ja/protocols-and-flows/advanced-flows/nativesso_spec_overview.png?fit=max&auto=format&n=WGQFc11Y4wUplGrc&q=85&s=f9da8388dfe4722c2481f9e98f817c13" width="1920" height="1080" data-path="ja/protocols-and-flows/advanced-flows/nativesso_spec_overview.png" />

<br />

### デバイスシークレット

デバイスシークレットはNative SSO仕様で次のように説明されています。

> The device secret contains relevant data to the device and the current users
> authenticated with the device. The device secret is completely opaque to the
> client and as such the AS MUST adequately protect the value such as using a
> JWE if the AS is not maintaining state on the backend.
>
> デバイスシークレットには、デバイスおよび現在そのデバイスで認証されているユーザに関連するデータが含まれます。
> デバイスシークレットはクライアントにとって完全に不透明であるため、認可サーバ (AS)
> はその値を適切に保護しなければなりません。
> 例えば、バックエンドで状態を保持しない場合にはJWEを使用するなどの手段が求められます。

デバイスシークレットを発行する際、OpenIDプロバイダは何らかの方法でデバイスの情報を取得し、デバイスシークレットと紐付けます。
そして、トークン交換リクエストを受け取った際、リクエストに含まれるデバイスシークレットに紐付くデバイスが、リクエスト送信元のデバイスと一致するかを確認します。

<br />

## 仕様詳細

### アプリ1の認可リクエスト

一つ目のアプリケーションは、認可コードフローに基づく認可リクエストをWebブラウザを介してOpenIDプロバイダに送信します。Native
SSO仕様固有の要求事項は、`scope`パラメータに`openid`スコープと`device_sso`スコープを含めることです。`device_sso`スコープは同仕様が定義するスコープです。

Native SSOに準拠する認可リクエストに最低限必要なリクエストパラメータは次の通りです。

| パラメータ           | 説明                                                                              |
| :-------------- | :------------------------------------------------------------------------------ |
| `client_id`     | クライアント識別子です。例えば`app_1`。                                                         |
| `response_type` | 要求するトークン群を空白文字区切りで列挙したものです。認可コードを要求する場合は`code`を含めます。                            |
| `scope`         | 要求するスコープ群を空白文字区切りで列挙したものです。Native SSO仕様に準拠するためには`openid`と`device_sso`を含めます。     |
| `redirect_uri`  | リダイレクトURIです。OpenID Connect仕様により、`openid`スコープを要求する際は`redirect_uri`パラメータが必須となります。 |

下記は認可リクエストの例です。

```text theme={null}
https://trial.authlete.net/api/authorization?client_id=app_1&response_type=code&scope=openid+device_sso&redirect_uri=https://nextdev-api.authlete.net/api/mock/redirection
```

<Note>
  この例は実際に動きます。認可ページが表示されたら、ログインIDとパスワードに`inga`、`inga`と入力してください。
  ログイン済みになっていて、再度ログインIDフィールドとパスワードフィールドを表示させたい場合は、認可リクエストの末尾に`&amp;prompt=login`を追加してください。
</Note>

<br />

### アプリ1のトークンリクエスト

上記の認可リクエストの結果得られた認可コードを用いてトークンリクエストを組み立てます。必要となるリクエストパラメータは次の通りです。

| パラメータ          | 説明                                                                                                                          |
| :------------- | :-------------------------------------------------------------------------------------------------------------------------- |
| `grant_type`   | グラントタイプです。どのフローを用いるかに関わらず必須のパラメータです。認可コードフローの場合は値として`authorization_code`を指定します。                                             |
| `code`         | 認可コードフローの場合に必須のパラメータです。認可リクエストの結果得られた認可コードを値として指定します。                                                                       |
| `redirect_uri` | リダイレクトURIです。先行する認可リクエストに`redirect_uri`パラメータを含めていた場合、トークンリクエストでも`redirect_uri`パラメータが必須となります。その値は認可リクエストで指定したものと同一でなければなりません。 |

また、上記に加え、アプリケーションのクライアントタイプ ([RFC 6749 Section 2.1](https://www.rfc-editor.org/rfc/rfc6749.html#section-2.1))
がクレデンシャルかパブリックか、また、クライアントタイプがコンフィデンシャルの場合にどのクライアント認証方式を用いるかにより、追加のパラメータが必要になります。

例えば、クライアントタイプがパブリックの場合は`client_id`リクエストパラメータが必須となります。一方、クライアントタイプがコンフィデンシャルでクライアント認証方式として`private_key_jwt`を用いる場合は`client_assertion`および`client_assertion_type`リクエストパラメータが必須となります。クライアント認証方式の詳細については『[OAuth 2.0クライアント認証](https://qiita.com/TakahikoKawasaki/items/63ed4a9d8d6e5109e401)』を参照してください。

下記はクライアントタイプがパブリックの場合のトークンリクエストの例です。

```text theme={null}
POST https://trial.authlete.net/api/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded

client_id=app_1&grant_type=authorization_code&code={{authorization_code}}&redirect_uri=https://nextdev-api.authlete.net/api/mock/redirection
```

<Note>
  この例を実際に試す場合は`{{authorization_code}}`を実際の値
  (認可リクエストの結果得られた認可コードの値) で置き換えてください。
</Note>

<br />

### アプリ1のトークンレスポンス

Native SSOに準拠するトークンレスポンスには、アクセストークンやリフレッシュトークン (任意)
に加えて、Native SSOに準拠するIDトークンおよびデバイスシークレットが含まれます。
デバイスシークレットは`device_secret`プロパティの値として返却されます。

```json theme={null}
{
  "access_token": "R28TIqhCydVvH2x2a3XsOzJykFEs7yFotO4ip-a2MbY",
  "token_type": "Bearer",
  "expires_in": 86400,
  "scope": "openid device_sso",
  "refresh_token": "lmlNXafSApRDAq7gZvy40ojya9bplgFSHczms46mTms",
  "id_token": "eyJraWQiOiJaWUdJT0hZdUE5SXBVaWpWd1FOdWwzbkU1MzZ4MUpTV0hpT2ZkUzdzYWRnIiwiYWxnIjoiRVMyNTYifQ.eyJpc3MiOiJodHRwczovL3RyaWFsLmF1dGhsZXRlLm5ldCIsInN1YiI6IjEwMDQiLCJhdWQiOlsiYXBwXzEiXSwiZXhwIjoxNzQ2NDM3MTE5LCJpYXQiOjE3NDYzNTA3MTksImF1dGhfdGltZSI6MTc0NjM1MDY3MiwiZHNfaGFzaCI6IlhrYmdHQ1JKUTFOQUhuS25NbjhKMFhIS25fOEVNenhCOWFRdUZITk0ycDQiLCJzaWQiOiJub2RlMDM4Y2F0N2ozMDhzZzE4MjhtMXNnMmRleGwzIn0.JAYlCEbGhjJwpgSZ4lUNaXkWD2ICeDs6FCBd3bKRvKPhrrGZKUAZDRij_Bmn_AF7DyTQS5ALHl82cJqjaLCcIw",
  "device_secret": "b81d5ae9-9f85-4c6d-8658-1a36ffa42c83"
}
```

トークンレスポンスに含まれる`id_token`プロパティの値がIDトークンです。

```text theme={null}
eyJraWQiOiJaWUdJT0hZdUE5SXBVaWpWd1FOdWwzbkU1MzZ4MUpTV0hpT2ZkUzdzYWRnIiwiYWxnIjoiRVMyNTYifQ.eyJpc3MiOiJodHRwczovL3RyaWFsLmF1dGhsZXRlLm5ldCIsInN1YiI6IjEwMDQiLCJhdWQiOlsiYXBwXzEiXSwiZXhwIjoxNzQ2NDM3MTE5LCJpYXQiOjE3NDYzNTA3MTksImF1dGhfdGltZSI6MTc0NjM1MDY3MiwiZHNfaGFzaCI6IlhrYmdHQ1JKUTFOQUhuS25NbjhKMFhIS25fOEVNenhCOWFRdUZITk0ycDQiLCJzaWQiOiJub2RlMDM4Y2F0N2ozMDhzZzE4MjhtMXNnMmRleGwzIn0.JAYlCEbGhjJwpgSZ4lUNaXkWD2ICeDs6FCBd3bKRvKPhrrGZKUAZDRij_Bmn_AF7DyTQS5ALHl82cJqjaLCcIw
```

<br />

このIDトークンのペイロード部をbase64urlでデコードすると次のようになります。Native SSO仕様に準拠するIDトークンには`ds_hash`クレームと`sid`クレームが含まれます。

```json theme={null}
{
  "iss": "https://trial.authlete.net",
  "sub": "1004",
  "aud": [
    "app_1"
  ],
  "exp": 1746437119,
  "iat": 1746350719,
  "auth_time": 1746350672,
  "ds_hash": "XkbgGCRJQ1NAHnKnMn8J0XHKn_8EMzxB9aQuFHNM2p4",
  "sid": "node038cat7j308sg1828m1sg2dexl3"
}
```

`ds_hash`クレームはデバイスシークレットのハッシュ値です。ハッシュ値をどのように計算するかは実装依存とされていますが、`ds_hash`クレームにより、IDトークンとデバイスシークレットを関連付けることができます。

`sid`クレームはユーザの認証セッションを一意に特定する文字列です。いわゆるセッションIDです。

アプリケーションは、取得したIDトークンとデバイスシークレットを、Native SSOで連携したい他のアプリケーション群がアクセスできる場所に保存します。

<br />

### アプリ2のトークンリクエスト

Native SSO仕様は、[RFC 8693: OAuth 2.0 Token Exchange](https://www.rfc-editor.org/rfc/rfc8693.html)仕様を拡張し、Native
SSOを実現するための要求事項を追加しています。
二つ目のアプリケーションは、一つ目のアプリケーションが保存したIDトークンとデバイスシークレットを取り出し、それらを用いてNative
SSO仕様に準拠するトークン交換リクエストを組み立てます。

<Note>
  RFC 8693については、解説記事『<a href="https://www.authlete.com/ja/developers/token_exchange/">RFC 8693 OAuth 2.0トークン交換</a>』もご参照ください。
</Note>

Native SSO仕様に準拠するトークン交換リクエストのリクエストパラメータは次の通りです。

| パラメータ                | 説明                                                                                                                                                                                                |
| :------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `grant_type`         | グラントタイプです。トークン交換リクエストでは`urn:ietf:params:oauth:grant-type:token-exchange`を指定します。                                                                                                                   |
| `audience`           | トークン交換リクエストにより発行されるトークンを使用する対象です。Native SSO用のトークン交換リクエストではOpenIDプロバイダの識別子を指定します。                                                                                                                  |
| `subject_token`      | 誰のためのトークン交換リクエストであるかを示すトークンです。Native SSO用のトークン交換リクエストではIDトークンを指定します。                                                                                                                              |
| `subject_token_type` | `subject_token`のタイプを示す識別子です。Native SSO用のトークン交換リクエストでは`subject_token`は常にIDトークンなので、`subject_token_type`パラメータの値には`urn:ietf:params:oauth:token-type:id_token`を指定します。                                  |
| `actor_token`        | トークン交換リクエストの実行者を表すトークンです。Native SSO用のトークン交換リクエストではデバイスシークレットを指定します。                                                                                                                               |
| `actor_token_type`   | `actor_token`のタイプを示す識別子です。Native SSO用のトークン交換リクエストでは`actor_token`は常にデバイスシークレットなので、`actor_token_type`パラメータの値には`urn:openid:params:token-type:device-secret`を指定します。この値はNative SSO仕様が新たに定義するトークンタイプです。 |
| `scope`              | トークン交換リクエストの結果発行されるアクセストークンに紐付けるスコープです。このパラメータは任意です。                                                                                                                                              |

また、上記に加え、アプリケーションのクライアントタイプ ([RFC 6749 Section 2.1](https://www.rfc-editor.org/rfc/rfc6749.html#section-2.1))
がクレデンシャルかパブリックか、また、クライアントタイプがコンフィデンシャルの場合にどのクライアント認証方式を用いるかにより、追加のパラメータが必要になります。

例えば、クライアントタイプがパブリックの場合は`client_id`リクエストパラメータが必須となります。一方、クライアントタイプがコンフィデンシャルでクライアント認証方式として`private_key_jwt`を用いる場合は`client_assertion`および`client_assertion_type`リクエストパラメータが必須となります。クライアント認証方式の詳細については『[OAuth 2.0クライアント認証](https://qiita.com/TakahikoKawasaki/items/63ed4a9d8d6e5109e401)』を参照してください。

下記はクライアントタイプがパブリックの場合のトークン交換リクエストの例です。

```text theme={null}
POST https://trial.authlete.net/api/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded

client_id=app_2&grant_type=urn:ietf:params:oauth:grant-type:token-exchange&audience=https://trial.authlete.net&subject_token={{id_token}}&subject_token_type=urn:ietf:params:oauth:token-type:id_token&actor_token={{device_secret}}&actor_token_type=urn:openid:params:token-type:device-secret&scope=openid
```

<Note>
  この例を実際に試す場合は`{{id_token}}`と`{{device_secre}}`を実際の値で置き換えてください。
</Note>

<br />

### アプリ2のトークンレスポンス

トークン交換リクエストに対するレスポンスは、認可コードフローのトークンレスポンスとほぼ同じです。
唯一の違いは`issued_token_type`プロパティが含まれていることです。
Native SSOの場合、`issued_token_type`の値は`urn:ietf:params:oauth:token-type:access_token`です。

```json theme={null}
{
  "access_token": "rH9115-g83z9zIiCJ1mzIe8mza3bX4NaBTWmGs5qqow",
  "token_type": "Bearer",
  "expires_in": 86400,
  "scope": "openid",
  "refresh_token": "_F7NMCU1ny8DQ-3Pru_owgII52gIew0T6wuWKeIrfL4",
  "id_token": "eyJraWQiOiJaWUdJT0hZdUE5SXBVaWpWd1FOdWwzbkU1MzZ4MUpTV0hpT2ZkUzdzYWRnIiwiYWxnIjoiRVMyNTYifQ.eyJpc3MiOiJodHRwczovL3RyaWFsLmF1dGhsZXRlLm5ldCIsInN1YiI6IjEwMDQiLCJhdWQiOlsiYXBwXzIiXSwiZXhwIjoxNzQ2NDM4MzUxLCJpYXQiOjE3NDYzNTE5NTEsImRzX2hhc2giOiJYa2JnR0NSSlExTkFIbktuTW44SjBYSEtuXzhFTXp4QjlhUXVGSE5NMnA0Iiwic2lkIjoibm9kZTAzOGNhdDdqMzA4c2cxODI4bTFzZzJkZXhsMyJ9.8jNNF5mpeHnbqp1FTK_1adR8FlgPmHK9_rwUzaz-o5P7RMyaelBaSj74IhxHY6wbCJeD0n_N14h8vD8zWYh-8w",
  "device_secret": "b81d5ae9-9f85-4c6d-8658-1a36ffa42c83",
  "issued_token_type": "urn:ietf:params:oauth:token-type:access_token"
}
```

上記のレスポンス例からIDトークンを取り出し、

```text theme={null}
eyJraWQiOiJaWUdJT0hZdUE5SXBVaWpWd1FOdWwzbkU1MzZ4MUpTV0hpT2ZkUzdzYWRnIiwiYWxnIjoiRVMyNTYifQ.eyJpc3MiOiJodHRwczovL3RyaWFsLmF1dGhsZXRlLm5ldCIsInN1YiI6IjEwMDQiLCJhdWQiOlsiYXBwXzIiXSwiZXhwIjoxNzQ2NDM4MzUxLCJpYXQiOjE3NDYzNTE5NTEsImRzX2hhc2giOiJYa2JnR0NSSlExTkFIbktuTW44SjBYSEtuXzhFTXp4QjlhUXVGSE5NMnA0Iiwic2lkIjoibm9kZTAzOGNhdDdqMzA4c2cxODI4bTFzZzJkZXhsMyJ9.8jNNF5mpeHnbqp1FTK_1adR8FlgPmHK9_rwUzaz-o5P7RMyaelBaSj74IhxHY6wbCJeD0n_N14h8vD8zWYh-8w
```

<br />

ペイロード部をbase64urlでデコードすると次のようになります。

```json theme={null}
{
  "iss": "https://trial.authlete.net",
  "sub": "1004",
  "aud": [
    "app_2"
  ],
  "exp": 1746438351,
  "iat": 1746351951,
  "ds_hash": "XkbgGCRJQ1NAHnKnMn8J0XHKn_8EMzxB9aQuFHNM2p4",
  "sid": "node038cat7j308sg1828m1sg2dexl3"
}
```

`ds_hash`クレームと`sid`クレームの値は一つ目のアプリケーションが受け取ったIDトークンと同じですが、`aud`クレームの値は異なっています。このIDトークンでは、二つ目のアプリケーションの識別子である`app_2`が`aud`配列に含まれています。

<br />

## 利用設定

Authleteでは、バージョン3.0以降でNative SSOをサポートします。

<br />

### サービス設定

<br />

#### nativeSsoSupportedプロパティ

Native SSOをサポートするかどうかを示す新しい真偽値プロパティ`nativeSsoSupported`がサービスに追加されました。
このプロパティのデフォルト値は`false`なので、Native SSOを利用する場合は明示的に`true`に設定する必要があります。

`nativeSsoSupported`プロパティは、Native SSO仕様が定義するサーバメタデータの`native_sso_supported`に対応しています。
`nativeSsoSupported`が`true`に設定されている場合、Authleteの`/service/configuration`APIが生成するディスカバリ文書
([OpenID Connect Discovery 1.0](https://openid.net/specs/openid-connect-discovery-1_0.html))
に次のエントリが追加されます。

```json theme={null}
"native_sso_supported": true
```

`nativeSsoSupported`が`false`の場合、AuthleteはNative SSOについて何も知らないかのような動作をします。
例えば、`device_sso`スコープは特別な意味を持たなくなり、トークンタイプ`urn:openid:params:token-type:device-secret`は未知のトークンタイプとして扱われます
(`actor_token_type`の値として指定するとエラーになります)。

<br />

#### device\_ssoスコープ

Native SSOのため、認可サーバが`device_sso`スコープをサポートしている必要があります。
明示的に`device_sso`スコープを追加登録してください。

OAuth 2.0仕様の要請により、認可サーバは未知のスコープをエラー扱いせず、単に無視します。
そのため、`device_sso`スコープを登録し忘れていると、認可リクエストに`device_sso`スコープを含めていても、何の警告もなくNative
SSO用の処理は実行されないので注意してください。

<br />

#### サービスのグラントタイプ

サービスがサポートするグラントタイプに`TOKEN_CHANGE`を追加してください。

また、AuthleteにはToken Exchangeに関する設定項目が複数存在するので、それらが意図通りの設定になっているか確認してください。特に次の二つには留意してください。

| プロパティ                                    | 説明                                                                                                                                                      |
| :--------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `tokenExchangeByConfidentialClientsOnly` | トークン交換リクエストを実行できるクライアントをコンフィデンシャルクライアントのみに限定するかどうかを示す真偽値プロパティです。この値が`true`に設定されていると、パブリッククライアントからのトークン交換リクエストは受け付けられなくなります。                             |
| `tokenExchangeByPermittedClientsOnly`    | トークン交換リクエストを実行できるクライアントを事前に許可を与えられたクライアントのみに限定するかどうかを示す真偽値プロパティです。この値が`true`に設定されていると、トークン交換リクエストを実行することを明示的に許可されていないクライアントからのトークン交換リクエストは受け付けられなくなります。 |

<br />

### クライアント設定

<br />

#### device\_ssoスコープ

設定により、クライアントが要求できるスコープが限定されている場合があります。
そのような設定になっている場合、`device_sso`スコープを要求可能なスコープのリストに追加してください。

<br />

#### クライアントのグラントタイプ

クライアントが利用する可能性のあるグラントタイプのリストに`TOKEN_EXCHANGE`を追加してください。

また、サービスの`tokenExchangeByPermittedClientsOnly`プロパティが`true`に設定されていると、明示的に許可を与えられていないクライアントからのトークン交換リクエストは拒否されてしまいます。サービスの設定がそのようになっている場合、クライアントの`extension.tokenExchangePermitted`プロパティに`true`を設定する必要があります。

<br />

## OpenIDプロバイダ実装

### 認証セッションとデバイスシークレットの管理

ユーザ認証・ユーザ管理とOAuth 2.0/OpenID Connectプロトコル処理を完全に分離し、後者のみの機能を提供するのがAuthleteの大きな特長となっており、市場から大きな支持を得ています。
この独特のアーキテクチャのため、Authleteはユーザの認証セッションの管理を全くおこないません。
そのため、認証セッションの管理はAuthlete利用者 (OpenIDプロバイダ実装者) がおこないます。
Native SSOに準拠するIDトークンにはセッションIDの値を`sid`クレームの値として埋め込む必要がありますが、その値はOpenIDプロバイダが管理することになります。

また、Authleteアーキテクチャの別の特長に、「バックエンドで動き、クライアントアプリケーションと直接通信をしない」、というものがあります。
結果として、Authleteはクライアントアプリケーションが動いているデバイスの情報を直接知ることはできません。
Native SSOに準拠するOpenIDプロバイダはデバイス情報に紐付くデバイスシークレットを発行できなければなりませんが、Authleteはそれができないということです。
デバイスシークレットの生成・管理もOpenIDプロバイダ側の責任範囲となります。

一方で、Native SSOに準拠するIDトークンやトークンレスポンスの生成はAuthleteがおこないます。
これらの生成に必要なセッションID、デバイスシークレット (およびデバイスシークレットハッシュ)
は、Authlete APIのリクエストパラメータを介してOpenIDプロバイダからAuthleteに渡すことになります。

<img alt="" class="mx-auto d-block" src="https://mintcdn.com/authlete/WGQFc11Y4wUplGrc/ja/protocols-and-flows/advanced-flows/nativesso_token_management.png?fit=max&auto=format&n=WGQFc11Y4wUplGrc&q=85&s=e6f138fae4e9fabb83980c54caceaf8f" width="1920" height="1080" data-path="ja/protocols-and-flows/advanced-flows/nativesso_token_management.png" />

<br />

### 認可エンドポイントの実装

認可リクエストがNative SSOを要求していると判断された場合、具体的には次の条件が全て満たされた場合、Authleteの`/auth/authorization`APIからのレスポンスには、`nativeSsoRequested`という真偽値プロパティが値`true`で含まれます。

|     | 条件                                                                                      |
| :-: | :-------------------------------------------------------------------------------------- |
|  1  | サービスがNative SSOをサポートしている。 (`Service.nativeSsoSupported`の値が`true`と設定されている。)              |
|  2  | サービスが`openid`スコープと`device_sso`スコープをサポートしている。                                            |
|  3  | クライアントが`openid`スコープと`device_sso`スコープを要求することを許可されている。(Requestable Scopes機能による制限を受けていない。) |
|  4  | 認可リクエストの`scope`に`openid`と`device_sso`が含まれている。                                           |
|  5  | サービスが認可コードフローをサポートしている。(`Service.supportedGrantTypes`に`AUTHORIZATION_CODE`が含まれている。)     |
|  6  | クライアントが認可コードフローを使用すると宣言している。(`Client.grantTypes`に`AUTHORIZATION_CODE`が含まれている。)          |
|  7  | 認可リクエストの`response_type`に`code`が含まれている。                                                  |
|  8  | サービスがそのレスポンスタイプをサポートしている。(`Service.supportedResponseTypes`に当該レスポンスタイプが含まれている。)          |
|  9  | クライアントがそのレスポンスタイプを使用すると宣言している。(`Client.responseTypes`に当該レスポンスタイプが含まれている。)               |

この表の条件は複雑に見えますが、サービスやクライアントの設定条件を除いて認可リクエストの条件だけに限って言えば、「`scope`に`openid`と`device_sso`が含まれていて`response_type`に`code`が含まれている」場合、Native SSOを要求していると判断されます。

`nativeSsoRequested`プロパティの値が`true`の場合、認可エンドポイントの実装は`/auth/authorization/issue`APIを呼ぶ際に`sessionId`リクエストパラメータを含めなければなりません。
その値には、現在のユーザの認証セッションを表す識別子 (いわゆるセッションID) を指定します。ここで指定された値は、IDトークンの`sid`クレームの値として用いられます。

<img alt="" class="mx-auto d-block" src="https://mintcdn.com/authlete/WGQFc11Y4wUplGrc/ja/protocols-and-flows/advanced-flows/nativesso_authorization_endpoint.png?fit=max&auto=format&n=WGQFc11Y4wUplGrc&q=85&s=f7e18d5024e4ed0dafee7a7dbfa021bc" width="1920" height="1080" data-path="ja/protocols-and-flows/advanced-flows/nativesso_authorization_endpoint.png" />

認可エンドポイントの実装は、実際のセッションIDの値を`sessionId`パラメータの値としてAuthleteに渡してもよいですし、何らかの変換を加えてからAuthleteに渡してもかまいません。
ただし、あまり長い文字列は使えません。おおむね150文字程度が上限となります。`sessionId`として渡された文字列を暗号化してbase64urlエンコードした結果得られる文字列の長さが255を超えるとエラーになります (暗号化のロジックは非公開)。

`/auth/authorization` APIのレスポンス内の`nativeSsoRequested`の値が`true`か`false`かに関係なく、常に`/auth/authorization/issue`APIに`sessionId`パラメータを渡すという実装でもかまいません。認可リクエストがNative SSOを要求していない場合、たとえ`sessionId`パラメータが指定されていたとしても、AuthleteはIDトークンに`sid`クレームを埋め込みません。

<br />

### トークンエンドポイントの実装 (検証)

トークンリクエストがNative SSO用のものだと判断された場合、具体的には次の条件セットのいずれかが満たされた場合、Authleteの`/auth/token`APIに含まれる`action`プロパティの値は`NATIVE_SSO`になります。

|     | 条件セット1: 認可コードフロー                                                                        |
| :-: | :-------------------------------------------------------------------------------------- |
|  1  | サービスがNative SSOをサポートしている。 (`Service.nativeSsoSupported`の値が`true`と設定されている。)              |
|  2  | サービスが`openid`スコープと`device_sso`スコープをサポートしている。                                            |
|  3  | クライアントが`openid`スコープと`device_sso`スコープを要求することを許可されている。(Requestable Scopes機能による制限を受けていない。) |
|  4  | 対応する認可リクエストの`scope`に`openid`と`device_sso`が含まれている。                                       |
|  5  | サービスが認可コードフローをサポートしている。(`Service.supportedGrantTypes`に`AUTHORIZATION_CODE`が含まれている。)     |
|  6  | クライアントが認可コードフローを使用すると宣言している。(`Client.grantTypes`に`AUTHORIZATION_CODE`が含まれている。)          |
|  7  | `grant_type`パラメータの値が`authorization_code`である。                                            |

|     | 条件セット2: リフレッシュトークンフロー                                                                      |
| :-: | :----------------------------------------------------------------------------------------- |
|  1  | サービスがNative SSOをサポートしている。 (`Service.nativeSsoSupported`の値が`true`と設定されている。)                 |
|  2  | サービスが`device_sso`スコープをサポートしている。                                                            |
|  3  | クライアントが`device_sso`スコープを要求することを許可されている。(Requestable Scopes機能による制限を受けていない。)                 |
|  4  | サービスがリフレッシュトークンフローをサポートしている。(`Service.supportedGrantTypes`に`REFRESH_TOKEN`が含まれている。)        |
|  5  | クライアントがリフレッシュトークンフローを使用すると宣言している。(`Client.grantTypes`に`REFRESH_TOKEN`が含まれている。)             |
|  6  | `grant_type`パラメータの値が`refresh_token`である。                                                    |
|  7  | トークンリクエストの`scope`パラメータの指定によりスコープの範囲が狭められたとしても、`device_sso`スコープが依然としてカバーされている。              |
|  8  | 提示されたリフレッシュトークンがユーザ認証セッションに紐付けられている。(実質的にNative SSOに準拠した認可コードフローにより生成されたリフレッシュトークンしか使えない。) |

|     | 条件セット3: トークン交換フロー                                                                          |
| :-: | :----------------------------------------------------------------------------------------- |
|  1  | サービスがNative SSOをサポートしている。 (`Service.nativeSsoSupported`の値が`true`と設定されている。)                 |
|  2  | サービスがトークン交換フローをサポートしている。(`Service.supportedGrantTypes`に`TOKEN_EXCHANGE`が含まれている。)           |
|  3  | クライアントがトークン交換フローを使用すると宣言している。(`Client.grantTypes`に`TOKEN_EXCHANGE`が含まれている。)                |
|  4  | サービスのトークン交換フローの各種設定 (`tokenExchangeByConfidentialClientsOnly`等) がクライアントのトークン交換リクエストを拒絶しない。 |
|  5  | `grant_type`パラメータの値が`urn:ietf:params:oauth:grant-type:token-exchange`である。                  |
|  6  | `actor_token_type`パラメータの値が`urn:openid:params:token-type:device-secret`である。                 |

上記の条件群は複雑に見えますが、要は、「Native SSOに準拠したIDトークンとトークンレスポンスを生成する必要がある」とAuthleteが判断した場合、`action`の値が`NATIVE_SSO`になります。

`action`の値が`NATIVE_SSO`の場合、トークンエンドポイントの実装はトークンリクエストの処理を完了させるために`/nativesso`APIをコールする必要があります。しかし、それに先立ち、セッションIDやデバイスシークレットの検証、必要に応じてデバイスシークレットの生成を行わなければなりません。

さらに、`/auth/token`APIのレスポンスに`nonce`と`s_hash`を含むJSON文字列として`additionalClaims`フィールドが含まれている場合は、その値を`claims`パラメータにそのまま引き渡してください。

```json theme={null}
{
  "accessToken": "{{accessToken}}",
  "deviceSecret": "{{deviceSecret}}",
  "claims": "{{additionalClaims}}"
}
```

`additionalClaims`が返ってきた場合、`claims`パラメータにはそのJSON文字列をそのまま指定してください。これらのクレームはIDトークン検証に不可欠であり、`/nativesso`APIが生成するIDトークンに埋め込まれます。

<img alt="" class="mx-auto d-block" src="https://mintcdn.com/authlete/WGQFc11Y4wUplGrc/ja/protocols-and-flows/advanced-flows/nativesso_token_endpoint.png?fit=max&auto=format&n=WGQFc11Y4wUplGrc&q=85&s=a71fe824549ce64dd87cf38e55b2d0b9" width="1920" height="1080" data-path="ja/protocols-and-flows/advanced-flows/nativesso_token_endpoint.png" />

<br />

#### セッションIDの検証

`/auth/token`APIからのレスポンスに含まれる`action`の値が`NATIVE_SSO`の場合、そのレスポンスには`sessionId`パラメータが含まれ、その値はユーザ認証セッションを表す値、すなわちセッションIDです。トークンエンドポイントの実装は、このセッションIDが依然として有効かどうかを確認しなければなりません。無効の場合は`/nativesso`APIを呼ばず、代わりに`invalid_grant`エラーを示すトークンレスポンスを生成してクライアントに返却してください。

```http theme={null}
HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-store

{
    "error": "invalid_grant",
    "error_description": "The session ID is no longer valid."
}
```

`/auth/token`APIのレスポンスに含まれる`sessionId`の値は、元々は`/auth/authorization/issue`APIの`sessionId`リクエストパラメータの値としてOpenIDプロバイダからAuthleteに渡されたものです。
AuthleteはそのセッションIDが有効かどうか判断しない (判断できない) ので、セッションIDの検証はOpenIDプロバイダ側で実施する必要があります。

トークンリクエストが認可コードフローまたはリフレッシュトークンフローのものである場合、`sessionId`の値は、認可コードまたはリフレッシュトークンに紐付くセッションIDです。

一方、トークンリクエストがトークン交換フローのものである場合、`sessionId`の値は`subject_token`パラメータの値として提示されたIDトークンの`sid`クレームの値です。

<img alt="" class="mx-auto d-block" src="https://mintcdn.com/authlete/WGQFc11Y4wUplGrc/ja/protocols-and-flows/advanced-flows/nativesso_session_id_in_exchange.png?fit=max&auto=format&n=WGQFc11Y4wUplGrc&q=85&s=de6c8336807d89048b8daf35f6889287" width="1920" height="1080" data-path="ja/protocols-and-flows/advanced-flows/nativesso_session_id_in_exchange.png" />

<br />

#### デバイスシークレットの検証 (認可コードフローとリフレッシュトークンフロー)

`action`の値が`NATIVE_SSO`で、当該トークンリクエストが認可コードフローもしくはリフレッシュトークンフローのものである場合
(`grantType`が`AUTHORIZATION_CODE`または`REFRESH_TOKEN`の場合)、`/auth/token`APIのレスポンスには`deviceSecret`パラメータが含まれている可能性があります。
その値は、トークンリクエストの`device_secret`リクエストパラメータの値です。
このリクエストパラメータ自体はオプショナルです。

<img alt="" class="mx-auto d-block" src="https://mintcdn.com/authlete/WGQFc11Y4wUplGrc/ja/protocols-and-flows/advanced-flows/nativesso_device_secret_in_code_or_refresh.png?fit=max&auto=format&n=WGQFc11Y4wUplGrc&q=85&s=1d75122dfcdcd843f67bb1d545cace99" width="1920" height="1080" data-path="ja/protocols-and-flows/advanced-flows/nativesso_device_secret_in_code_or_refresh.png" />

`deviceSecret`の値が`null`でない場合、その値が有効かどうかを検証してください。
もし有効であれば、その値をそのまま`/nativesso`APIに渡してください。
一方、`deviceSecret`の値が存在しない、または無効の場合、新しいデバイスシークレットを生成し、その値を`/nativesso`APIに渡してください。

<br />

#### デバイスシークレットの検証 (トークン交換フロー)

`action`の値が`NATIVE_SSO`で、当該トークンリクエストがトークン交換フローのものである場合
(`grantType`が`TOKEN_EXCHAGE`の場合)、`/auth/token`APIからのレスポンスには必ず`deviceSecret`パラメータと`deviceSecretHash`パラメータが含まれます。

`deviceSecret`の値は、トークンリクエストの`actor_token`パラメータの値として指定されたデバイスシークレットです。
`deviceSecretHash`の値は、トークンリクエストの`subject_token`パラメータの値として指定されたIDトークンに含まれる`ds_hash`クレームの値です。

<img alt="" class="mx-auto d-block" src="https://mintcdn.com/authlete/WGQFc11Y4wUplGrc/ja/protocols-and-flows/advanced-flows/nativesso_device_secret_in_exchange.png?fit=max&auto=format&n=WGQFc11Y4wUplGrc&q=85&s=7f2f577f63352122722a158a020836db" width="1920" height="1080" data-path="ja/protocols-and-flows/advanced-flows/nativesso_device_secret_in_exchange.png" />

トークンエンドポイントの実装では、デバイスシークレットハッシュがデバイスシークレットに対応するものかどうか確認してください。
対応するものでない場合は`/nativesso`APIを呼ばず、代わりに`invalid_grant`エラーを示すトークンレスポンスを生成してクライアントに返却してください。

```http theme={null}
HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-store

{
    "error": "invalid_grant",
    "error_description": "The device secret hash in the subject token does not correspond to the device secret."
}
```

<br />

### トークンエンドポイントの実装 (nativesso APIコール)

セッションIDの検証およびデバイスシークレットの検証または生成の完了後、Native SSOに準拠するIDトークンとトークンレスポンスを生成するため、Authleteの`/nativesso`APIをコールしてください。

<br />

#### nativessoリクエスト

`/nativesso`APIは、`application/json`または`application/x-www-form-urlencoded`形式のHTTP POSTリクエストを受け付けます。リクエストパラメータは下表の通りです。

| パラメータ              |  要否 | 説明                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| :----------------- | :-: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `accessToken`      |  必須 | `/auth/token`APIのレスポンスに`jwtAccessToken`が含まれていればその値を、含まれていなければ`accessToken`の値を指定します。指定された値は`/nativesso`APIが用意するトークンレスポンスの`access_token`プロパティの値になります。                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `refreshToken`     |  任意 | `/auth/token`APIのレスポンスに含まれる`refreshToken`の値を指定します。指定された値は`/nativesso`APIが用意するトークンレスポンスの`refresh_token`プロパティの値になります。                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `deviceSecret`     |  必須 | `/auth/token`APIのレスポンスに`deviceSecret`が含まれていればその値を、含まれていなければ新しいデバイスシークレットを生成してその値を指定します。指定された値は`/nativesso`APIが用意するトークンレスポンスの`device_secret`プロパティの値になります。                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `deviceSecretHash` |  推奨 | デバイスシークレットのハッシュ値を指定します。デバイスシークレットからハッシュ値を求めるロジックはOpenIDプロバイダの実装依存です。このパラメータが省略された場合、`/nativesso`APIの実装は`deviceSecret`パラメータの値のSHA-256ハッシュを計算し、そのハッシュ値をbase64urlエンコードしたものをデバイスシークレットハッシュとします。`deviceSecretHash`パラメータで指定された値、または`/nativesso`APIが生成した値は、`/nativesso`APIが生成するIDトークンに`ds_hash`クレームの値として埋め込まれます。                                                                                                                                                                                                                                                                                                                                                                                                      |
| `sub`              |  任意 | `/nativesso`APIが生成するIDトークンの`sub`クレームの値です。このパラメータが省略された場合、`accessToken`パラメータで指定されたアクセストークンに紐付くサブジェクトが`sub`クレームの値として用いられます。<br /><br />IDトークンの生成を伴うAuthlete APIには`sub`リクエストパラメータがあるため、`/nativesso`APIもこのリクエストパラメータを受け付けます。しかしながら、Native SSOの文脈でアクセストークンのサブジェクトと異なる値を`sub`クレームの値に用いると、意図しない不整合を起こす可能性があるので、この`sub`パラメータを使う際は慎重におこなってください。<br /><br />この`sub`パラメータの値に関わらず、Native SSOのトークン交換フローでアクセストークンを新規作成する際、Authleteはサブジェクトトークン (過去の`/nativesso`APIコールにより生成されたIDトークン) の`sub`クレームの値をアクセストークンに紐付くサブジェクトとして設定します。`/auth/token`APIがレスポンスを返した時点で既にアクセストークンの生成は完了しており、`/nativesso`APIの`sub`パラメータではアクセストークンに紐付くサブジェクトを変更することはできません。                                                                                   |
| `claims`           |  任意 | IDトークンに埋め込むクレームを指定します。値はJSONオブジェクトを表す文字列でなければなりません。<br /><br />Native SSOでは、このパラメータには次の2つの役割があります。<br />1. **プロトコル上必須のクレームの伝搬:** `/auth/token`APIのレスポンスに`additionalClaims`フィールド (`nonce`と`s_hash`を含む) が含まれている場合は、その値を変換せずに`claims`パラメータに指定してください。これらのクレームはIDトークン検証に必須です。<br />2. **カスタムクレームの追加:** さらにカスタムクレームを埋め込みたい場合は、`additionalClaims`のJSONとマージした結果をこのパラメータに指定してください。<br /><br />まとめると:<br />- `additionalClaims`がありカスタムクレームが不要な場合は、その値をそのまま`claims`に指定します。<br />- `additionalClaims`がありカスタムクレームも必要な場合は、両方のJSONをマージして`claims`に指定します。<br />- `additionalClaims`が返されずカスタムクレームも不要な場合は、`claims`パラメータを省略するか空のJSONオブジェクトを渡せます。<br />- `additionalClaims`が返されないがカスタムクレームを追加したい場合は、カスタムクレームのJSONを`claims`として指定します。 |
| `idtHeaderParams`  |  任意 | IDトークンのJWSヘッダに埋め込む追加のパラメータ群を指定します。形式はJSONオブジェクトを表す文字列でなければなりません。                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `idTokenAudType`   |  任意 | IDトークンの`aud`クレームの形式を指定します。`array`を指定した場合は`aud`クレームの値はJSON配列となり、`string`を指定した場合はJSON文字列となります。この`idTokenAudType`パラメータを省略した場合、サービスの`idTokenAudType`プロパティの設定が参照されます。サービスの当プロパティが設定されていない場合、`aud`クレームの値はJSON配列となります。                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |

<br />

#### nativessoレスポンス

`/nativesso`APIからのレスポンスのメッセージボディの形式はJSONです。他の多くのAuthlete APIと同様に、`/nativesso`APIのレスポンスにも`action`プロパティが含まれています。トークンエンドポイントの実装では、この`action`の値に従ってトークンレスポンスを組み立ててください。

`action`が`OK`の場合、`/nativesso`APIの処理が全て成功裡に終わったことを示します。
このとき、トークンエンドポイントの実装はクライアントに成功応答 (`200 OK`) を返すようにします。
`/nativesso`APIからのレスポンスに含まれる`responseContent`プロパティの値は、トークンレスポンスのメッセージボディとしてそのまま使えます。
このため、成功応答は次のように構築できます。

```http theme={null}
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store

(responseContentの値をここに置く)
```

<img alt="" class="mx-auto d-block" src="https://mintcdn.com/authlete/WGQFc11Y4wUplGrc/ja/protocols-and-flows/advanced-flows/nativesso_api.png?fit=max&auto=format&n=WGQFc11Y4wUplGrc&q=85&s=0a2f49a05455e489cab7d165bcbf7f38" width="1920" height="1080" data-path="ja/protocols-and-flows/advanced-flows/nativesso_api.png" />

`action`が`INTERNAL_SERVER_ERROR`の場合、Authlete側で何か問題が発生したことを意味します。
例えば、`accessToken`パラメータで指定されたアクセストークンをデータベースから取り出す際にデータベースエラーが発生した、といった問題です。
このとき、トークンエンドポイントの実装はクライアントにエラーレスポンスを返すべきです。
最も単純な実装では、`500 Internal Server Error`を返します。

```http theme={null}
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
Cache-Control: no-store

(responseContentの値をここに置く)
```

しかし、商用環境では、500エラーとは異なる抽象度の高い (エラーの内容を直接記述しない)
エラーを返したほうがよいかもしれません。

`action`が`CALLER_ERROR`の場合、API呼び出し側 (OpenIDプロバイダの実装) に問題があることを示しています。
例えば、必須パラメータである`accessToken`を含めなかった、といった問題です。
`CALLER_ERROR`が返された場合はOpenIDプロバイダの実装を見直してください。

<br />

### シングルログアウト

一回の操作で複数のアプリケーションからログアウトすることをシングルログアウト (SLO) と呼びます。
シングルサインオンと対となります。

Native SSO仕様はシングルログアウトを実現するための具体的なプロトコルを定義していません。
しかし、特定のセッションIDに紐付くアクセストークン・リフレッシュトークン群をまとめて削除することにより、シングルログアウトを実現できます。

Authleteは`/nativesso/logout`APIによりシングルログアウトの機能を提供します。
このAPIは`application/json`または`application/x-www-form-urlencoded`形式のHTTP
POSTリクエストを受け付けます。リクエストパラメータは`sessionId`一つのみです。
このリクエストパラメータに対象となるセッションIDを指定して`/nativesso/logout`APIを呼ぶと、そのセッションIDに紐付いたアクセストークン・リフレッシュトークンが全て削除されます。

```http theme={null}
POST https://{{authlete_api_server}}/api/{{service_id}}/nativesso/logout HTTP/1.1
Authorization: Bearer {{authlete_access_token}}
Content-Type: application/json

{
    "sessionId": "{{session_id}}"
}
```

```json theme={null}
{
  "action": "OK",
  "count": 2,
  "resultCode": "A503001",
  "resultMessage": "[A503001] The /nativesso/logout API call successfully deleted 2 access/refresh token record(s)."
}
```

`/nativesso/logout`は、指定されたセッションIDに紐付けられたアクセストークン・リフレッシュトークンが存在せずに結果として削除件数が0件だったとしてもエラーにはなりません。

```json theme={null}
{
  "action": "OK",
  "count": 0,
  "resultCode": "A503002",
  "resultMessage": "[A503002] The /nativesso/logout API call completed without deleting any access/refresh token records."
}
```

<br />

### サンプル実装

OpenIDプロバイダ側のNative SSOサンプル実装は[java-oauth-server](https://github.com/authlete/java-oauth-server)と[authlete-java-jaxrs](https://github.com/authlete/authlete-java-jaxrs)ライブラリに含まれています。どちらもJava言語によるオープンソース実装です。

以下はサンプル実装を読む際のヒントです。

* `/auth/authorization/issue`APIに渡すセッションIDは、<a href="https://github.com/authlete/java-oauth-server/blob/master/src/main/java/com/authlete/jaxrs/server/api/AuthorizationDecisionEndpoint.java">AuthorizationDecisionEndpoint</a>の中で`HttpServletRequest.getSession(false).getId()`を実行することで取得しています。ただし、この処理で得られるセッションはWebサーバとWebブラウザ間のHTTPセッションであるため、Native SSOの商用実装では別の仕組みを用いることになると思います。
* 取得したセッションIDは<a href="https://github.com/authlete/authlete-java-jaxrs/blob/master/src/main/java/com/authlete/jaxrs/spi/AuthorizationDecisionHandlerSpi.java">AuthorizationDecisionHandlerSpi</a>インターフェースを介して<a href="https://github.com/authlete/authlete-java-jaxrs/blob/master/src/main/java/com/authlete/jaxrs/AuthorizationDecisionHandler.java">AuthorizationDecisionHandler</a>に渡ります。
* AuthorizationDecisionHandlerは<a href="https://github.com/authlete/authlete-java-jaxrs/blob/master/src/main/java/com/authlete/jaxrs/AuthleteApiCaller.java">AuthleteApiCaller</a>の`callAuthorizationIssue`メソッドを介してAuthleteの`/auth/authorization/issue`APIを呼びます。
* `/auth/token`APIのレスポンスの`action`に基づくディスパッチ処理は<a href="https://github.com/authlete/authlete-java-jaxrs/blob/master/src/main/java/com/authlete/jaxrs/TokenRequestHandler.java">TokenRequestHandler</a>に書かれています。
* TokenRequestHandlerは、`action`が`NATIVE_SSO`の場合、<a href="https://github.com/authlete/authlete-java-jaxrs/blob/master/src/main/java/com/authlete/jaxrs/spi/TokenRequestHandlerSpi.java">TokenRequestHandlerSpi</a>インターフェースの`nativeSso`メソッドを呼びます。
* TokenRequestHandlerSpiインターフェースの実装である<a href="https://github.com/authlete/java-oauth-server/blob/master/src/main/java/com/authlete/jaxrs/server/api/TokenRequestHandlerSpiImpl.java">TokenRequestHandlerSpiImpl</a>は、`nativeSso`メソッドの中から<a href="https://github.com/authlete/java-oauth-server/blob/master/src/main/java/com/authlete/jaxrs/server/api/NativeSsoProcessor.java">NativeSsoProcessor</a>の`process`メソッドを呼びます。
* NativeSsoProcessorは、`/auth/token`APIからのレスポンスを表す<a href="https://github.com/authlete/authlete-java-common/blob/master/src/main/java/com/authlete/common/dto/TokenResponse.java">TokenResponse</a>クラスのインスタンスから、セッションID、デバイスシークレット、デバイスシークレットハッシュを取り出します。
* NativeSsoProcessorは、`retrieveDeviceId()`メソッドの中で、トークンエンドポイントにアクセスしてきたデバイスのデバイス識別子を取得します。ただし、サンプル実装ではこのメソッドの実装は空なので注意してください。
* NativeSsoProcessorは、`validateParameters`メソッドの中で、セッションID、デバイスシークレット、デバイスシークレットハッシュ、デバイス識別子の検証をおこなっています。
* セッションIDが有効かどうかのチェックは<a href="https://github.com/authlete/java-oauth-server/blob/master/src/main/java/com/authlete/jaxrs/server/core/SessionTracker.java">SessionTracker</a>の`isActiveSessionId(String)`メソッドを呼び出すことで行っています。SessionTrackerはHttpSessionListenerインターフェースを実装しており、セッションの生成と削除を監視しています。SessionTrackerは<a href="https://github.com/authlete/java-oauth-server/blob/master/src/main/webapp/WEB-INF/web.xml">web.xml</a>内でリスナーとして登録されています。
* デバイスシークレットとセッションID、デバイスシークレットハッシュ、デバイス識別子の関係は<a href="https://github.com/authlete/java-oauth-server/blob/master/src/main/java/com/authlete/jaxrs/server/nativesso/DeviceSecret.java">DeviceSecret</a>クラスで表現されています。
* DeviceSecretのインスタンス群は<a href="https://github.com/authlete/java-oauth-server/blob/master/src/main/java/com/authlete/jaxrs/server/nativesso/DeviceSecretManager.java">DeviceSecretManager</a>が管理しています。

<br />

## Authlete実装

### Authleteバージョン

Authleteでは、バージョン3.0以降でNative SSOをサポートします。

<br />

### Native SSOバージョン

Authlete Native SSOの最初のバージョンは[OpenID Connect Native SSO for Mobile Apps 1.0](https://openid.net/specs/openid-connect-native-sso-1_0.html)仕様のドラフト07に基づいて実装されました。そのため、古い版で使われていた`urn:x-oath:params:\*`識別子にかわって`urn:openid:params:\*`識別子が使われています。

<br />

### トークン交換リクエスト検証

トークン交換リクエストに`actor_token_type`パラメータが含まれており、その値が`urn:openid:params:token-type:device-secret`である場合、Authleteはトークン交換リクエスト固有のリクエストパラメータに対して下記の検証をおこないます。これらを全てパスした場合のみ、`/auth/token`APIのレスポンスの`action`が`NATIVE_SSO`になります。

1. `audience`パラメータが指定されており、その値がサービスのOpenIDプロバイダ識別子 (`Service.issuer`に設定されている値) と一致する。なお、`audience`パラメータは複数指定することが許されており、複数指定された場合は、いずれかの値が一致すればよい。
2. `requested_token_type`パラメータが指定されている場合、その値が既知のトークンタイプである。
3. `subject_token_type`パラメータが指定されており、その値が`urn:ietf:params:oauth:token-type:id_token`である。
4. `subject_token`パラメータが指定されており、その値 (IDトークン) が次の検証項目を全てパスする。
   * JWTとしてパースできる。
   * `exp`クレームを含んでおり、値の型が数値である。注: Native SSOの文脈では`exp`の値が現在時刻より未来であることを確認しない。これは期限切れのIDトークンをサブジェクトトークンとして使えることを意味する (参照: [id\_token usage](https://openid.net/specs/openid-connect-native-sso-1_0.html#name-id_token-usage))。
   * `iat`クレームを含んでおり、値が現在時刻または過去を示している。
   * `nbf`クレームを含む場合、値が現在時刻または過去を示している。
   * `iss`クレームを含んでおり、値がサービスのOpenIDプロバイダ識別子 (`Service.issuer`に設定されている値) と一致する。
   * `sub`クレームを含んでおり、値の型が文字列である。
   * `aud`クレームを含んでおり、値の型が文字列または配列である。また、配列の場合、一つ以上の要素を含み、全ての要素の型が文字列である。
   * `nonce`クレームを含む場合、値の型が文字列である。
   * `sid`クレームを含んでおり、値の型が文字列である。
   * `ds_hash`クレームを含んでおり、値の型が文字列である。
   * JWEではない。
   * 署名されている。
   * 署名検証に成功する。
5. `actor_token`パラメータが指定されている。

<Note>
  Native SSO仕様のドラフト07では、トークン交換リクエストに`scope`パラメータが含まれている場合、そのスコープリストに`openid`が含まれていなければならないと定めています。
  しかし、<a href="https://bitbucket.org/openid/connect/issues/2178">AB/Connect ISSUE 2178:
  \[Native SSO] the openid scope on token exchange</a>に記述されている理由のため、Authleteの現在の実装は`openid`が含まれるかどうかを確認しません。
</Note>

<br />

## 参考情報

* 仕様: [OpenID Connect Native SSO for Mobile Apps 1.0](https://openid.net/specs/openid-connect-native-sso-1_0.html)
* 仕様: [RFC 8693: OAuth 2.0 Token Exchange](https://www.rfc-editor.org/rfc/rfc8693.html)
* Native SSO仕様書ソースコード: [openid-connect-native-sso-1\_0.xml](https://bitbucket.org/openid/connect/src/master/openid-connect-native-sso-1_0.xml)
* Native SSO仕様Issue Tracker: [bitbucket.org/openid/connect/issues](https://bitbucket.org/openid/connect/issues?status=new\&status=open\&status=submitted\&component=Native%20SSO%20for%20Apps)
* 解説文書: [RFC 8693 OAuth 2.0 トークン交換](https://www.authlete.com/ja/developers/token_exchange/)
