コンセプト
OpenID Connect Native SSO for Mobile Apps 1.0 (以下Native SSO) は、同一ベンダーの管理下にある複数のモバイルアプリケーション間でシングルサインオン (SSO) を実現する仕組みを標準仕様として定義します。この仕様が実装されていると、ユーザはモバイルアプリケーション毎にユーザ認証を行う必要はなく、Native SSOで連携したアプリケーション群に対して一回のユーザ認証で済みます。
仕様概要
詳細に踏み込む前に、仕様の概要を紹介します。 まず、一つ目のアプリケーションが認可コードフローを用いて下記のトークン群を取得します。注目すべき点は、IDトークンがNative SSO仕様に準拠していること、および、デバイスシークレットという新しいタイプのトークンが含まれていることです。- アクセストークン
- リフレッシュトークン (任意)
- ID トークン (Native SSO仕様準拠)
- デバイスシークレット





デバイスシークレット
デバイスシークレットは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プロバイダは何らかの方法でデバイスの情報を取得し、デバイスシークレットと紐付けます。 そして、トークン交換リクエストを受け取った際、リクエストに含まれるデバイスシークレットに紐付くデバイスが、リクエスト送信元のデバイスと一致するかを確認します。
仕様詳細
アプリ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パラメータが必須となります。 |
この例は実際に動きます。認可ページが表示されたら、ログインIDとパスワードに
inga、ingaと入力してください。
ログイン済みになっていて、再度ログインIDフィールドとパスワードフィールドを表示させたい場合は、認可リクエストの末尾に&prompt=loginを追加してください。アプリ1のトークンリクエスト
上記の認可リクエストの結果得られた認可コードを用いてトークンリクエストを組み立てます。必要となるリクエストパラメータは次の通りです。| パラメータ | 説明 |
|---|---|
grant_type | グラントタイプです。どのフローを用いるかに関わらず必須のパラメータです。認可コードフローの場合は値としてauthorization_codeを指定します。 |
code | 認可コードフローの場合に必須のパラメータです。認可リクエストの結果得られた認可コードを値として指定します。 |
redirect_uri | リダイレクトURIです。先行する認可リクエストにredirect_uriパラメータを含めていた場合、トークンリクエストでもredirect_uriパラメータが必須となります。その値は認可リクエストで指定したものと同一でなければなりません。 |
client_idリクエストパラメータが必須となります。一方、クライアントタイプがコンフィデンシャルでクライアント認証方式としてprivate_key_jwtを用いる場合はclient_assertionおよびclient_assertion_typeリクエストパラメータが必須となります。クライアント認証方式の詳細については『OAuth 2.0クライアント認証』を参照してください。
下記はクライアントタイプがパブリックの場合のトークンリクエストの例です。
この例を実際に試す場合は
{{authorization_code}}を実際の値
(認可リクエストの結果得られた認可コードの値) で置き換えてください。アプリ1のトークンレスポンス
Native SSOに準拠するトークンレスポンスには、アクセストークンやリフレッシュトークン (任意) に加えて、Native SSOに準拠するIDトークンおよびデバイスシークレットが含まれます。 デバイスシークレットはdevice_secretプロパティの値として返却されます。
id_tokenプロパティの値がIDトークンです。
ds_hashクレームとsidクレームが含まれます。
ds_hashクレームはデバイスシークレットのハッシュ値です。ハッシュ値をどのように計算するかは実装依存とされていますが、ds_hashクレームにより、IDトークンとデバイスシークレットを関連付けることができます。
sidクレームはユーザの認証セッションを一意に特定する文字列です。いわゆるセッションIDです。
アプリケーションは、取得したIDトークンとデバイスシークレットを、Native SSOで連携したい他のアプリケーション群がアクセスできる場所に保存します。
アプリ2のトークンリクエスト
Native SSO仕様は、RFC 8693: OAuth 2.0 Token Exchange仕様を拡張し、Native SSOを実現するための要求事項を追加しています。 二つ目のアプリケーションは、一つ目のアプリケーションが保存したIDトークンとデバイスシークレットを取り出し、それらを用いてNative SSO仕様に準拠するトークン交換リクエストを組み立てます。RFC 8693については、解説記事『RFC 8693 OAuth 2.0トークン交換』もご参照ください。
| パラメータ | 説明 |
|---|---|
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 |
scope | トークン交換リクエストの結果発行されるアクセストークンに紐付けるスコープです。このパラメータは任意です。 |
client_idリクエストパラメータが必須となります。一方、クライアントタイプがコンフィデンシャルでクライアント認証方式としてprivate_key_jwtを用いる場合はclient_assertionおよびclient_assertion_typeリクエストパラメータが必須となります。クライアント認証方式の詳細については『OAuth 2.0クライアント認証』を参照してください。
下記はクライアントタイプがパブリックの場合のトークン交換リクエストの例です。
この例を実際に試す場合は
{{id_token}}と{{device_secre}}を実際の値で置き換えてください。アプリ2のトークンレスポンス
トークン交換リクエストに対するレスポンスは、認可コードフローのトークンレスポンスとほぼ同じです。 唯一の違いはissued_token_typeプロパティが含まれていることです。
Native SSOの場合、issued_token_typeの値はurn:ietf:params:oauth:token-type:access_tokends_hashクレームとsidクレームの値は一つ目のアプリケーションが受け取ったIDトークンと同じですが、audクレームの値は異なっています。このIDトークンでは、二つ目のアプリケーションの識別子であるapp_2がaud配列に含まれています。
利用設定
Authleteでは、バージョン3.0以降でNative SSOをサポートします。サービス設定
nativeSsoSupportedプロパティ
Native SSOをサポートするかどうかを示す新しい真偽値プロパティnativeSsoSupportedがサービスに追加されました。
このプロパティのデフォルト値はfalseなので、Native SSOを利用する場合は明示的にtrueに設定する必要があります。
nativeSsoSupportedプロパティは、Native SSO仕様が定義するサーバメタデータのnative_sso_supportedに対応しています。
nativeSsoSupportedがtrueに設定されている場合、Authleteの/service/configurationAPIが生成するディスカバリ文書
(OpenID Connect Discovery 1.0)
に次のエントリが追加されます。
nativeSsoSupportedがfalseの場合、AuthleteはNative SSOについて何も知らないかのような動作をします。
例えば、device_ssoスコープは特別な意味を持たなくなり、トークンタイプurn:openid:params:token-type:device-secretは未知のトークンタイプとして扱われます
(actor_token_typeの値として指定するとエラーになります)。
device_ssoスコープ
Native SSOのため、認可サーバがdevice_ssoスコープをサポートしている必要があります。
明示的にdevice_ssoスコープを追加登録してください。
OAuth 2.0仕様の要請により、認可サーバは未知のスコープをエラー扱いせず、単に無視します。
そのため、device_ssoスコープを登録し忘れていると、認可リクエストにdevice_ssoスコープを含めていても、何の警告もなくNative
SSO用の処理は実行されないので注意してください。
サービスのグラントタイプ
サービスがサポートするグラントタイプにTOKEN_CHANGEを追加してください。
また、AuthleteにはToken Exchangeに関する設定項目が複数存在するので、それらが意図通りの設定になっているか確認してください。特に次の二つには留意してください。
| プロパティ | 説明 |
|---|---|
tokenExchangeByConfidentialClientsOnly | トークン交換リクエストを実行できるクライアントをコンフィデンシャルクライアントのみに限定するかどうかを示す真偽値プロパティです。この値がtrueに設定されていると、パブリッククライアントからのトークン交換リクエストは受け付けられなくなります。 |
tokenExchangeByPermittedClientsOnly | トークン交換リクエストを実行できるクライアントを事前に許可を与えられたクライアントのみに限定するかどうかを示す真偽値プロパティです。この値がtrueに設定されていると、トークン交換リクエストを実行することを明示的に許可されていないクライアントからのトークン交換リクエストは受け付けられなくなります。 |
クライアント設定
device_ssoスコープ
設定により、クライアントが要求できるスコープが限定されている場合があります。 そのような設定になっている場合、device_ssoスコープを要求可能なスコープのリストに追加してください。
クライアントのグラントタイプ
クライアントが利用する可能性のあるグラントタイプのリストにTOKEN_EXCHANGEを追加してください。
また、サービスのtokenExchangeByPermittedClientsOnlyプロパティがtrueに設定されていると、明示的に許可を与えられていないクライアントからのトークン交換リクエストは拒否されてしまいます。サービスの設定がそのようになっている場合、クライアントのextension.tokenExchangePermittedプロパティにtrueを設定する必要があります。
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に渡すことになります。

