Table of Contents
OpenID Connect Native SSO for Mobile Apps 1.0 (以下Native SSO) は、同一ベンダーの管理下にある複数のモバイルアプリケーション間でシングルサインオン (SSO) を実現する仕組みを標準仕様として定義します。この仕様が実装されていると、ユーザはモバイルアプリケーション毎にユーザ認証を行う必要はなく、Native SSOで連携したアプリケーション群に対して一回のユーザ認証で済みます。
詳細に踏み込む前に、仕様の概要を紹介します。
まず、一つ目のアプリケーションが認可コードフローを用いて下記のトークン群を取得します。注目すべき点は、IDトークンがNative SSO仕様に準拠していること、および、デバイスシークレットという新しいタイプのトークンが含まれていることです。
一つ目のアプリケーションは、二つ目のアプリケーションがアクセスできる共有ストレージにIDトークンとデバイスシークレットを保存します。
次に、二つ目のアプリケーションが共有ストレージから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プロバイダは何らかの方法でデバイスの情報を取得し、デバイスシークレットと紐付けます。 そして、トークン交換リクエストを受け取った際、リクエストに含まれるデバイスシークレットに紐付くデバイスが、リクエスト送信元のデバイスと一致するかを確認します。
一つ目のアプリケーションは、認可コードフローに基づく認可リクエストを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 パラメータが必須となります。 |
下記は認可リクエストの例です。
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
上記の認可リクエストの結果得られた認可コードを用いてトークンリクエストを組み立てます。必要となるリクエストパラメータは次の通りです。
パラメータ | 説明 |
---|---|
grant_type |
グラントタイプです。どのフローを用いるかに関わらず必須のパラメータです。認可コードフローの場合は値としてauthorization_ を指定します。 |
code |
認可コードフローの場合に必須のパラメータです。認可リクエストの結果得られた認可コードを値として指定します。 |
redirect_uri |
リダイレクトURIです。先行する認可リクエストにredirect_ パラメータを含めていた場合、トークンリクエストでもredirect_ パラメータが必須となります。その値は認可リクエストで指定したものと同一でなければなりません。 |
また、上記に加え、アプリケーションのクライアントタイプ (RFC 6749 Section 2.1) がクレデンシャルかパブリックか、また、クライアントタイプがコンフィデンシャルの場合にどのクライアント認証方式を用いるかにより、追加のパラメータが必要になります。
例えば、クライアントタイプがパブリックの場合はclient_
リクエストパラメータが必須となります。一方、クライアントタイプがコンフィデンシャルでクライアント認証方式としてprivate_
を用いる場合はclient_
およびclient_
リクエストパラメータが必須となります。クライアント認証方式の詳細については『OAuth 2.0クライアント認証』を参照してください。
下記はクライアントタイプがパブリックの場合のトークンリクエストの例です。
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
Native SSOに準拠するトークンレスポンスには、アクセストークンやリフレッシュトークン (任意)
に加えて、Native SSOに準拠するIDトークンおよびデバイスシークレットが含まれます。
デバイスシークレットはdevice_secret
プロパティの値として返却されます。
{
"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トークンです。
eyJraWQiOiJaWUdJT0hZdUE5SXBVaWpWd1FOdWwzbkU1MzZ4MUpTV0hpT2ZkUzdzYWRnIiwiYWxnIjoiRVMyNTYifQ.eyJpc3MiOiJodHRwczovL3RyaWFsLmF1dGhsZXRlLm5ldCIsInN1YiI6IjEwMDQiLCJhdWQiOlsiYXBwXzEiXSwiZXhwIjoxNzQ2NDM3MTE5LCJpYXQiOjE3NDYzNTA3MTksImF1dGhfdGltZSI6MTc0NjM1MDY3MiwiZHNfaGFzaCI6IlhrYmdHQ1JKUTFOQUhuS25NbjhKMFhIS25fOEVNenhCOWFRdUZITk0ycDQiLCJzaWQiOiJub2RlMDM4Y2F0N2ozMDhzZzE4MjhtMXNnMmRleGwzIn0.JAYlCEbGhjJwpgSZ4lUNaXkWD2ICeDs6FCBd3bKRvKPhrrGZKUAZDRij_Bmn_AF7DyTQS5ALHl82cJqjaLCcIw
このIDトークンのペイロード部をbase64urlでデコードすると次のようになります。Native SSO仕様に準拠するIDトークンにはds_hash
クレームとsid
クレームが含まれます。
{
"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で連携したい他のアプリケーション群がアクセスできる場所に保存します。
Native SSO仕様は、RFC 8693: OAuth 2.0 Token Exchange仕様を拡張し、Native SSOを実現するための要求事項を追加しています。 二つ目のアプリケーションは、一つ目のアプリケーションが保存したIDトークンとデバイスシークレットを取り出し、それらを用いてNative SSO仕様に準拠するトークン交換リクエストを組み立てます。
Native SSO仕様に準拠するトークン交換リクエストのリクエストパラメータは次の通りです。
パラメータ | 説明 |
---|---|
grant_type |
グラントタイプです。トークン交換リクエストではurn: を指定します。 |
audience |
トークン交換リクエストにより発行されるトークンを使用する対象です。Native SSO用のトークン交換リクエストではOpenIDプロバイダの識別子を指定します。 |
subject_token |
誰のためのトークン交換リクエストであるかを示すトークンです。Native SSO用のトークン交換リクエストではIDトークンを指定します。 |
subject_token_type |
subject_token のタイプを示す識別子です。Native SSO用のトークン交換リクエストではsubject_token は常にIDトークンなので、subject_token_type パラメータの値にはurn: |
actor_token |
トークン交換リクエストの実行者を表すトークンです。Native SSO用のトークン交換リクエストではデバイスシークレットを指定します。 |
actor_token_type |
actor_token のタイプを示す識別子です。Native SSO用のトークン交換リクエストではactor_token は常にデバイスシークレットなので、actor_token_type パラメータの値にはurn: |
scope |
トークン交換リクエストの結果発行されるアクセストークンに紐付けるスコープです。このパラメータは任意です。 |
また、上記に加え、アプリケーションのクライアントタイプ (RFC 6749 Section 2.1) がクレデンシャルかパブリックか、また、クライアントタイプがコンフィデンシャルの場合にどのクライアント認証方式を用いるかにより、追加のパラメータが必要になります。
例えば、クライアントタイプがパブリックの場合はclient_
リクエストパラメータが必須となります。一方、クライアントタイプがコンフィデンシャルでクライアント認証方式としてprivate_
を用いる場合はclient_
およびclient_
リクエストパラメータが必須となります。クライアント認証方式の詳細については『OAuth 2.0クライアント認証』を参照してください。
下記はクライアントタイプがパブリックの場合のトークン交換リクエストの例です。
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
トークン交換リクエストに対するレスポンスは、認可コードフローのトークンレスポンスとほぼ同じです。
唯一の違いはissued_
プロパティが含まれていることです。
Native SSOの場合、issued_
の値はurn:
{
"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トークンを取り出し、
eyJraWQiOiJaWUdJT0hZdUE5SXBVaWpWd1FOdWwzbkU1MzZ4MUpTV0hpT2ZkUzdzYWRnIiwiYWxnIjoiRVMyNTYifQ.eyJpc3MiOiJodHRwczovL3RyaWFsLmF1dGhsZXRlLm5ldCIsInN1YiI6IjEwMDQiLCJhdWQiOlsiYXBwXzIiXSwiZXhwIjoxNzQ2NDM4MzUxLCJpYXQiOjE3NDYzNTE5NTEsImRzX2hhc2giOiJYa2JnR0NSSlExTkFIbktuTW44SjBYSEtuXzhFTXp4QjlhUXVGSE5NMnA0Iiwic2lkIjoibm9kZTAzOGNhdDdqMzA4c2cxODI4bTFzZzJkZXhsMyJ9.8jNNF5mpeHnbqp1FTK_1adR8FlgPmHK9_rwUzaz-o5P7RMyaelBaSj74IhxHY6wbCJeD0n_N14h8vD8zWYh-8w
ペイロード部をbase64urlでデコードすると次のようになります。
{
"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
配列に含まれています。
Authleteでは、バージョン3.0以降でNative SSOをサポートします。
Native SSOをサポートするかどうかを示す新しい真偽値プロパティnative
がサービスに追加されました。
このプロパティのデフォルト値はfalse
なので、Native SSOを利用する場合は明示的にtrue
に設定する必要があります。
native
プロパティは、Native SSO仕様が定義するサーバメタデータのnative_
に対応しています。
native
がtrue
に設定されている場合、Authleteの/service
APIが生成するディスカバリ文書
(OpenID Connect Discovery 1.0)
に次のエントリが追加されます。
"native_sso_supported": true
native
がfalse
の場合、AuthleteはNative SSOについて何も知らないかのような動作をします。
例えば、device_
スコープは特別な意味を持たなくなり、トークンタイプurn:
は未知のトークンタイプとして扱われます
(actor_
の値として指定するとエラーになります)。
Native SSOのため、認可サーバがdevice_
スコープをサポートしている必要があります。
明示的にdevice_
スコープを追加登録してください。
OAuth 2.0仕様の要請により、認可サーバは未知のスコープをエラー扱いせず、単に無視します。
そのため、device_
スコープを登録し忘れていると、認可リクエストにdevice_
スコープを含めていても、何の警告もなくNative
SSO用の処理は実行されないので注意してください。
サービスがサポートするグラントタイプにTOKEN_
を追加してください。
また、AuthleteにはToken Exchangeに関する設定項目が複数存在するので、それらが意図通りの設定になっているか確認してください。特に次の二つには留意してください。
プロパティ | 説明 |
---|---|
token |
トークン交換リクエストを実行できるクライアントをコンフィデンシャルクライアントのみに限定するかどうかを示す真偽値プロパティです。この値がtrue に設定されていると、パブリッククライアントからのトークン交換リクエストは受け付けられなくなります。 |
token |
トークン交換リクエストを実行できるクライアントを事前に許可を与えられたクライアントのみに限定するかどうかを示す真偽値プロパティです。この値がtrue に設定されていると、トークン交換リクエストを実行することを明示的に許可されていないクライアントからのトークン交換リクエストは受け付けられなくなります。 |
設定により、クライアントが要求できるスコープが限定されている場合があります。
そのような設定になっている場合、device_
スコープを要求可能なスコープのリストに追加してください。
クライアントが利用する可能性のあるグラントタイプのリストにTOKEN_
を追加してください。
また、サービスのtoken
プロパティがtrue
に設定されていると、明示的に許可を与えられていないクライアントからのトークン交換リクエストは拒否されてしまいます。サービスの設定がそのようになっている場合、クライアントのextension.
プロパティにtrue
を設定する必要があります。
ユーザ認証・ユーザ管理と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
APIからのレスポンスには、native
という真偽値プロパティが値true
で含まれます。
条件 | |
---|---|
1 | サービスがNative SSOをサポートしている。 (Service. の値がtrue と設定されている。) |
2 | サービスがopenid スコープとdevice_sso スコープをサポートしている。 |
3 | クライアントがopenid スコープとdevice_sso スコープを要求することを許可されている。(Requestable Scopes機能による制限を受けていない。) |
4 | 認可リクエストのscope にopenid とdevice_sso が含まれている。 |
5 | サービスが認可コードフローをサポートしている。(Service. にAUTHORIZATION_ が含まれている。) |
6 | クライアントが認可コードフローを使用すると宣言している。(Client. にAUTHORIZATION_ が含まれている。) |
7 | 認可リクエストのresponse_type にcode が含まれている。 |
8 | サービスがそのレスポンスタイプをサポートしている。(Service. に当該レスポンスタイプが含まれている。) |
9 | クライアントがそのレスポンスタイプを使用すると宣言している。(Client. に当該レスポンスタイプが含まれている。) |
この表の条件は複雑に見えますが、サービスやクライアントの設定条件を除いて認可リクエストの条件だけに限って言えば、「scope
にopenid
とdevice_sso
が含まれていてresponse_type
にcode
が含まれている」場合、Native SSOを要求していると判断されます。
native
プロパティの値がtrue
の場合、認可エンドポイントの実装は/auth
APIを呼ぶ際にsessionId
リクエストパラメータを含めなければなりません。
その値には、現在のユーザの認証セッションを表す識別子 (いわゆるセッションID) を指定します。ここで指定された値は、IDトークンのsid
クレームの値として用いられます。
認可エンドポイントの実装は、実際のセッションIDの値をsessionId
パラメータの値としてAuthleteに渡してもよいですし、何らかの変換を加えてからAuthleteに渡してもかまいません。
ただし、あまり長い文字列は使えません。おおむね150文字程度が上限となります。sessionId
として渡された文字列を暗号化してbase64urlエンコードした結果得られる文字列の長さが255を超えるとエラーになります (暗号化のロジックは非公開)。
/auth
APIのレスポンス内のnative
の値がtrue
かfalse
かに関係なく、常に/auth
APIにsessionId
パラメータを渡すという実装でもかまいません。認可リクエストがNative SSOを要求していない場合、たとえsessionId
パラメータが指定されていたとしても、AuthleteはIDトークンにsid
クレームを埋め込みません。
トークンリクエストがNative SSO用のものだと判断された場合、具体的には次の条件セットのいずれかが満たされた場合、Authleteの/auth
APIに含まれるaction
プロパティの値はNATIVE_SSO
になります。
条件セット1: 認可コードフロー | |
---|---|
1 | サービスがNative SSOをサポートしている。 (Service. の値がtrue と設定されている。) |
2 | サービスがopenid スコープとdevice_sso スコープをサポートしている。 |
3 | クライアントがopenid スコープとdevice_sso スコープを要求することを許可されている。(Requestable Scopes機能による制限を受けていない。) |
4 | 対応する認可リクエストのscope にopenid とdevice_sso が含まれている。 |
5 | サービスが認可コードフローをサポートしている。(Service. にAUTHORIZATION_ が含まれている。) |
6 | クライアントが認可コードフローを使用すると宣言している。(Client. にAUTHORIZATION_ が含まれている。) |
7 | grant_type パラメータの値がauthorization_code である。 |
条件セット2: リフレッシュトークンフロー | |
---|---|
1 | サービスがNative SSOをサポートしている。 (Service. の値がtrue と設定されている。) |
2 | サービスがdevice_sso スコープをサポートしている。 |
3 | クライアントがdevice_sso スコープを要求することを許可されている。(Requestable Scopes機能による制限を受けていない。) |
4 | サービスがリフレッシュトークンフローをサポートしている。(Service. にREFRESH_ が含まれている。) |
5 | クライアントがリフレッシュトークンフローを使用すると宣言している。(Client. にREFRESH_ が含まれている。) |
6 | grant_type パラメータの値がrefresh_token である。 |
7 | トークンリクエストのscope パラメータの指定によりスコープの範囲が狭められたとしても、device_sso スコープが依然としてカバーされている。 |
8 | 提示されたリフレッシュトークンがユーザ認証セッションに紐付けられている。(実質的にNative SSOに準拠した認可コードフローにより生成されたリフレッシュトークンしか使えない。) |
条件セット3: トークン交換フロー | |
---|---|
1 | サービスがNative SSOをサポートしている。 (Service. の値がtrue と設定されている。) |
2 | サービスがトークン交換フローをサポートしている。(Service. にTOKEN_ が含まれている。) |
3 | クライアントがトークン交換フローを使用すると宣言している。(Client. にTOKEN_ が含まれている。) |
4 | サービスのトークン交換フローの各種設定 (token 等) がクライアントのトークン交換リクエストを拒絶しない。 |
5 | grant_type パラメータの値がurn: である。 |
6 | actor_token_type パラメータの値がurn: である。 |
上記の条件群は複雑に見えますが、要は、「Native SSOに準拠したIDトークンとトークンレスポンスを生成する必要がある」とAuthleteが判断した場合、action
の値がNATIVE_SSO
になります。
action
の値がNATIVE_SSO
の場合、トークンエンドポイントの実装はトークンリクエストの処理を完了させるために/nativesso
APIをコールする必要があります。しかし、それに先立ち、セッションIDやデバイスシークレットの検証、必要に応じてデバイスシークレットの生成を行わなければなりません。
/auth/token
APIからのレスポンスに含まれるaction
の値がNATIVE_SSO
の場合、そのレスポンスにはsessionId
パラメータが含まれ、その値はユーザ認証セッションを表す値、すなわちセッションIDです。トークンエンドポイントの実装は、このセッションIDが依然として有効かどうかを確認しなければなりません。無効の場合は/nativesso
APIを呼ばず、代わりにinvalid_grant
エラーを示すトークンレスポンスを生成してクライアントに返却してください。
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
APIのsessionId
リクエストパラメータの値としてOpenIDプロバイダからAuthleteに渡されたものです。
AuthleteはそのセッションIDが有効かどうか判断しない (判断できない) ので、セッションIDの検証はOpenIDプロバイダ側で実施する必要があります。
トークンリクエストが認可コードフローまたはリフレッシュトークンフローのものである場合、sessionId
の値は、認可コードまたはリフレッシュトークンに紐付くセッションIDです。
一方、トークンリクエストがトークン交換フローのものである場合、sessionId
の値はsubject_token
パラメータの値として提示されたIDトークンのsid
クレームの値です。
action
の値がNATIVE_SSO
で、当該トークンリクエストが認可コードフローもしくはリフレッシュトークンフローのものである場合
(grantType
がAUTHORIZATION_
またはREFRESH_
の場合)、/auth/token
APIのレスポンスにはdevice
パラメータが含まれている可能性があります。
その値は、トークンリクエストのdevice_
リクエストパラメータの値です。
このリクエストパラメータ自体はオプショナルです。
deviceSecret
の値がnull
でない場合、その値が有効かどうかを検証してください。
もし有効であれば、その値をそのまま/nativesso
APIに渡してください。
一方、device
の値が存在しない、または無効の場合、新しいデバイスシークレットを生成し、その値を/nativesso
action
の値がNATIVE_SSO
で、当該トークンリクエストがトークン交換フローのものである場合
(grantType
がTOKEN_EXCHAGE
の場合)、/auth
APIからのレスポンスには必ずdevice
パラメータとdevice
パラメータが含まれます。
deviceSecret
の値は、トークンリクエストのactor_token
パラメータの値として指定されたデバイスシークレットです。
device
の値は、トークンリクエストのsubject_
パラメータの値として指定されたIDトークンに含まれるds_hash
クレームの値です。
トークンエンドポイントの実装では、デバイスシークレットハッシュがデバイスシークレットに対応するものかどうか確認してください。
対応するものでない場合は/nativesso
APIを呼ばず、代わりにinvalid_grant
エラーを示すトークンレスポンスを生成してクライアントに返却してください。
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."
}
セッションIDの検証およびデバイスシークレットの検証または生成の完了後、Native SSOに準拠するIDトークンとトークンレスポンスを生成するため、Authleteの/nativesso
APIをコールしてください。
/nativesso
APIは、application/json
またはapplication/
形式のHTTP POSTリクエストを受け付けます。リクエストパラメータは下表の通りです。
説明 | ||
---|---|---|
access |
必須 | /auth/token APIのレスポンスにjwtAccessToken が含まれていればその値を、含まれていなければaccessToken の値を指定します。指定された値は/nativesso APIが用意するトークンレスポンスのaccess_token プロパティの値になります。 |
refresh |
任意 | /auth/token APIのレスポンスに含まれるrefreshToken の値を指定します。指定された値は/nativesso APIが用意するトークンレスポンスのrefresh_token プロパティの値になります。 |
device |
必須 | /auth/token APIのレスポンスにdeviceSecret が含まれていればその値を、含まれていなければ新しいデバイスシークレットを生成してその値を指定します。指定された値は/nativesso APIが用意するトークンレスポンスのdevice_secret プロパティの値になります。 |
device |
推奨 | デバイスシークレットのハッシュ値を指定します。デバイスシークレットからハッシュ値を求めるロジックはOpenIDプロバイダの実装依存です。このパラメータが省略された場合、/nativesso APIの実装はdevice パラメータの値のSHA-256ハッシュを計算し、そのハッシュ値をbase64urlエンコードしたものをデバイスシークレットハッシュとします。device パラメータで指定された値、または/nativesso APIが生成した値は、/nativesso APIが生成するIDトークンにds_hash クレームの値として埋め込まれます。 |
sub |
任意 | /nativesso APIが生成するIDトークンのsub クレームの値です。このパラメータが省略された場合、access パラメータで指定されたアクセストークンに紐付くサブジェクトがsub クレームの値として用いられます。IDトークンの生成を伴うAuthlete APIには sub リクエストパラメータがあるため、/nativesso APIもこのリクエストパラメータを受け付けます。しかしながら、Native SSOの文脈でアクセストークンのサブジェクトと異なる値をsub クレームの値に用いると、意図しない不整合を起こす可能性があるので、このsub パラメータを使う際は慎重におこなってください。この sub パラメータの値に関わらず、Native SSOのトークン交換フローでアクセストークンを新規作成する際、Authleteはサブジェクトトークン (過去の/nativesso APIコールにより生成されたIDトークン) のsub クレームの値をアクセストークンに紐付くサブジェクトとして設定します。/auth APIがレスポンスを返した時点で既にアクセストークンの生成は完了しており、/nativesso APIのsub パラメータではアクセストークンに紐付くサブジェクトを変更することはできません。 |
claims |
任意 | IDトークンに埋め込む追加のクレーム群を指定します。形式はJSONオブジェクトを表す文字列でなければなりません。 |
idt |
任意 | IDトークンのJWSヘッダに埋め込む追加のパラメータ群を指定します。形式はJSONオブジェクトを表す文字列でなければなりません。 |
id |
任意 | IDトークンのaud クレームの形式を指定します。array を指定した場合はaud クレームの値はJSON配列となり、string を指定した場合はJSON文字列となります。このid パラメータを省略した場合、サービスのid プロパティの設定が参照されます。サービスの当プロパティが設定されていない場合、aud クレームの値はJSON配列となります。 |
/nativesso
APIからのレスポンスのメッセージボディの形式はJSONです。他の多くのAuthlete APIと同様に、/nativesso
APIのレスポンスにもaction
プロパティが含まれています。トークンエンドポイントの実装では、このaction
の値に従ってトークンレスポンスを組み立ててください。
action
がOK
の場合、/nativesso
APIの処理が全て成功裡に終わったことを示します。
このとき、トークンエンドポイントの実装はクライアントに成功応答 (200 OK
) を返すようにします。
/nativesso
APIからのレスポンスに含まれるresponse
プロパティの値は、トークンレスポンスのメッセージボディとしてそのまま使えます。
このため、成功応答は次のように構築できます。
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
(responseContentの値をここに置く)
action
がINTERNAL_SERVER_ERROR
の場合、Authlete側で何か問題が発生したことを意味します。
例えば、access
パラメータで指定されたアクセストークンをデータベースから取り出す際にデータベースエラーが発生した、といった問題です。
このとき、トークンエンドポイントの実装はクライアントにエラーレスポンスを返すべきです。
最も単純な実装では、500 Internal Server Error
を返します。
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
Cache-Control: no-store
(responseContentの値をここに置く)
しかし、商用環境では、500エラーとは異なる抽象度の高い (エラーの内容を直接記述しない) エラーを返したほうがよいかもしれません。
action
がCALLER_ERROR
の場合、API呼び出し側 (OpenIDプロバイダの実装) に問題があることを示しています。
例えば、必須パラメータであるaccessToken
を含めなかった、といった問題です。
CALLER_
が返された場合はOpenIDプロバイダの実装を見直してください。
一回の操作で複数のアプリケーションからログアウトすることをシングルログアウト (SLO) と呼びます。 シングルサインオンと対となります。
Native SSO仕様はシングルログアウトを実現するための具体的なプロトコルを定義していません。 しかし、特定のセッションIDに紐付くアクセストークン・リフレッシュトークン群をまとめて削除することにより、シングルログアウトを実現できます。
Authleteは/nativesso/logout
APIによりシングルログアウトの機能を提供します。
このAPIはapplication/
またはapplication/
形式のHTTP
POSTリクエストを受け付けます。リクエストパラメータはsessionId
一つのみです。
このリクエストパラメータに対象となるセッションIDを指定して/nativesso
APIを呼ぶと、そのセッションIDに紐付いたアクセストークン・リフレッシュトークンが全て削除されます。
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}}"
}
{
"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件だったとしてもエラーにはなりません。
{
"action": "OK",
"count": 0,
"resultCode": "A503002",
"resultMessage": "[A503002] The /nativesso/logout API call completed without deleting any access/refresh token records."
}
OpenIDプロバイダ側のNative SSOサンプル実装はjava-oauth-serverとauthlete-java-jaxrsライブラリに含まれています。どちらもJava言語によるオープンソース実装です。
以下はサンプル実装を読む際のヒントです。
/auth/authorization/issue
APIに渡すセッションIDは、AuthorizationHttpServletRequest.getSession(false).getId()
を実行することで取得しています。ただし、この処理で得られるセッションはWebサーバとWebブラウザ間のHTTPセッションであるため、Native SSOの商用実装では別の仕組みを用いることになると思います。callAuthorizationIssue
メソッドを介してAuthleteの/auth/authorization/issue
APIを呼びます。/auth/token
APIのレスポンスのaction
に基づくディスパッチ処理はTokenaction
がNATIVE_SSO
の場合、TokennativeSso
メソッドを呼びます。nativeSso
メソッドの中からNativeprocess
メソッドを呼びます。/auth/token
APIからのレスポンスを表すTokenretrieveDeviceId()
メソッドの中で、トークンエンドポイントにアクセスしてきたデバイスのデバイス識別子を取得します。ただし、サンプル実装ではこのメソッドの実装は空なので注意してください。validateParameters
メソッドの中で、セッションID、デバイスシークレット、デバイスシークレットハッシュ、デバイス識別子の検証をおこなっています。isActiveSessionId(String)
メソッドを呼び出すことで行っています。SessionAuthleteでは、バージョン3.0以降でNative SSOをサポートします。
Authlete Native SSOの最初のバージョンはOpenID Connect Native SSO for Mobile Apps 1.0仕様のドラフト07に基づいて実装されました。そのため、古い版で使われていたurn:
識別子にかわってurn:
識別子が使われています。
トークン交換リクエストにactor_
パラメータが含まれており、その値がurn:
である場合、Authleteはトークン交換リクエスト固有のリクエストパラメータに対して下記の検証をおこないます。これらを全てパスした場合のみ、/auth
APIのレスポンスの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トークン) が次の検証項目を全てパスする。
exp
クレームを含んでおり、値の型が数値である。注: Native SSOの文脈ではexp
の値が現在時刻より未来であることを確認しない。これは期限切れのIDトークンをサブジェクトトークンとして使えることを意味する (参照: id_token usage)。iat
クレームを含んでおり、値が現在時刻または過去を示している。nbf
クレームを含む場合、値が現在時刻または過去を示している。iss
クレームを含んでおり、値がサービスのOpenIDプロバイダ識別子 (Service.issuer
に設定されている値) と一致する。sub
クレームを含んでおり、値の型が文字列である。aud
クレームを含んでおり、値の型が文字列または配列である。また、配列の場合、一つ以上の要素を含み、全ての要素の型が文字列である。nonce
クレームを含む場合、値の型が文字列である。sid
クレームを含んでおり、値の型が文字列である。ds_hash
クレームを含んでおり、値の型が文字列である。actor_token
パラメータが指定されている。