概要
OAuth は、保護されたサーバー・リソースへのアクセス権を、リソース・オーナーに代わってクライアントが取得できるように認可するためのオープン・スタンダードです。リソース・オーナーは、別のクライアントの場合もあれば、エンド・ユーザーの場合もあります。OAuth はまた、エンド・ユーザーが自ら所有するサーバー・リソースへのアクセスをサード・パーティーに対して認可する上で役に立ちますが、そのために自分のクレデンシャル (ユーザー名とパスワードなど) を共有する必要はありません。この記事のシリーズでは、RFC6749 で概説している OAuth 2.0 認可フレームワークに忠実に従います。RFC 6749 で概説している完全な OAuth 2.0 認可フレームワークについては、Internet Engineering Task Force の Web サイトを参照してください。
認可グラント
認可グラントとは、保護されたリソースにアクセスするために使用できる、リソース・オーナーの認可を表すクレデンシャルのことです。クライアントは、このクレデンシャルを使用してアクセス・トークンを取得し、そのアクセス・トークンを、保護されたリソースにアクセスするためのリクエストと一緒に送信します。OAuth 2.0 では、認可グラントのタイプとして、次の 4 つを定義しています。
- 認可コードによるグラント
- インプリシット (暗黙的) グラント
- リソース・オーナー・パスワード・クレデンシャル・グラント
- クライアント・クレデンシャル・グラント
この全 4 回からなる記事のシリーズでは、上記にリストアップした認可グラントの各タイプを使用して、Java プログラミングで OAuth 2.0 クライアントを実装する方法を説明します。第 3 回目となる今回の記事で説明するのは、認可コードによるグラントを実装する方法です。記事ではこの認可コードによるグラントの詳細を説明し、サンプル・クライアント・コードの内容を解説します。このサンプル・コードを使用すれば、認可コードによるグラントをサポートする任意の OAuth 2.0 対応サーバーとインターフェースを取ることができます。記事を読み終わる頃には、OAuth 2.0 クライアントの実装について完全に理解し、サンプル・クライアント・コードをダウンロードして独自にコードをテストできるようになっているはずです。
認可コードによるグラント
このグラントは、コンフィデンシャル・クライアント用に最適化されており、アクセス・トークンとリフレッシュ・トークンの両方を取得するために使用されます。このやりとりはリダイレクトをベースとしたフローであるため、クライアントには必須条件として、リソース・オーナーのユーザー・エージェント (通常は Web ブラウザー) とやりとりできること、また認可サーバーから (ユーザー・エージェントからのリダイレクトによって) 送信される要求を受け入れられることが必須条件として求められます。
図 1 に、認可コードによるグラントのフローを示します。
図 1. 認可コードによるグラントのフロー
図 1 に示すフローは、以下のステップで構成されています。
- (A) クライアント (通常は Web アプリケーション) がリソース・オーナーのユーザー・エージェント (通常は Web ブラウザー) を介して認可エンドポイントにリクエストを送信することによって、フローを開始します。クライアントからのリクエストには、クライアント識別子、リクエストのスコープ (適用範囲)、ローカル・ステート、リダイレクト URI が含まれます。このリダイレクト URI は、アクセスが許可 (または拒否) された後、認可サーバーがユーザー・エージェント (通常は Web ブラウザー) をリダイレクトさせるために使用されます。
- (B) リソース・オーナーは、ユーザー・エージェントを介して認可サーバーとの認証を行い、クライアントからのアクセス・リクエストに対する許可または拒否を通知します。
- (C) リソース・オーナーがアクセスを許可した場合、認可サーバーは先ほどのクライアントからのリクエストに含めて渡されたリダイレクト URI か、クライアント登録時に提供されたリダイレクト URI を使用して、ユーザー・エージェント (通常は Web ブラウザー) をクライアントにリダイレクトさせます。その際、リダイレクト URI には、認可コードとクライアントから前に渡されたローカル・ステートが含められます。
- (D) クライアントは、認可サーバーのトークン・エンドポイントからアクセス・トークンを取得するために、前のステップで受け取った認可コードを含めたアクセス・トークン・リクエストを送信します。クライアントはリクエストを送信する際に、クライアント・クレデンシャルを使用して認可サーバーとの認証を行います。クライアントはまた、認可コードを取得するために使用されたリダイレクト URI もアクセス・トークン・リクエストに検証用として含めます。
- (E) 認可サーバーはクライアントの認証を行います。認可サーバーは認可コードの有効性を検査し、渡されたリダイレクト URI が、ステップ (C) でユーザー・エージェントをクライアントにリダイレクトさせるために使用された URI と一致するかどうかを確認します。認可コードが有効で URI が一致した場合、認可サーバーはアクセス・トークンを含めたレスポンスを返します。オフライン・アクセスがリクエストされた場合には、レスポンスにオプションでリフレッシュ・トークンも含めます。
認可コード・リクエスト
認可コード・リクエストは、図 1 で説明したステップ (A) と (B) に該当します。ステップ (A) では、クライアントが認可サーバーに対し、application/x-www-form-urlencoded
形式のリクエストを送信します (リスト 1 を参照)。
リスト 1. 認可コード・リクエストの例
GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1 Host: server.example.com
認可コード・リクエストには、以下のパラメーターが含まれている必要があります。
response_type
: 必須。値は「code
」に設定されている必要があります。client_id
: 必須。クライアント ID です。redirect_uri
: 必須。ユーザー・エージェントをリダイレクトさせるために使用されます。scope
: オプション。アクセス・リクエストのスコープ (適用範囲) を指定します。state
: オプション。リクエストからコールバックまでの間、ステートを維持する場合に使用します。
認可サーバーはリクエストの有効性を確認した後、HTTP リダイレクト・コード 302 のレスポンスをクライアントに送信します。このレスポンスには、HTTP Location
ヘッダーにリダイレクト URI も格納されます。クライアントはステップ (B) で、この URI にユーザー・エージェント (通常は Web ブラウザー) をリダイレクトさせる必要があります。通常、このリダイレクト URI はログイン・ページです。リソース・オーナーはそのログイン・ページで、自身のクレデンシャルを使用してサインインし、クライアントからのリクエストに対してアクセス権を付与することや取り消すことができます。
認可コード・レスポンス
認可コード・レスポンスは、図 1 のステップ (C) に該当します。リソース・オーナーがアクセス・リクエストに対する許可を通知した場合、認可サーバーは認可コードを発行し、ステップ (A) でリクエストの一部として渡されたリダイレクト URI にユーザー・エージェントをリダイレクトさせます。その際、認可コードをリダイレクト URI のクエリー・コンポーネントの一部として application/x-www-form-urlencoded
形式で含めます。
リダイレクト URI のパラメーターは以下のとおりです。:
Code
: 必須。認可サーバーによって生成された認可コードです。この認可コードは一時的なコードであり、生成後、短時間で失効するようにしなければなりません。クライアントが同じ認可コードを複数回使用することはできません。以降のリクエストで同じ認可コードが使用されていると、そのリクエストは認可サーバーによって取り消されます。認可コードは、クライアント識別子とリダイレクト URI に紐付けられています。State
: 必須。クライアントの認可コード・リクエストに state パラメーターが使用されていた場合、このパラメーターの値をクライアントから受信した値どおりに設定する必要があります。
アクセス・トークン・リクエスト
アクセス・トークン・リクエストは、図 1 のステップ (D) に該当します。このステップでは、クライアントがトークン・エンドポイント (認可サーバー) に対し、application/x-www-form-urlencoded
形式のリクエストを送信します (リスト 2 を参照)。
リスト 2. アクセス・トークン・リクエストの例
POST /token HTTP/1.1 Host: server.example.com Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW Content-Type: application/x-www-form-urlencoded grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom&client_id=c342
アクセス・トークン・リクエストには、以下のパラメーターが設定されている必要があります。
grant_type
: 必須。値は「authorization_code
」に設定されている必要があります。client_id
: 必須。クライアント ID です。client_secret
: オプション。認可サーバーとの認証で使用するシークレットです。code
: 必須。認可サーバーから受信した認可コードです。redirect_uri
: 必須。ステップ (A) で送信されたリダイレクト URI と同一でなければなりません。
認可サーバーは、認可コードとリダイレクト URI が有効であることを検証します。コンフィデンシャル・クライアントの場合、認可サーバーはクライアントを認証する際に、リクエストの本体または認可ヘッダーに含めて渡されたクライアント・クレデンシャルも使用します。
アクセス・トークン・レスポンス
アクセス・トークン・レスポンスは、図 1 のステップ (E) に該当します。アクセス・トークン・リクエストが有効であり、認可されると、認可サーバーはアクセス・トークン・レスポンスにアクセス・トークンを含めて返します。リスト 3 に、成功した場合のレスポンスの例を記載します。
リスト 3. 成功した場合のアクセス・トークン・レスポンスの例
HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 Cache-Control: no-store Pragma: no-cache { "access_token":"2YotnFZFEjr1zCsicMWpAA", "token_type":"Bearer", "expires_in":3600, "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA", "example_parameter":"example_value" }
アクセス・トークン・リクエストが有効でない場合や認可されない場合には、それに該当するエラー・メッセージとコードが認可サーバーから返されます。
リフレッシュ・アクセス・トークン・リクエスト
リフレッシュ・アクセス・トークン・リクエストの送信は、クライアントがオフライン・アクセスをリクエストした結果として、アクセス・トークン・レスポンスに refresh_token
が含まれていた場合に該当するオプション・ステップです。アクセス・トークンは一時的なものであり、通常は 1 時間後に失効します。アクセス・トークンが失効すると、クライアントは認証プロセスを繰り返す必要があり、リソース・オーナーはログインして、クライアントが再びアクセス・トークン・リクエストを送信できるよう許可を与える必要もあります。
クライアントがオフライン・アクセスを使用するのは、アクセス・トークンをリフレッシュしなければならない一方で、リソース・オーナーがブラウザーから離れているためにログインしてクライアントを認証できない場合です。クライアントは最初の認可コード・リクエストを送信する際に (ステップ (A) を参照)、オフライン・アクセスをリクエストすることができます。この方式では、認可サーバーがアクセス・トークンに加えてリフレッシュ・トークンを返します。リフレッシュ・トークンは長期間有効なトークンであり、リソース・オーナーによって明示的に取り消されない限り、失効することはありません。アクセス・トークンが失効すると、クライアントはその度に、リフレッシュ・トークンを使用してアクセス・トークンを再生成することができます。この場合、リソース・オーナーがサインインしてアクセス・リクエストを認可する必要はありません。
クライアントはトークン・エンドポイント (認可サーバー) に対し、application/x-www-form-urlencoded
形式のリクエストを送信します (リスト 4 を参照)。
リスト 4. トークン・エンドポイントに対するリクエスト
POST /token HTTP/1.1 Host: server.example.com Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW Content-Type: application/x-www-form-urlencoded grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA
リクエスト・パラメーターは、以下のように定義されます。
grant_type
: 必須。値は「refresh_token
」に設定されている必要があります。refresh_token
: 必須。前のアクセス・トークン・リクエストの結果として取得されたリフレッシュ・トークンです。scope
: オプション。アクセス・リクエストのスコープ (適用範囲) を指定します。
認可サーバーはリフレッシュ・トークンを検証した上で、新しいアクセス・トークンを発行します。
リフレッシュ・アクセス・トークン・レスポンス
リクエストが成功した場合、認可サーバーは新しいアクセス・トークンを発行して返します。リスト 5 に、リクエストが成功した場合のレスポンスの例を記載します。
リスト 5. リフレッシュ・アクセス・トークン・レスポンス
HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 Cache-Control: no-store Pragma: no-cache { "access_token":"2YotnFZFEjr1zCsicMWpAA", "token_type":"Bearer", "expires_in":3600, "example_parameter":"example_value" }
リフレッシュ・アクセス・トークン・リクエストが有効でない場合や認可されない場合には、それに該当するエラー・メッセージとコードが認可サーバーから返されます。
セットアップ
サンプル OAuth2.0 クライアントは、動的 Web プロジェクトです。このプロジェクトとソース・コードが含まれる .war ファイルは、「ダウンロード」セクションからダウンロードすることができます。ダウンロードした .war ファイルをご使用の Eclipse 環境にインポートしてください。
前提条件
開発環境を構築して付属のプロジェクトをインポートするには、Eclipse IDE for Java EE Developers が必要です。Eclipse は、Eclipse のダウンロード・ページからダウンロードすることができます。
OAuth 2.0 Web クライアントを実行するための Apache Tomcat JSP/Servlet コンテナーが必要です。Tomcat は、Apache Tomcat のダウンロード・ページからダウンロードすることができます。
依存関係
クライアント・コード・プロジェクトは、以下の JAR ファイルに依存しています。
- commons-codec-1.6.jar
- commons-logging-1.1.1.jar
- httpclient-4.2.5.jar
- httpclient-cache-4.2.5.jar
- httpcore-4.2.4.jar
- httpmime-4.2.5.jar
- json-simple-1.1.1.jar
上記の 1 から 6 までの JAR ファイルは、「HttpComponents Downloads」ダウンロード・ページからダウンロードすることができる HttpComponents
JAR ファイルに含まれています。json-simple-1.1.1.jar
ファイルは、JSON Simple プロジェクトからダウンロードすることができます。必ず、これらの JAR ファイルを Apache Tomcat インストール・ディレクトリーの lib フォルダーにコピーしてください。必要な JAR ファイルの一部は、デフォルトで既に Tomcat に用意されているはずなので、足りないファイルをコピーするだけでかまいません。
プロジェクトの .war ファイルを Eclipse にインポートする
Eclipse と Apache Tomcat がインストールされた状態になったら、「ダウンロード」セクションから .war ファイルをインポートします。WAR ファイルをインポートするには、Eclipse Web サイトの「Importing Web archive (WAR) files」で概説されている簡単なステップに従ってください。
.war ファイルを Eclipse にインポートする際に、必ず、Tomcat サーバーをプロジェクトに関連付けてください。Tomcat のバージョンを選択し、その Tomcat がインストールされているディレクトリーのルート・パスを指定して、構成を完了する必要があります。
Tomcat サーバーをプロジェクトに関連付ける方法については、「Creating a Tomcat server」に詳しい手順が説明されています。
.war ファイルが正常に Eclipse ワークスペースにインポートされると、この Java リソース・プロジェクト階層の /src フォルダー配下にソース・コードが格納された状態になります。
OAuth 2.0 クライアントを実行する
.war ファイルが正常にインポートされて、必要な JAR ファイルを使用して Tomcat がセットアップされた状態になったら、プロジェクト名「OAuth2.0_AuthCode」を右クリックし、「Run As (実行)」、「Run on Server (サーバーで実行)」の順に選択することで、OAuth 2.0 クライアントを実行することができます。
上述の操作によって、OAuth 2.0 クライアントがサーバーにデプロイされて、index.html ページが Eclipse の内部ブラウザーにロードされます。個人的には、OAuth 2.0 Web クライアントを操作する手段として Eclipse の内部ブラウザーではなく、外部ブラウザーを使用することを選択します。
任意のブラウザーから、http://localhost:8080/OAuth2.0_AuthCode
にナビゲートしてこの OAuth 2.0 Web クライアントにアクセスすることができます。
以降のセクションでは、OAuth 2.0 クライアント・コードを詳細に調べ、よく使われている Salesforce、Google、IBM などの OAuth 2.0 対応のサーバーで、この OAuth 2.0 クライアントをテストする方法を紹介します。
OAuth 2.0 クライアント・コード
この記事のサンプル OAuth 2.0 クライアントは、認可コードによるグラントを実装したものです。これまでの記事で取り上げたグラント・タイプの場合とは異なり、このサンプル OAuth 2.0 クライアント・コードは通常の Java プロジェクトではなく Web アプリケーションとなっています。その理由は、認可コードによるグラントのフローは Web アプリケーションを対象とするようになっており、ユーザー・エージェント (通常は Web ブラウザー) 用に最適化されているためです。
入力パラメーター
OAuth 2.0 クライアントの入力パラメーターは、プロジェクトの src フォルダー内にある Oauth2Client.config プロパティー・ファイルで指定する必要があります。入力パラメーターは以下のとおりです。
scope
: オプション・パラメーター。アクセス・リクエストのスコープ (適用範囲) を表します。サーバーから返されるアクセス・トークンを使用してアクセスできるのは、このパラメーターで指定されているサービスのみです。state
: オプション・パラメーター。クライアントの整合性を確保する目的で、クライアントからリクエストが送信されてから認可サーバーのリダイレクト・レスポンスが返されるまでステートを維持するために使用されます。grant_type
: 認可コードによるグラント・タイプを表す「authorization_code
」に設定されている必要があります。client_id
: リソース・サーバーへのアプリケーション登録時にリソース・サーバーから提供されたクライアント ID またはコンシューマー ID。client_secret
: リソース・サーバーへのアプリケーション登録時にリソース・サーバーから提供されたクライアント・シークレットまたはコンシューマー・シークレット。access_token
: 有効かつ認可されたアクセス・トークン・リクエストに対するレスポンスに含まれる形で、トークン・エンドポイントから返されたアクセス・トークン。認可サーバーから返された認可コードと引き換える形でこのアクセス・トークンは渡されます。refresh_token
: 有効かつ許可されたアクセス・トークン・リクエストに対するレスポンスに含まれる形で、トークン・エンドポイントから返されたリフレッシュ・トークン。リフレッシュ・トークンを使用することで、リソース・オーナーが不在で再認証を行えなくても、失効したアクセス・トークンをリフレッシュすることができます。クライアントは、サーバーに対して明示的にリフレッシュ・トークンをリクエストする必要があります。redirect_uri
: 認可サーバーが認可コード・リクエストの処理の一環としてユーザー・エージェントをリダイレクトさせる先の URI。認可コードは、この URI に対して送信されます。authentication_server_url
: 認可サーバーを表します。認可コードを取得するためのすべてのリクエストは、この URL に送信する必要があります。token_endpoint_url
: トークン・エンドポイントを表します。アクセス・トークンを取得するためのリクエストや、リフレッシュ・トークン・リクエストによってアクセス・トークンをリフレッシュするためのリクエストは、いずれもこの URL に送信する必要があります。resource_server_url
: 保護されたリソースにアクセスするために接続する必要があるリソース・サーバーの URL を表します。これらのリソースにアクセスするには、アクセス・トークンを認可ヘッダーに含めた形でこのリソース・サーバーに渡す必要があります。approval_prompt_key
: 認可サーバーが承認プロンプトの条件を定義するために用いるプロパティーの名前。通常、(Salesforce、Google、IBM などの) 認可サーバーのそれぞれに対し、クライアントがリクエストごとに承認プロンプトを出すようにしたいかどうかを示すカスタム・プロパティーを、認可コード・リクエストに含めて渡す必要があります。Google の場合、そのプロパティーの名前は「approval_prompt
」です。Salesforce の場合は、「login consent
」です。access_type_key
: 認可サーバーがアクセス・タイプの条件を定義するために用いるプロパティーの名前。通常、(Salesforce、Google、IBM などの) 認可サーバーのそれぞれには、クライアントがアクセス・トークン・リクエストの一部としてアクセス・トークンと一緒にリフレッシュ・トークンの発行も求めるかどうかを示すために使用できるカスタム・メソッドがあります。Google では、access_type
プロパティーを指定することでリフレッシュ・トークンをリクエストできるようになっています。Salesforce では、スコープ (適用範囲) の一部として値「refresh_token
」を入力する必要があります。access_type_value
:access_type_key
プロパティーの値。Google の場合、サーバーにアクセス・トークンと一緒にリフレッシュ・トークンも含めるようリクエストするには、値「offline
」を渡す必要があります。
図 2 に、サンプル OAuth 2.0 クライアント・コードの index.html ページを示します。プロジェクトを正常に Eclipse で構成して Tomcat にデプロイしてあれば、このページが表示されるはずです。
図 2. OAuth 2.0 テスト・クライアントのホーム・ページ
このクライアントは、OAuth 2.0 について理解する上で必要な 2 つの基本操作を公開しています。一方の操作で示しているのは、サーバーからアクセス・トークンを取得する方法です。もう一方の操作では、保護されたリソースにアクセスするためにサーバーから提供された既存のアクセス・トークンを使用する方法を示しています。
OAuth 2.0 テスト・クライアントを実行するには、以下のようにします。
- まず、必要なすべての値を Oauth2Client.config ファイルに入力します。
- 「Get Access token (アクセス・トークンの取得)」の横にある「Start Test (テストの開始)」をクリックします。これにより、HTTP GET リクエストが OAuth2Client サーブレットに送信されます。このサーブレットにアクセスするための URI は http://localhost:8080/OAuth2.0_AuthCode/handler です。
- ユーザー・インターフェース・コードは、OAuth2Client サーブレットを呼び出す際にクエリー・パラメーターとして、
caller=token (アクセス・トークンを取得するためのリクエスト), caller=resource (保護されたリソースにアクセスするためのリクエスト)
を渡します。
リスト 6 の OAuth 2.0 クライアント・コードは、OAuth2Client クラスの doGet
メソッドからの抜粋です。
リスト 6. サンプル OAuth 2.0 クライアントの doGet メソッドからの抜粋
String caller = request.getParameter(OAuthConstants.CALLER); String code = request.getParameter(OAuthConstants.CODE); //Load the properties file Properties config = OAuthUtils.getClientConfigProps(OAuthConstants.CONFIG_FILE_PATH); //Generate the OAuthDetails bean from the config properties file OAuth2Details oauthDetails = OAuthUtils.createOAuthDetails(config); //Validate Input List<String> invalidProps = OAuthUtils.validateInput(oauthDetails); if(invalidProps!=null && invalidProps.size() == 0){ //Validation successful if(OAuthUtils.isValid(caller)){ //Request was sent from web application. //Check type of request if(caller.equalsIgnoreCase(OAuthConstants.TOKEN)){ //Request for Access token oauthDetails.setAccessTokenRequest(true); String location = OAuthUtils.getAuthorizationCode(oauthDetails); //Send redirect to location returned by endpoint response.sendRedirect(location); return; } else{ //Request for accessing protected resource if(!OAuthUtils.isValid(oauthDetails.getResourceServerUrl())){ invalidProps.add(OAuthConstants.RESOURCE_SERVER_URL); } if(!OAuthUtils.isValid(oauthDetails.getAccessToken())){ if(!OAuthUtils.isValid(oauthDetails.getRefreshToken())){ invalidProps.add(OAuthConstants.REFRESH_TOKEN); } } if(invalidProps.size() > 0){ sendError(response, invalidProps); return; } Map<String,String> map = OAuthUtils.getProtectedResource(oauthDetails); response.getWriter().println(new Gson().toJson(map)); return; } } else if(OAuthUtils.isValid(code)){ //Callback from endpoint with code. Map<String,String> map = OAuthUtils.getAccessToken(oauthDetails, code); response.getWriter().println(new Gson().toJson(map)); return; } else{ //Invalid request/error response String queryString = request.getQueryString(); String error = "Invalid request"; if(OAuthUtils.isValid(queryString)){ //Endpoint returned an error error = queryString; } response.getWriter().println(error); return; } } else{ //Input is not valid. Send error sendError(response, invalidProps); return; }
リスト 6 に関する注意事項は以下のとおりです。
- OAuth 2.0 クライアントは、クエリー・パラメーターとして
caller
およびcode
を取得します。リクエストが上記のユーザー・インターフェースから送信された場合、caller
パラメーターには有効な値が格納されます。そうでない場合、リクエストは認可サーバーからリダイレクト呼び出しの一環として送られたものであり、code
に有効な値が格納されます。 - 続いて、OAuth 2.0 クライアント・コードは
OAuth2Client.config
ファイル内に指定されたプロパティーを読み取って、OAuthDetails
Bean を作成します。 - 作成された Bean が正しく完全なものであるかどうかが検証されます。無効なプロパティーや欠落しているプロパティーが見つかると、それに応じたエラー・レスポンスが (欠落しているプロパティーや不正確なプロパティーが示された形で) ユーザー・インターフェースに送信されます。
- 次に、OAuth 2.0 クライアント・コードはリクエストされた操作を検証し、該当する操作が呼び出されます。
アクセス・トークン・リクエスト
リスト 7 のコードに、認可コード・リクエストを送信する方法を示します。
リスト 7. 認可コード・リクエストのコード
リスト 7 のコードに関する注意事項は以下のとおりです。
- このコードは、
HttpPost
メソッドを作成するところから始まります。このメソッドは、URL エンコードされたパラメーターを送信するために使用されます。 response_type
、client_id
、redirect_uri
は、必須パラメーターです。リクエストにはこれらのパラメーターが含められます。- 他のオプション・パラメーター (
state
、scope
、approval_prompt_key/value
、access_type_key/value
など) がリクエストに含められるのは、config ファイル内に有効な値が設定されている場合です。 DefaultHttpClient
は、認可サーバーに対してリクエストを送信するために使用されます。- 認可サーバーは、リクエスト・パラメーターの有効性を検査した上で、
Location
ヘッダーを伴う HTTP リダイレクト・コード 302 で応答します。 - コードは、認可サーバーから 302 を受け取ると、レスポンス・ヘッダーから
Location
ヘッダーを取得します。 Location
ヘッダーには、クライアントがユーザー・エージェント (Web ブラウザー) をリダイレクトさせるために必要な URI が含まれています。この URI は通常、リソース・オーナーがサインインしてクライアントを承認するためのログイン・プロンプトになっています。- ロケーション URI の値が、呼び出し側メソッド (
OAuth2Client.doGet()
) に返されます。 Oauth2Client.doGet()
メソッドがレスポンスをロケーション URI にリダイレクトします。- これで、リソース・オーナーのユーザー・エージェント (Web ブラウザー) がログイン・ページ/承認ページにリダイレクトされます。リソース・オーナーはこれらのページにサインインして、リクエストを行ったクライアントを承認する必要があります。
- リソース・オーナーがクライアントを承認した後、認可サーバーは元の認可コード・リクエストで渡されたリダイレクト URI を使用して
code
(認可コード) を送信します。
リスト 8 のコードに、前のステップで受け取った認可コードを使用して最終的なアクセス・トークン・リクエストを送信する方法を示します。
リスト 8. アクセス・トークン・リクエストのサンプル・コード
HttpPost post = new HttpPost(oauthDetails.getTokenEndpointUrl()); String clientId = oauthDetails.getClientId(); String clientSecret = oauthDetails.getClientSecret(); String scope = oauthDetails.getScope(); Map<String, String> map = new HashMap<String, String>(); List<BasicNameValuePair> parametersBody = new ArrayList<BasicNameValuePair>(); parametersBody.add(new BasicNameValuePair(OAuthConstants.GRANT_TYPE, oauthDetails.getGrantType())); parametersBody.add(new BasicNameValuePair(OAuthConstants.CODE, authorizationCode)); parametersBody.add(new BasicNameValuePair(OAuthConstants.CLIENT_ID, clientId)); if (isValid(clientSecret)) { parametersBody.add(new BasicNameValuePair( OAuthConstants.CLIENT_SECRET, clientSecret)); } parametersBody.add(new BasicNameValuePair(OAuthConstants.REDIRECT_URI, oauthDetails.getRedirectURI())); DefaultHttpClient client = new DefaultHttpClient(); HttpResponse response = null; String accessToken = null; try { post.setEntity(new UrlEncodedFormEntity(parametersBody, HTTP.UTF_8)); response = client.execute(post); int code = response.getStatusLine().getStatusCode(); map = handleResponse(response); accessToken = map.get(OAuthConstants.ACCESS_TOKEN); } catch (ClientProtocolException e) { // TODO Auto-generated catch block e.printStackTrace(); throw new RuntimeException(e.getMessage()); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); throw new RuntimeException(e.getMessage()); } return map;
リスト 8 に関する注意事項は以下のとおりです。
- このコードは、
HttpPost
メソッドを使用して、トークン・エンドポイントにアクセス・トークン・リクエストを送信します。 grant_type
、code
、client_id
、redirect_uri
は、必須パラメーターです。client_secret
が有効な場合は、このパラメーターもリクエストに含められます。code
の値は、前のリクエストで認可サーバーから返されたコードです。- リクエストが有効であれば、トークン・エンドポイントはアクセス・トークンと (リクエストがオフライン・アクセスを求めるものであった場合) リフレッシュ・トークンを返します。
- このリクエストの一部として送信されるリダイレクト URI は、認可コード・リクエストの一部として送信されたものと同一でなければならないことに注意してください。トークン・エンドポイントは、このリダイレクト URI がクライアントのアプリケーション (トークン・エンドポイントで使用可能なデータを含むアプリケーション) で指定されたリダイレクト URI と一致することを検証します。
リフレッシュ・アクセス・トークン
前述のとおり、アクセス・トークンは、通常はその性質上、一時的なものであり、標準的な有効期間は 1 時間です。クライアントがリフレッシュ・トークンを持っていれば、リソース・オーナーがサインインしてクライアントを再認証しなくても、失効したアクセス・トークンを自動的にリフレッシュすることができます。この仕組みは、リスト 9 のコードに説明されています。
リスト 9. リフレッシュ・アクセス・トークンのサンプル・コード
String clientId = oauthDetails.getClientId(); String clientSecret = oauthDetails.getClientSecret(); String scope = oauthDetails.getScope(); String refreshToken = oauthDetails.getRefreshToken(); Map<String, String> map = new HashMap<String, String>(); if (!isValid(refreshToken)) { throw new RuntimeException( "Please provide valid refresh token in config file"); } List<BasicNameValuePair> parametersBody = new ArrayList<BasicNameValuePair>(); parametersBody.add(new BasicNameValuePair(OAuthConstants.GRANT_TYPE, OAuthConstants.REFRESH_TOKEN)); parametersBody.add(new BasicNameValuePair(OAuthConstants.REFRESH_TOKEN, oauthDetails.getRefreshToken())); if (isValid(clientId)) { parametersBody.add(new BasicNameValuePair(OAuthConstants.CLIENT_ID, clientId)); } if (isValid(clientSecret)) { parametersBody.add(new BasicNameValuePair( OAuthConstants.CLIENT_SECRET, clientSecret)); } if (isValid(scope)) { parametersBody.add(new BasicNameValuePair(OAuthConstants.SCOPE, scope)); } DefaultHttpClient client = new DefaultHttpClient(); HttpResponse response = null; try { post.setEntity(new UrlEncodedFormEntity(parametersBody, HTTP.UTF_8)); response = client.execute(post); int code = response.getStatusLine().getStatusCode(); map = handleResponse(response); } catch (ClientProtocolException e) { // TODO Auto-generated catch block e.printStackTrace(); throw new RuntimeException(e.getMessage()); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); throw new RuntimeException(e.getMessage()); } return map;
リスト 9 に関する注意事項は以下のとおりです。
- このコードは、
HttpPost
メソッドを使用して、トークン・エンドポイントにリクエストを送信します。 grant_type
とrefresh_token
は必須パラメーターです。client_id
、client_secret
、scope
が有効な場合は、これらのパラメーターもリクエストに含められます。- リクエストが有効な場合、トークン・エンドポイントは新しいアクセス・トークンを返します。
保護されたリソースにアクセスする
リスト 10 のコードには、保護されたリソースにアクセスするために、アクセス・トークンを使用する方法が示されています。このリストに示されているように、アクセス・トークンが失効している場合は、アクセス・トークンをリフレッシュしてから、保護されたリソースへのアクセスを再試行します。
リスト 10. 保護されたリソースにアクセスするためのサンプル・コード
リスト 10 に関する注意事項は以下のとおりです。
- このメソッドは、config ファイルから取得した値が設定された
OauthDetails
Bean を受け入れます。 - 名前からわかるように、このメソッドは単純な
HttpGet
メソッドを使用して、保護されたリソースをリソース・サーバーから取得しようとします。 - リソース・サーバーとの認証を行うには、アクセス・トークンを認可ヘッダーに含めて送信する必要があります。例えば、
Authorization: Bearer accessTokenValue
のようにします。 - コードは
DefaultHttpClient
を作成して、リソース・サーバーに GET リクエストを送信します。 - リソース・サーバーから受け取ったレスポンス・コードが 401 または 403 の場合、認証に使用されたアクセス・トークンはおそらく失効しているか無効です。
- 次のステップとなるのは、アクセス・トークンを再生成することです (リスト 9 を参照)。
- アクセス・トークンが正常に再生成された後、コードは
OauthDetails
Bean に含まれるアクセス・トークン値を更新します。また、GET
メソッドの既存の認可ヘッダーを新しいアクセス・トークン値で置き換えます。 - コードはあらためて、保護されたリソースにアクセスするためのリクエストを送信します。
- アクセス・トークンが有効であり、かつリソース・サーバーの URL が正しければ、コンソールでレスポンスの内容を確認できるはずです。
Salesforce.com でのクライアントのテスト
Salesforce.com に登録する
SaaS (Software as a Service) アプリケーションとしてよく使われている Salesforce.com は、OAuth 2.0 の認可コードによるグラント・タイプをサポートしています。
Salesforce.com に登録する方法は以下のとおりです。
- 「
Login
(ログイン)」をクリックし、「Sign up for free (無料サインアップ)」をクリックします。 - 登録を完了し、クレデンシャルを受け取ります。
- ユーザー名とパスワードに加え、セキュリティー・トークンも受け取ります。アクセス・トークン・リクエストを送信するために入力するパスワードは、このパスワードとセキュリティー・トークンを連結したものでなければなりません (例:
password12312123
)。 - salesforce.com でアプリケーションを作成する方法については、「参考文献」セクションに有用な記事へのリンクが記載されています。
- 認可コードによるグラント・タイプの場合、salesforce.com ではアプリケーションの作成時にユーザーが指定したリダイレクト URI を使用してユーザーとやりとりします。
Salesforce.com でサンプル・クライアントを実行する
Salesforce で OAuth 2.0 対応のサーバーをセットアップできたので、保護された情報をサーバーから取得できるかどうか、クライアントをテストする準備が整いました。
この Java リソース・プロジェクトの /src フォルダー配下に、Oauth2Client_salesforce.config というファイルがあります。この構成ファイルは salesforce.com 用にカスタマイズされていて、Salesforce.com に対してテストする構成値を指定する際のテンプレートとして利用することができます。
前に説明したように Tomcat サーバーで Oauth2Client Web アプリケーションを実行し、クライアントのテスト開始ページを表示します (図 3 を参照)。
図 3. Salesforce でクライアントをテストするページ
図 3 に示されているクライアントのテスト・ページで、「Get Access token (アクセス・トークンの取得)」の横にある「Start Test (テストの開始)」をクリックします。すると次に表示されるのは、Salesforce でのアクセス・トークン・リクエスト用のログイン・プロンプトです (図 4 を参照)。
図 4. Salesforce でのアクセス・トークン・リクエスト (ログイン・プロンプト)
Salesforce.com から 302 のレスポンスと Location
ヘッダーを受け取った後、クライアントは図 4 に示されている Salesforce ログイン・ページにユーザー・エージェント (Web ブラウザー) をリダイレクトします。
ログインすると、Salesforce でのアクセス・トークン・リクエストの承認プロンプトが表示されます (図 5 を参照)。
図 5. Salesforce でのアクセス・トークン・リクエスト (承認プロンプト)
図 5 に示されている承認プロンプトを使用して、リソース・オーナーは (ログイン後に) VarunOAuth2.0 アプリケーション (リソース・オーナーのデータへのアクセスをリクエストするサンプル・コード) へのアクセス権を付与します。
Salesforce でのアクセス・トークンの出力
Salesforce での承認プロンプトでクライアントにアクセス権を付与すると、トークン・エンドポイントはアクセス・トークンとリフレッシュ・トークンおよびその他の Salesforce 固有のデータが含まれたレスポンスを返します。Eclipse コンソール・ウィンドウには、リスト 11 に記載する出力が表示されるはずです。
リスト 11. Salesforce でのアクセス・トークンの出力
Salesforce サーバーからユーザー情報を取得する
アクセス・トークンを入手できたので、Salesforce からリソース・オーナーのアカウント情報にアクセスするためのリクエストを送信することができます。このリクエストには、OAuth 2.0 認証が必要です。
Oauth2Client.confg ファイルを更新して、入手したアクセス・トークンとリフレッシュ・トークンを反映させます。また、リソース・サーバー URL プロパティーに、テスト対象のリソース・サーバーの URL を設定します。例えば、リソース・サーバー URL プロパティーに、Salesforce からアクセス・トークン・レスポンスの一部として返された id
フィールドの値を設定することができます。この id
フィールドの値が、リソース・オーナーのアカウント情報にアクセスする際に使用されます (例: https://login.salesforce.com/id/00D90000000mQaYEAU/00590000001HCB7AAO
)。
プロジェクトを最新の状態に更新して、Tomcat サーバーで再実行します。「Access Protected Resource (保護されたリソースへのアクセス)」の横にある「Start Test (テストの開始)」をクリックしてください。
Eclipse コンソール・ウィンドウに、リスト 12 のような出力が表示されるはずです。
リスト 12. Salesforce での保護されたリソースの出力
このとおり、OAuth 2.0 を使用した認証によって、Salesforce から、リソース・オーナーのアカウント情報に正常にアクセスすることができます。構成ファイル内に指定されたアクセス・トークンが失効すると、クライアントは次のリクエストで自動的にアクセス・トークンを再生成し、その再生成されたアクセス・トークンを使用して、リソース・サーバーの URL から入手できる、保護されたリソースを取得します。
Google に登録する
Google では OAuth 2.0 を使用して、GoogleDrive、TaskQueue、CloudSQL をはじめとする各種サービスにアクセスするために使用可能な API に対する認証を行っています。OAuth 2.0 クライアントを Google でテストするためのアプリケーションをセットアップするには、https://developers.google.com/accounts/docs/OAuth2?hl=ES に記載されている説明に従ってください。
Google でクライアントを実行する
OAuth 2.0 対応サーバーのセットアップが完了したので、保護された情報をサーバーから取得できるかどうか、クライアントをテストする準備が整いました。
この Java リソース・プロジェクトの /src フォルダー配下に、Oauth2Client_Google.config というファイルがあります。この構成ファイルは Google サービス用にカスタマイズされていて、構成値を指定する際のテンプレートとして利用することができます。
前に説明したように Tomcat サーバーで Oauth2Client Web アプリケーションを実行します。図 6 に、「Get Access token (アクセス・トークンの取得)」操作と「Access Protected Resources (保護されたリソースへのアクセス)」操作のフローおよび出力を示します。
図 6. アクセス・トークン・リクエスト (ログイン・プロンプト)
Google から 302 のレスポンスと Location
ヘッダーを受け取った後、クライアントは図 6 に示されている Google ログイン・ページにユーザー・エージェント (Web ブラウザー) をリダイレクトさせます。
図 7 に示されている承認プロンプトを使用して、リソース・オーナーは (ログイン後に) Cast Iron アプリケーション (リソース・オーナーのデータへのアクセスをリクエストするアプリケーション) へのアクセス権を付与します。
図 7. アクセス・トークン・リクエスト (承認プロンプト)
リソース・オーナーがクライアントにアクセス権を付与すると、トークン・エンドポイントはアクセス・トークンとリフレッシュ・トークンが含まれたレスポンスを返します。コンソール・ウィンドウには、リスト 13 に示すような出力が表示されるはずです。
リスト 13.
********** Response Received ********** expires_in = 3600 token_type = Bearer refresh_token = 1/TtCxaFlKMRsHeIlxrY-2ZJIO8DcRmQEiQ_2Wxw8 access_token = ya29.ZQDpI-ahF6TMURwAAABqBu-2-U0_lUWfbwh053j3db3PzaNXV4k_k6fc_VT7uQ
Google サーバーからユーザー情報を取得する
アクセス・トークンを入手できたので、リソース・オーナーの GoogleDrive 情報にアクセスするためのリクエストを送信することができます。このアクセス・リクエストには、OAuth 2.0 認証が必要です。
Oauth2Client.confg ファイルを更新して、入手したアクセス・トークンとリフレッシュ・トークンを反映させて、リソース・サーバーの url プロパティーにテスト対象のリソース・サーバーの URL
を設定します。ここでは、リソース・サーバーの URL を https://www.googleapis.com/drive/v2/about で置き換えます。この URL は、GoogleDrive アカウント情報の URL です。
プロジェクトを最新の状態に更新して、Tomcat サーバーで再実行します。「Access Protected Resource (保護されたリソースへのアクセス)」の横にある「Start Test (テストの開始)」をクリックしてください。
コンソール・ウィンドウには、リスト 14 に示すスニペットのような出力が表示されるはずです。
リスト 14.
このとおり、OAuth 2.0 を使用した認証によって、GoogleDrive から、リソース・オーナーのアカウント情報に正常にアクセスすることができます。
構成ファイル内に指定されたアクセス・トークンが失効すると、クライアントは次のリクエストで自動的にアクセス・トークンを再生成し、その再生成されたアクセス・トークンを使用して、リソース・サーバー URL から入手できる、保護されたリソースを取得します。
IBM エンドポイントでのクライアントのテスト
このクライアント・サンプル・コードは、IBM Websphere Application Server および IBM DataPower といった OAuth 2.0 対応の IBM エンドポイントを使用してテスト済みです。
OAuth 2.0 サーバーを Websphere Application Server 上に構築する手順は、「Using OAuth: Enabling the OAuth service provider in WebSphere Application Server」で説明されています。
IBM エンドポイントをテストする方法は、Salesforce や Google でのテスト方法と同じです。
まとめ
このチュートリアル・シリーズの第 3 回では、認可コードによるグラント・タイプの基礎として、複数の OAuth 2.0 対応エンドポイントに接続して、そのエンドポイントから保護されたリソースを取得できる、汎用の OAuth 2.0 クライアントを Java コードで作成する方法をデモンストレーションしました。「ダウンロード」セクションから入手できるサンプル・クライアント Web プロジェクトは、Eclipse 環境にインポートして、カスタマイズした OAuth 2.0 クライアントを作成する際の出発点として使用することができます。
ダウンロード
内容 | ファイル名 | サイズ |
---|---|---|
Sample Java code for Authorization code grant type | OAuth2.0_AuthCode_part_3.war | 25KB |
参考文献
- このチュートリアル・シリーズの他の記事を読んでください。
- 「OAuth 2.0 Authorization Framework」の詳細を学んでください。
- Websphere Application Server 上で OAuth 2.0 を構築する方法を調べてください。
コメント
IBM PureSystems
IBM がどのように IT に革命をもたらしているのかをご自身でお確かめください
Knowledge path
developerWorks の Knowledge path シリーズでは、テーマ別の学習資料をご提供しています
ソフトウェア評価版: ダウンロード
developerWorksでIBM製品をお試しください!