GeekFactory

int128.hatenablog.com

12/7に行われたGoogle API OAuth 2.0の仕様変更について

12/8あたりからGoogle APIのOAuth 2.0の挙動が変わりました。Google Groupsのポストをたどると、12/7に仕様変更が行われたようです。変更点について説明している記事を見つけたので、日本語に訳してみました。変なところがあったらご指摘頂けると助かります。

Using OAuth 2.0 to Access Google APIs - Google Accounts Authentication and Authorization — Google Developers も合わせて読むことをおすすめします。



Upcoming changes to OAuth 2.0 endpoint - The official Google Code blog
(訳注:この記事は2011/10/xxに書かれて、2011/11/14にアップデートされています)

近日中に行われる OAuth 2.0 エンドポイントの仕様変更について

In the coming weeks we will be making three changes to the experimental OAuth 2.0 endpoint. We expect the impact to be minimal, and we’re emailing developers who are most likely to be affected.
今後数週間のうちにOAuth 2.0エンドポイントに対して3つの変更を行います。我々は最小限の影響で済むことを期待しています。また、影響を受ける可能性が高い開発者にメールを送っています。

We will be releasing these changes on December 7, 2011. This post describes the changes, their impact, and how they can be mitigated.
我々はこれらの変更を2011/12/7にリリースする予定です。この記事では変更点や影響、対処法を説明します。

Change #1: Error responses for client-side web applications

変更点1: client-side flow におけるエラーレスポンス

The first change relates to the way errors are returned in OAuth 2.0 client-side web applications. It does not impact server-side, native, or device flows.
最初の変更は、client-side flow のアプリにおけるエラーの返し方についてです。server-side flow, native flow, device flow には影響しません。

The current behavior of the OAuth 2.0 endpoint in certain error conditions is to return the error to the application as a query string parameter, for example:
現在、OAuth 2.0 のエンドポイントでは特定のエラーにおいて、クエリパラメータでアプリにエラーを返しています。例えば、このようにしています:

https://www.example.com/back?error=access_denied.

The OAuth 2.0 specification indicates that the error should be returned in the fragment of the response. We are updating our OAuth 2.0 implementation to support the most recent draft of the specification. As a result, we will be changing the way we return errors to applications in the client-side flow.
OAuth 2.0 の仕様では、レスポンスのフラグメントでエラーを返すように規定されています。最新のドラフト仕様をサポートするように実装をアップデートするので、client-side flow におけるエラーの返し方が変わります。

As an example, today an error returns to your application as
例えば、現在はアプリにこのようなエラーが返されます:

https://www.example.com/back?error=access_denied.

After this change, it will be returned as
変更後はこのようになります:

https://www.example.com/back#error=access_denied. 

There is no mitigation for this change, so your application will have to handle these types of errors in client-side script.
この変更の対処法はないので(訳注:コードを直さずに対処するのは無理なので)、アプリのエラーハンドリングを見直す必要があります。

Change #2: Offline access as a separate parameter

変更点2:オフラインアクセスのパラメータ指定

The second change impacts the OAuth 2.0 server-side flow only. It does not impact client-side, native, or device flows. For context, this flow consists of the following steps:
2つ目の変更は server-side flow のみに影響します。client-side flow, native flow, device flow には影響しません。server-side flow は以下のステップから成ります:

  1. Redirect the browser to the Google OAuth 2.0 endpoint.
    ブラウザをエンドポイントにリダイレクトします。
  2. The user will be shown a consent page.
    ユーザに同意を求めるページが表示されます。
  3. If the user consents, parse the authorization code from the query string of the response.
    ユーザが同意すると、(訳注:リダイレクトを経由して)レスポンスのクエリ文字列から認可コードを取り出します。
  4. Exchange the authorization code for a short-lived access token and a long-lived refresh token.
    認可コードをアクセストークンやリフレッシュトークンと交換します。アクセストークンは短時間だけ使えるもの、リフレッシュトークンは長期間使えるものです。

Once your application has obtained a long-lived refresh token (step 4), it may access a Google API at any time. This means server-side applications do not require the end-user to be present when obtaining new access tokens. We’re calling this type of access offline.
ステップ4でアプリがリフレッシュトークンを受け取ったら、いつでもGoogle APIにアクセスできるようになります。つまり、サーバサイドのアプリはエンドユーザに確認しなくても新しいアクセストークンを取得できます。これをオフラインアクセスと呼びます。

The client-side flow, in contrast, requires the user to be present when obtaining an access token. This type of access is called online.
一方で client-side flow だと、アクセストークンを取得するにはエンドユーザの確認が必要です。これはオンラインアクセスと呼びます。