認可エンドポイントの実装
認可リクエストがNative SSOを要求していると判断された場合、具体的には次の条件が全て満たされた場合、Authleteの/auth/authorizationAPIからのレスポンスには、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/issueAPIを呼ぶ際にsessionIdリクエストパラメータを含めなければなりません。
その値には、現在のユーザの認証セッションを表す識別子 (いわゆるセッションID) を指定します。ここで指定された値は、IDトークンのsidクレームの値として用いられます。

sessionIdパラメータの値としてAuthleteに渡してもよいですし、何らかの変換を加えてからAuthleteに渡してもかまいません。
ただし、あまり長い文字列は使えません。おおむね150文字程度が上限となります。sessionIdとして渡された文字列を暗号化してbase64urlエンコードした結果得られる文字列の長さが255を超えるとエラーになります (暗号化のロジックは非公開)。
/auth/authorization APIのレスポンス内のnativeSsoRequestedの値がtrueかfalseかに関係なく、常に/auth/authorization/issueAPIにsessionIdパラメータを渡すという実装でもかまいません。認可リクエストがNative SSOを要求していない場合、たとえsessionIdパラメータが指定されていたとしても、AuthleteはIDトークンにsidクレームを埋め込みません。
トークンエンドポイントの実装 (検証)
トークンリクエストがNative SSO用のものだと判断された場合、具体的には次の条件セットのいずれかが満たされた場合、Authleteの/auth/tokenAPIに含まれる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である。 |
actionの値がNATIVE_SSOになります。
actionの値がNATIVE_SSOの場合、トークンエンドポイントの実装はトークンリクエストの処理を完了させるために/nativessoAPIをコールする必要があります。しかし、それに先立ち、セッションIDやデバイスシークレットの検証、必要に応じてデバイスシークレットの生成を行わなければなりません。
さらに、/auth/tokenAPIのレスポンスにnonceとs_hashを含むJSON文字列としてadditionalClaimsフィールドが含まれている場合は、その値をclaimsパラメータにそのまま引き渡してください。
additionalClaimsが返ってきた場合、claimsパラメータにはそのJSON文字列をそのまま指定してください。これらのクレームはIDトークン検証に不可欠であり、/nativessoAPIが生成するIDトークンに埋め込まれます。

