概要
目的: このドキュメントでは、GoogleCredential ユーティリティ クラスを使用して Google サービスで OAuth 2.0 認証を行う方法について説明します。Google が提供する汎用 OAuth 2.0 関数については、OAuth 2.0 と Java 用 Google OAuth クライアント ライブラリをご覧ください。
概要: Google サービスに保存されている保護データにアクセスするには、承認に OAuth 2.0 を使用します。Google API は、さまざまな種類のクライアント アプリケーションの OAuth 2.0 フローをサポートしています。これらのフローでは、クライアント アプリケーションは、クライアント アプリケーションとアクセスされる保護データの所有者のみに関連付けられたアクセス トークンをリクエストします。アクセス トークンは、クライアント アプリケーションがアクセスできるデータの種類(「タスクの管理」など)を定義する限定されたスコープにも関連付けられます。OAuth 2.0 の重要な目標は、保護されたデータへの安全で便利なアクセスを提供しながら、アクセス トークンが盗まれた場合の影響を最小限に抑えることです。
Java 用 Google API クライアント ライブラリの OAuth 2.0 パッケージは、汎用の Java 用 Google OAuth 2.0 クライアント ライブラリ上に構築されています。
詳しくは、次のパッケージの Javadoc ドキュメントをご覧ください。
- com.google.api.client.googleapis.auth.oauth2 (google-api-client から)
- com.google.api.client.googleapis.extensions.appengine.auth.oauth2 (google-api-client-appengine から)
Google API Console
Google API にアクセスする前に、クライアントがインストールされたアプリケーション、モバイル アプリケーション、ウェブサーバー、ブラウザで実行されるクライアントのいずれであっても、認証と課金の目的で Google API Console でプロジェクトを設定する必要があります。
認証情報を正しく設定する手順については、API コンソールのヘルプをご覧ください。
認証情報
GoogleCredential
GoogleCredential は、アクセス トークンを使用して保護されたリソースにアクセスするための OAuth 2.0 のスレッドセーフなヘルパークラスです。たとえば、アクセス トークンがすでにある場合は、次のようにリクエストできます。
GoogleCredential credential = new GoogleCredential().setAccessToken(accessToken); Plus plus = new Plus.builder(new NetHttpTransport(), GsonFactory.getDefaultInstance(), credential) .setApplicationName("Google-PlusSample/1.0") .build();
Google App Engine の ID
この代替認証情報は、Google App Engine App Identity Java API に基づいています。クライアント アプリケーションがエンドユーザーのデータへのアクセスをリクエストする認証情報とは異なり、App Identity API はクライアント アプリケーション自身のデータへのアクセスを提供します。
AppIdentityCredential(google-api-client-appengine から)を使用します。この認証情報は、Google App Engine がすべての詳細を処理するため、はるかにシンプルです。必要な OAuth 2.0 スコープのみを指定します。
urlshortener-robots-appengine-sample からのコード例:
static Urlshortener newUrlshortener() { AppIdentityCredential credential = new AppIdentityCredential( Collections.singletonList(UrlshortenerScopes.URLSHORTENER)); return new Urlshortener.Builder(new UrlFetchTransport(), GsonFactory.getDefaultInstance(), credential) .build(); }
データストア
アクセス トークンは通常 1 時間で期限切れになります。期限切れ後にそのトークンを使用しようとするとエラーが返されます。トークンは GoogleCredential によって自動的に「更新」(単純な言い方では、新しいアクセス トークンが取得)されます。これは、有効期間の長い更新トークンを使用して行われます。通常、認証コードフローで access_type=offline パラメータを使用すると、アクセス トークンとともに更新トークンが受信されます(GoogleAuthorizationCodeFlow.Builder.setAccessType(String) を参照)。
ほとんどのアプリケーションでは、認証情報のアクセス トークンや更新トークンを永続化する必要があります。認証情報のアクセス トークンや更新トークンを永続化するには、StoredCredential を使用して DataStoreFactory の独自の実装を提供するか、ライブラリが提供する次のいずれかの実装を使用します。
- AppEngineDataStoreFactory: Google App Engine Data Store API を使用して認証情報を保持します。
- MemoryDataStoreFactory: 認証情報をメモリに「永続化」します。これは、プロセスのライフタイムの短期保存としてのみ有用です。
- FileDataStoreFactory: 認証情報をファイルに保持します。
AppEngine ユーザー: AppEngineCredentialStore は非推奨となり、まもなく削除されます。StoredCredential で AppEngineDataStoreFactory を使用することをおすすめします。古い方法で認証情報を保存している場合は、追加されたヘルパー メソッド migrateTo(AppEngineDataStoreFactory) または migrateTo(DataStore) を使用して移行できます。
DataStoreCredentialRefreshListener を使用し、GoogleCredential.Builder.addRefreshListener(CredentialRefreshListener) を使用して認証情報に設定できます。
認可コードフロー
認可コードフローを使用して、エンドユーザーが Google APIs の保護されたデータへのアクセス権をアプリケーションに付与できるようにします。このフローのプロトコルは、認証コード権限付与で指定されています。
このフローは GoogleAuthorizationCodeFlow を使用して実装されます。ステップは次のとおりです。
- エンドユーザーがアプリケーションにログインします。そのユーザーを、アプリに固有のユーザー ID に関連付ける必要があります。
- ユーザー ID に基づいて AuthorizationCodeFlow.loadCredential(String) を呼び出し、エンドユーザーの認証情報がすでにわかっているかどうかを確認します。その場合は、これで完了です。
- そうでない場合は、AuthorizationCodeFlow.newAuthorizationUrl() を呼び出し、エンドユーザーのブラウザを認証ページにリダイレクトして、保護されたデータへのアクセス権をアプリケーションに付与します。
- Google 認証サーバーは、
codeクエリ パラメータとともに、アプリケーションで指定されたリダイレクト URL にブラウザをリダイレクトします。codeパラメータを使用して、AuthorizationCodeFlow.newTokenRequest(String)) を使用してアクセス トークンをリクエストします。 - AuthorizationCodeFlow.createAndStoreCredential(TokenResponse, String) を使用して、保護されたリソースにアクセスするための認証情報を保存して取得します。
また、GoogleAuthorizationCodeFlow を使用していない場合は、下位レベルのクラスを使用できます。
- DataStore.get(String) を使用して、ユーザー ID に基づいてストアから認証情報を読み込みます。
- GoogleAuthorizationCodeRequestUrl を使用して、ブラウザを認証ページにリダイレクトします。
- AuthorizationCodeResponseUrl を使用して、認証レスポンスを処理し、認証コードを解析します。
- GoogleAuthorizationCodeTokenRequest を使用して、アクセス トークンと更新トークンをリクエストします。
- 新しい GoogleCredential を作成し、DataStore.set(String, V) を使用して保存します。
GoogleCredentialを使用して保護されたリソースにアクセスします。有効期限切れのアクセス トークンは、更新トークンを使用して自動的に更新されます(該当する場合)。DataStoreCredentialRefreshListener を使用し、GoogleCredential.Builder.addRefreshListener(CredentialRefreshListener) を使用して認証情報を設定してください。
Google API Console でプロジェクトを設定する際に、使用するフローに応じてさまざまな認証情報を選択します。詳しくは、OAuth 2.0 の設定と OAuth 2.0 のシナリオをご覧ください。各フローのコード スニペットは以下のとおりです。
ウェブサーバー アプリケーション
このフローのプロトコルについては、ウェブサーバー アプリケーションに OAuth 2.0 を使用するをご覧ください。
このライブラリは、基本的なユースケースの認可コードフローを大幅に簡素化するサーブレット ヘルパークラスを提供します。AbstractAuthorizationCodeServlet と AbstractAuthorizationCodeCallbackServlet(google-oauth-client-servlet から)の具体的なサブクラスを指定して、web.xml ファイルに追加するだけです。ウェブ アプリケーションのユーザー ログインを処理し、ユーザー ID を抽出する必要があることに注意してください。
public class CalendarServletSample extends AbstractAuthorizationCodeServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { // do stuff } @Override protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException { GenericUrl url = new GenericUrl(req.getRequestURL().toString()); url.setRawPath("/oauth2callback"); return url.build(); } @Override protected AuthorizationCodeFlow initializeFlow() throws IOException { return new GoogleAuthorizationCodeFlow.Builder( new NetHttpTransport(), GsonFactory.getDefaultInstance(), "[[ENTER YOUR CLIENT ID]]", "[[ENTER YOUR CLIENT SECRET]]", Collections.singleton(CalendarScopes.CALENDAR)).setDataStoreFactory( DATA_STORE_FACTORY).setAccessType("offline").build(); } @Override protected String getUserId(HttpServletRequest req) throws ServletException, IOException { // return user ID } } public class CalendarServletCallbackSample extends AbstractAuthorizationCodeCallbackServlet { @Override protected void onSuccess(HttpServletRequest req, HttpServletResponse resp, Credential credential) throws ServletException, IOException { resp.sendRedirect("/"); } @Override protected void onError( HttpServletRequest req, HttpServletResponse resp, AuthorizationCodeResponseUrl errorResponse) throws ServletException, IOException { // handle error } @Override protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException { GenericUrl url = new GenericUrl(req.getRequestURL().toString()); url.setRawPath("/oauth2callback"); return url.build(); } @Override protected AuthorizationCodeFlow initializeFlow() throws IOException { return new GoogleAuthorizationCodeFlow.Builder( new NetHttpTransport(), GsonFactory.getDefaultInstance() "[[ENTER YOUR CLIENT ID]]", "[[ENTER YOUR CLIENT SECRET]]", Collections.singleton(CalendarScopes.CALENDAR)).setDataStoreFactory( DATA_STORE_FACTORY).setAccessType("offline").build(); } @Override protected String getUserId(HttpServletRequest req) throws ServletException, IOException { // return user ID } }
Google App Engine アプリケーション
App Engine の認可コードフローは、サーブレットの認可コードフローとほぼ同じですが、Google App Engine の Users Java API を利用できる点が異なります。Users Java API を有効にするには、ユーザーがログインしている必要があります。まだログインしていないユーザーをログイン ページにリダイレクトする方法については、セキュリティと認証(web.xml 内)をご覧ください。
サーブレットの場合との主な違いは、AbstractAppEngineAuthorizationCodeServlet と AbstractAppEngineAuthorizationCodeCallbackServlet(google-oauth-client-appengine から)の具体的なサブクラスを指定することです。抽象サーブレット クラスを拡張し、Users Java API を使用して getUserId メソッドを実装します。AppEngineDataStoreFactory(google-http-client-appengine から)は、Google App Engine Data Store API を使用して認証情報を永続化するのに適したオプションです。
calendar-appengine-sample から(少し変更して)取得した例:
public class CalendarAppEngineSample extends AbstractAppEngineAuthorizationCodeServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { // do stuff } @Override protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException { return Utils.getRedirectUri(req); } @Override protected AuthorizationCodeFlow initializeFlow() throws IOException { return Utils.newFlow(); } } class Utils { static String getRedirectUri(HttpServletRequest req) { GenericUrl url = new GenericUrl(req.getRequestURL().toString()); url.setRawPath("/oauth2callback"); return url.build(); } static GoogleAuthorizationCodeFlow newFlow() throws IOException { return new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT, JSON_FACTORY, getClientCredential(), Collections.singleton(CalendarScopes.CALENDAR)).setDataStoreFactory( DATA_STORE_FACTORY).setAccessType("offline").build(); } } public class OAuth2Callback extends AbstractAppEngineAuthorizationCodeCallbackServlet { private static final long serialVersionUID = 1L; @Override protected void onSuccess(HttpServletRequest req, HttpServletResponse resp, Credential credential) throws ServletException, IOException { resp.sendRedirect("/"); } @Override protected void onError( HttpServletRequest req, HttpServletResponse resp, AuthorizationCodeResponseUrl errorResponse) throws ServletException, IOException { String nickname = UserServiceFactory.getUserService().getCurrentUser().getNickname