With this change, we will be exposing online and offline access as a separate parameter that’s available only in the server-side flow.
今回の変更で、オンラインとオフラインをパラメータで指定するようにしました。server-side flow だけで使えます。

When your application requests offline access, the consent page shown to a user will reflect that your application requests offline access and your application will receive an access and a refresh token. Once your application has a refresh token, it may obtain a new access token at any time.
アプリがオフラインアクセスを要求した場合、ユーザの同意画面にはアプリがオフラインアクセスを要求している旨が表示されます。そして、アプリはアクセストークンとリフレッシュトークンを受け取ります。リフレッシュトークンを持っていれば、いつでも新しいアクセストークンを取得できます。

When your application requests online access, your application will only receive an access token. No refresh token will be returned. This means that a user must be present in order for your application to obtain a new access token.
アプリがオンラインアクセスを要求した場合、アプリはアクセストークンしかもらえません。リフレッシュトークンはもらえないのです。つまり、アプリが新しいアクセストークンを取得するには(いちいち)ユーザに確認しなければならないのです。

If unspecified in the request, online is the default.
トークン要求で指定されていない場合はオンラインがデフォルトです。(訳注:これまではオフラインがデフォルトでした。)

A mitigation for this change is described at the end of this post.
今回の変更の対処法は最後のほうに書いてます。

Change #3: Server-side auto-approval

変更点3:server-side flow における自動承認

This change also impacts the OAuth 2.0 server-side flow only.
この変更も server-side flow だけに影響します。

In the current implementation of OAuth2, every time your application redirects a user to Google, that user must give explicit consent before an authorization code is given to your application. As a result, sending a user through the flow another time requires them to see the consent screen again.
現在の実装では、アプリは認可コードを取得する度にユーザをGoogleにリダイレクトし、ユーザが明示的に同意しなければなりません。結果的に、ユーザが別の機会にアプリを使うとまた同意画面を見ることになります。

Most applications don’t do this, but rather use the existing server-side flow as it was intended: a one-time association (import contacts, calendar operations, etc.) where the result is a refresh token which may be used to obtain new access tokens.
ほとんどのアプリはこんなことやってなくて、むしろ server-side flow を one-time association として使っています。新しいアクセストークンを取得するためにリフレッシュトークンを使っているのです。(訳注:すみません、ここよく分かりませんでした><)

The behavior is changing to the following:
今回の変更で挙動はこのように変わります:

  • Users will only see the consent screen on their first time through the sequence.
    ユーザは最初だけ(訳注:アプリを使い始めるときだけ)同意画面が見えます。
  • If the application requests offline access, only the first authorization code exchange results in a refresh token.アプリがオフラインアクセスを要求した場合、最初に認可コードを交換するときだけアプリにリフレッシュトークンを渡します。

To put it another way, consent will be auto-approved for returning users unless the user has revoked access. Refresh tokens are not returned for responses that were auto-approved.
ざっくり言うと、同意確認に対して自動的に承認されるようになります。ユーザがアプリのアクセス許可*1を取り消さない限りはね。自動承認されたときは、リフレッシュトークンはアプリに返されません。

The next section describes how to mitigate this change.
次のセクションでこの変更の対処法を説明します。

Mitigation of offline access (#2) and auto-approval (#3) changes

変更点2と変更点3の対処法

If you want to keep the existing behavior in your server-side applications, include the approval_prompt=force and access_type=offline parameters in an authorization code request.
もし server-side flow のアプリで今までの挙動を維持したいのなら、認可コード要求のパラメータに approval_prompt=forceaccess_type=offline を入れてください。

For example, if the following is a target URL for obtaining an authorization code today:
今の時点では、認可コードを取得するURLはこのようなものです:

https://accounts.google.com/o/oauth2/auth?
client_id=21302922996.apps.googleusercontent.com&
redirect_uri=https://www.example.com/back&
scope=https://www.google.com/m8/feeds/&
response_type=code

You can maintain the current behavior by changing the target URL to:
現在の挙動を維持したいのならURLをこのように変更します:

https://accounts.google.com/o/oauth2/auth?
client_id=21302922996.apps.googleusercontent.com&
redirect_uri=https://www.example.com/back&
scope=https://www.google.com/m8/feeds/&
response_type=code&
access_type=offline&
approval_prompt=force

You may start including these parameters in authorization code requests today.
(訳注:まだ変更がリリースされていませんが、)今から認可コード要求にこれらのパラメータを含めても大丈夫です。

12/11追記:文脈により "server side" の訳を "server-side flow" に変えました。

*1:アプリのアクセス許可はGoogleのアカウント情報で確認できます。アカウント情報の画面から自動承認になってるアプリを取り消したりできます。