セッションIDの検証
/auth/tokenAPIからのレスポンスに含まれるactionの値がNATIVE_SSOの場合、そのレスポンスにはsessionIdパラメータが含まれ、その値はユーザ認証セッションを表す値、すなわちセッションIDです。トークンエンドポイントの実装は、このセッションIDが依然として有効かどうかを確認しなければなりません。無効の場合は/nativessoAPIを呼ばず、代わりにinvalid_grantエラーを示すトークンレスポンスを生成してクライアントに返却してください。
/auth/tokenAPIのレスポンスに含まれるsessionIdの値は、元々は/auth/authorization/issueAPIのsessionIdリクエストパラメータの値としてOpenIDプロバイダからAuthleteに渡されたものです。
AuthleteはそのセッションIDが有効かどうか判断しない (判断できない) ので、セッションIDの検証はOpenIDプロバイダ側で実施する必要があります。
トークンリクエストが認可コードフローまたはリフレッシュトークンフローのものである場合、sessionIdの値は、認可コードまたはリフレッシュトークンに紐付くセッションIDです。
一方、トークンリクエストがトークン交換フローのものである場合、sessionIdの値はsubject_tokenパラメータの値として提示されたIDトークンのsidクレームの値です。

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

deviceSecretの値がnullでない場合、その値が有効かどうかを検証してください。
もし有効であれば、その値をそのまま/nativessoAPIに渡してください。
一方、deviceSecretの値が存在しない、または無効の場合、新しいデバイスシークレットを生成し、その値を/nativessoデバイスシークレットの検証 (トークン交換フロー)
actionの値がNATIVE_SSOで、当該トークンリクエストがトークン交換フローのものである場合
(grantTypeがTOKEN_EXCHAGEの場合)、/auth/tokenAPIからのレスポンスには必ずdeviceSecretパラメータとdeviceSecretHashパラメータが含まれます。
deviceSecretの値は、トークンリクエストのactor_tokenパラメータの値として指定されたデバイスシークレットです。
deviceSecretHashの値は、トークンリクエストのsubject_tokenパラメータの値として指定されたIDトークンに含まれるds_hashクレームの値です。

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

actionがINTERNAL_SERVER_ERRORの場合、Authlete側で何か問題が発生したことを意味します。
例えば、accessTokenパラメータで指定されたアクセストークンをデータベースから取り出す際にデータベースエラーが発生した、といった問題です。
このとき、トークンエンドポイントの実装はクライアントにエラーレスポンスを返すべきです。
最も単純な実装では、500 Internal Server Errorを返します。
actionがCALLER_ERRORの場合、API呼び出し側 (OpenIDプロバイダの実装) に問題があることを示しています。
例えば、必須パラメータであるaccessTokenを含めなかった、といった問題です。
CALLER_ERRORが返された場合はOpenIDプロバイダの実装を見直してください。
シングルログアウト
一回の操作で複数のアプリケーションからログアウトすることをシングルログアウト (SLO) と呼びます。 シングルサインオンと対となります。 Native SSO仕様はシングルログアウトを実現するための具体的なプロトコルを定義していません。 しかし、特定のセッションIDに紐付くアクセストークン・リフレッシュトークン群をまとめて削除することにより、シングルログアウトを実現できます。 Authleteは/nativesso/logoutAPIによりシングルログアウトの機能を提供します。
このAPIはapplication/jsonまたはapplication/x-www-form-urlencoded形式のHTTP
POSTリクエストを受け付けます。リクエストパラメータはsessionId一つのみです。
このリクエストパラメータに対象となるセッションIDを指定して/nativesso/logoutAPIを呼ぶと、そのセッションIDに紐付いたアクセストークン・リフレッシュトークンが全て削除されます。
/nativesso/logoutは、指定されたセッションIDに紐付けられたアクセストークン・リフレッシュトークンが存在せずに結果として削除件数が0件だったとしてもエラーにはなりません。
サンプル実装
OpenIDプロバイダ側のNative SSOサンプル実装はjava-oauth-serverとauthlete-java-jaxrsライブラリに含まれています。どちらもJava言語によるオープンソース実装です。 以下はサンプル実装を読む際のヒントです。/auth/authorization/issueAPIに渡すセッションIDは、AuthorizationDecision Endpointの中で HttpServletRequest.getSession(false).getId()を実行することで取得しています。ただし、この処理で得られるセッションはWebサーバとWebブラウザ間のHTTPセッションであるため、Native SSOの商用実装では別の仕組みを用いることになると思います。- 取得したセッションIDはAuthorization
Decision Handler Spiインターフェースを介してAuthorization Decision Handlerに渡ります。 - Authorization
Decision HandlerはAuthlete Api Callerの callAuthorizationIssueメソッドを介してAuthleteの/auth/authorization/issueAPIを呼びます。 /auth/tokenAPIのレスポンスのactionに基づくディスパッチ処理はTokenRequest Handlerに書かれています。 - Token
Request Handlerは、 actionがNATIVE_SSOの場合、TokenRequest Handler Spiインターフェースの nativeSsoメソッドを呼びます。 - Token
Request Handler Spiインターフェースの実装であるToken Request Handler Spi Implは、 nativeSsoメソッドの中からNativeSso Processorの processメソッドを呼びます。 - Native
Sso Processorは、 /auth/tokenAPIからのレスポンスを表すTokenResponseクラスのインスタンスから、セッションID、デバイスシークレット、デバイスシークレットハッシュを取り出します。 - Native
Sso Processorは、 retrieveDeviceId()メソッドの中で、トークンエンドポイントにアクセスしてきたデバイスのデバイス識別子を取得します。ただし、サンプル実装ではこのメソッドの実装は空なので注意してください。 - Native
Sso Processorは、 validateParametersメソッドの中で、セッションID、デバイスシークレット、デバイスシークレットハッシュ、デバイス識別子の検証をおこなっています。 - セッションIDが有効かどうかのチェックはSession
Trackerの isActiveSessionId(String)メソッドを呼び出すことで行っています。SessionTrackerはHttp Session Listenerインターフェースを実装しており、セッションの生成と削除を監視しています。Session Trackerはweb.xml内でリスナーとして登録されています。 - デバイスシークレットとセッションID、デバイスシークレットハッシュ、デバイス識別子の関係はDevice
Secretクラスで表現されています。 - Device
Secretのインスタンス群はDevice Secret Managerが管理しています。
Authlete実装
Authleteバージョン
Authleteでは、バージョン3.0以降でNative SSOをサポートします。Native SSOバージョン
Authlete Native SSOの最初のバージョンはOpenID Connect Native SSO for Mobile Apps 1.0仕様のドラフト07に基づいて実装されました。そのため、古い版で使われていたurn:x-oath:params:\*識別子にかわってurn:openid:params:\*識別子が使われています。
トークン交換リクエスト検証
トークン交換リクエストにactor_token_typeパラメータが含まれており、その値がurn:openid:params:token-type:device-secretである場合、Authleteはトークン交換リクエスト固有のリクエストパラメータに対して下記の検証をおこないます。これらを全てパスした場合のみ、/auth/tokenAPIのレスポンスのactionがNATIVE_SSOになります。
audienceパラメータが指定されており、その値がサービスのOpenIDプロバイダ識別子 (Service.issuerに設定されている値) と一致する。なお、audienceパラメータは複数指定することが許されており、複数指定された場合は、いずれかの値が一致すればよい。requested_token_typeパラメータが指定されている場合、その値が既知のトークンタイプである。subject_token_typeパラメータが指定されており、その値がurn:ietf:params:oauth:token-type:id_tokenである。subject_tokenパラメータが指定されており、その値 (IDトークン) が次の検証項目を全てパスする。- JWTとしてパースできる。
expクレームを含んでおり、値の型が数値である。注: Native SSOの文脈ではexpの値が現在時刻より未来であることを確認しない。これは期限切れのIDトークンをサブジェクトトークンとして使えることを意味する (参照: id_token usage)。iatクレームを含んでおり、値が現在時刻または過去を示している。nbfクレームを含む場合、値が現在時刻または過去を示している。issクレームを含んでおり、値がサービスのOpenIDプロバイダ識別子 (Service.issuerに設定されている値) と一致する。subクレームを含んでおり、値の型が文字列である。audクレームを含んでおり、値の型が文字列または配列である。また、配列の場合、一つ以上の要素を含み、全ての要素の型が文字列である。nonceクレームを含む場合、値の型が文字列である。sidクレームを含んでおり、値の型が文字列である。ds_hashクレームを含んでおり、値の型が文字列である。- JWEではない。
- 署名されている。
- 署名検証に成功する。
actor_tokenパラメータが指定されている。
Native SSO仕様のドラフト07では、トークン交換リクエストに
scopeパラメータが含まれている場合、そのスコープリストにopenidが含まれていなければならないと定めています。
しかし、AB/Connect ISSUE 2178:
[Native SSO] the openid scope on token exchangeに記述されている理由のため、Authleteの現在の実装はopenidが含まれるかどうかを確認しません。参考情報
- 仕様: OpenID Connect Native SSO for Mobile Apps 1.0
- 仕様: RFC 8693: OAuth 2.0 Token Exchange
- Native SSO仕様書ソースコード: openid-connect-native-sso-1_0.xml
- Native SSO仕様Issue Tracker: bitbucket.org/openid/connect/issues
- 解説文書: RFC 8693 OAuth 2.0 トークン交換