OAuth 2.0 の仕組みと認証方法
OAuth 2.0 とは
映画「フェリスはある朝突然に」では、駐車場係がフル整備された 1961 年もののフェラーリを勝手に乗り回すシーンが登場します。あなたの新車のマスタングが同じ目に遭わないようにするにはどうすればよいでしょうか。最近の車には、駐車場係に認める操作を制限して、トランクを開けたりスピードを出して運転できないようにした特殊な鍵が付いていることがあります。OAuth が生まれたのは、オンラインにおいて、これと本質的に同じ問題を解決するためです。
通常、オンラインのサービスを利用するための認証方式は、ログイン ID とパスワードを組み合わせたクレデンシャル情報で認証するログイン認証になります。しかし、近年ツイッターと Facebook が連携するように、異なるサービスの連携が進んでおり、双方の持つアカウント情報を共有する場合、どのように共有するかが問題となっていました。例えば、ツイッター側にユーザの個人情報があるとき、Facebook がすべての情報にアクセスできてしまう状況は好ましくありません。そのため、異なるサービスへのアクセスを認可する手段が必要とされていました。
そこで新しく考えだされた方法として、すべての情報にアクセスするのではなく部分的な情報にだけアクセスを許可する OAuth という仕様です。OAuth 2.0 は RFC6749 として定義されており、詳細な仕様は以下から確認することができます。
クレデンシャル情報による認可を使用しない理由はいくつかあります。一番重要な問題は、信頼性の問題です。クレデンシャル情報を渡すことは、すべての情報に対するアクセス権限を委譲することになります。例えば、悪意のあるアプリケーションが、ユーザに対してクレデンシャル情報を要求した場合、ユーザの個人情報が漏洩する危険があります。しかし、OAuth を使うと、異なるサービスと連携するためユーザにクレデンシャル情報を譲り渡すという要求をしなくても、ユーザの個人情報へセキュアにアクセスできるようになります。
OAuth は、あなた (リソースオーナー) の代わりに連携先の保護された情報 (保護リソース) にアクセスする方法を連携元のアプリケーション (クライアント) に提供します。クライアントが保護リソースにアクセスする前に、クライアントはまずリソースオーナーの認可を取得し、アクセス許可と権限の範囲と期間を示すトークン (アクセストークン) を交換しなけばなりません。クライアントは、保護リソースがあるサーバにアクセストークンを渡すことにより、保護リソースにアクセスできるようになります。
つまり、あなたが許可した人に、すべての機能を解錠できるマスターキーではなく、限定的な機能にしかアクセスできない特殊な鍵を渡すイメージになります。これは、冒頭で紹介した映画の例と本質的にまったく同じことです。
用語定義
OAuth における用語定義を以下に示します。
用語 | 説明 |
---|---|
保護リソース | リソースサーバ内にあるアクセスが制限されたリソースです。例えば、ツイッターのサーバ内にある情報 (リソース) はクレデンシャル情報によって保護されています。このような保護リソースには、OAuth 認証済みリクエストを利用することでクレデンシャル情報を必要とせずに取得が可能になります。 |
リソースサーバ | 保護リソースに対するリクエストを受付け、レスポンスを返すサーバです。例えば、Facebook から ツイッターのサーバに個人情報をリクエストする場合、ツイッター側がリソースサーバになります。 |
クライアント | リソースサーバにアクセスするサービス、またはアプリケーションです。例えば、Facebook から ツイッターのサーバにリクエストする場合、Facebook 側がクライアントになります。クライアントは、エンドユーザからの認可を取得して、保護リソースに対してリクエストを行います。 |
リソースオーナー | 保護リソースの持ち主で、多くの場合はエンドユーザと同じ意味です。保護リソースへのアクセスを認可するエンティティとなります。 |
エンドユーザ | リソースを所有するユーザで、多くの場合はリソースオーナーと同じ意味です。リソースオーナーはエンティティである点に対して、エンドユーザは何らかの操作 (認可ボタンを押下するなど) が必要になるタイミングで登場します。 |
トークン | クライアントに対して発行されたアクセス認可を表す文字列です。クライアントとリソースサーバ間で、不正なリクエストではないことを示すための一種の鍵情報であり、トークン自体は乱数で表されます。またトークンの付加情報として、リソースオーナーによって許可された権限の範囲やアクセス期間も含まれています。 |
アクセストークン | クライアントがリソースオーナーの代わりに認証済みリクエストを行うために使われるトークンです。アクセストークンを利用することでクレデンシャル情報を使わず、保護リソースへ部分的にアクセスできます。 |
リフレッシュトークン | クライアントがリソースオーナーの新しいアクセストークンを得るために使われるトークンです。アクセストークンを永続的に利用可能にしておくことはセキュリティ上のリスクがあります。そのため、アクセストークンには利用期限が設けられています。利用期限が切れたアクセストークンを再び取得するためにリフレッシュトークンを使うことで、エンドユーザの操作を省略することができます。 |
認証 (Authentication) | 認証とは、ユーザが「自分自身を何者であると主張しているか」を検証するプロセスです。現実世界では、警官が身分証の提示を求めるとき、警官は身分証の写真とあなたの外観を見比べて本人確認を行います。インターネット上における、認証とはあなたがアカウント所有者であることを確認する作業になります。通常の認証プロセスでは、ユーザ名とパスワードの入力が求められます。 |
認可 (Authorization) | 認可とは、何らかの行為 (ドキュメントを読んだり、電子メールアカウントにアクセスするなど) を行う際に、当該ユーザにその権限があるかどうかを検証するプロセスです。通常、現在のユーザに対する権限の有無を確認するには、最初にユーザのアイデンティティを検証 (認証) しなければいけません。現実世界では、警官がスピード違反の車を止めたとき、まず最初に免許証を使って認証を行います。次に、あなたが運転を認可されているかどうか (期限切れでないか、限定事項がないかなど) を確認します。インターネット上における認可とは、認証後に各操作に対するアクセスが、許可されている範囲のデータとサービスに対するものであることを確認します。 |
認可コード | OAuth 2.0 の認証フローに登場する生存期間の短いトークンで、認可コードはアクセストークンとリフレッシュトークンを得るために使われます。認可コードは、エンドユーザがリソースサーバへのアクセスを認可することによって発行されます。 |
認可サーバ | エンドユーザの認可と、クライアントの認証が成功した後、トークンを発行するためのサーバです。サービスプロバイダ側に含まれるサーバですが、厳密にはリソースサーバとは区別されますが、後述するフロー図では、リソースサーバに認可サーバも含めています。 |
認可エンドポイント | 認可サーバの HTTP エンドポイントで、エンドユーザの認証と認可の取得を行います。エンドポイントとは、それらの処理を行う終端のことを指します。 |
トークンエンドポイント | 認可サーバの HTTP エンドポイントで、トークンの発行とリフレッシュを行います。エンドポイントとは、それらの処理を行う終端のことを指します。 |
クライアント識別子 | 認可サーバがクライアント自身を識別するために発行される一意な識別子です。クライアント識別子は client_id で表され、中身の情報は乱数で表現されます。 |
アサーション | 異なるトラストフレームワークを利用して取得したアクセス許可。アサーションは、クライアントがアクセストークンを取得するために既存のトラストリレーションシップを利用することを可能にします。それらは OAuth と他のトラストフレームワークのブリッジを提供します。 |
パスワードクレデンシャル | 直接リソースオーナーとクレデンシャルをやり取りをするときに使用されます。ただし、リソースオーナーパスワードクレデンシャルは、リソースオーナーとクライアントの間に高度な信用があるときのみ利用するべきです。 |
OAuth 1.0 と OAuth 2.0 の仕組み
OAuth 1.0 での認証は、以下の流れで行われます。


OAuth 1.0 では、クライアントとサービスプロバイダの間で何度もリクエストのやり取りが行われています。そのため、上記の認証フローでは、以下の 3 つの問題点があります。
- 認証と署名
- デスクトップ/モバイルアプリ対応
- スケール時のパフォーマンス
認証と署名の問題点は、認証フローが複雑な点です。OAuth クライアントを作成するためには OAuth ライブラリが必要でしたが、複雑なためにバグが存在することもありました。また、ライブラリが存在しない言語で OAuth を使用した開発は非常に困難であるため、使用する言語が限られていました。
デスクトップ/モバイルアプリ対応の問題点は、OAuth 1.0 は Web アプリのみを対象としていていたため、デスクトップ/モバイルのアプリには対応していない点です。そのため、Twitter はデスクトップ/モバイルのアプリのために "xAuth" という独自の OAuth 拡張を取り入れました。
スケール時のパフォーマンスの問題点は、保護されたリソースにアクセスする際にクライアントクレデンシャルと、トークンクレデンシャルが必要になり、この 2 つの分離が難しいため、サーバをスケールするのが困難になる点です。一般的には、認証サーバにクレデンシャルの発行を任せて、API の応答は別サーバで行われますが、クレデンシャルの分離が困難である点がサーバをスケールするときの問題点となります。
これらの問題点を解決するために OAuth 2.0 での認証は、以下の流れで行われるようになりました。


上記のフローでは、以下のステップを踏みます。
クライアントは、OAuth 認証を行うためにエンドユーザ (リソースオーナー) に対して認可を要求します。認可を行う場合、専用の画面で行う必要があるため、クライアントはアクセス許可を発行する認可サーバにリソースオーナーをリダイレクトさせます。
クライアントは、認可サーバに対して認可を行い、アクセス許可を提示することでアクセストークンを要求します。
認可サーバはアクセス許可の正当性を確認し、アクセストークンを発行します。また、オプションとしてリフレッシュトークンを発行する場合もあります。リフレッシュトークンを利用することで、アクセストークンの有効期限が切れた場合でも、リソースオーナーに再度アクセス許可を要求することなく新しいアクセストークンが取得できます。
クライアントは保護リソースへのリクエストを実行し、アクセスを得るためにアクセストークンを提供します。リソースサーバはアクセストークンの正当性を確認し、有効な場合はリクエストを処理します。
クライアント認証 - リクエスト
本章は、上記フローの「A」に相当します。
ユーザがクライアントから他のサービスプロバイダに対して認証を求める場合、ブラウザには認可を求める画面が表示されます。例えば、Facebook から ツイッターに連携する場合、ツイッターからは以下のような認可を求める画面が表示されます。

GET /authorize?client_id=s6BhdRkqt3&scope=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fauth%2F&state=i1WsRn1uB1&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2F&response_type=code HTTP/1.1 Host: server.example.com
※ 分解すると以下のようになります
client_id = s6BhdRkqt3
scope = https%3A%2F%2Fclient%2Eexample%2Ecom%2Fauth%2F
state = i1WsRn1uB1
redirect_uri = https%3A%2F%2Fclient%2Eexample%2Ecom%2F
response_type = code
属性 | 必須/任意 | 説明 |
---|---|---|
client_id | 必須 | クライアント ID。アプリケーションを登録した際に割り当てられた値を設定します。 |
scope | 任意 | リクエストで要求するアクセス権の種類をスペース区切りの文字列で指定します。scope として有効な値は認可サーバによって異なるため、API プロバイダのドキュメントを参照してください。 |
state | 任意 (推奨) | アプリケーションに対する CSRF 攻撃を防ぐためにクライアントアプリケーションで使用する一意の値です。必須ではありませんが、設定することが推奨されます。この値は、リクエストごとに異なる乱数で一意な文字列である必要があります。 |
redirect_uri | 任意 | アプリケーションへのアクセス認可が終わった後にリダイレクトされる URI を指定します。通常、この値はあらかじめプロバイダに登録しておく必要があります。 |
response_type | 必須 | レスポンスの種類を表します。上記のフローはサーバサイドのフローであるため code が指定されます。code が指定された場合、認可リクエストを承認すると認可コードが返却されます。 |
クライアント認証 - レスポンス
本章は、上記フローの「B」に相当します。
HTTP/1.1 302 Found Location: https://client.example.com/?code=SplxlOBeZQQYbYS6WxSbIA&state=i1WsRn1uB1
※ 分解すると以下のようになります
code = SplxlOBeZQQYbYS6WxSbIA
state = i1WsRn1uB1
属性 | 必須/任意 | 説明 |
---|---|---|
code | 必須 | 認可コードが発行されます。認可コードは漏洩のリスクを軽減するために、失効するまでの時間は最大でも 10 分になります。また、認可コードは使い捨てのコードであり、認証サーバは 2 回以上使用されているリクエストは拒否し、その認証コードに基づいて発行されたトークンは無効にする必要があります。 |
state | 条件付き必須 | リクエストに state が含まれている場合は必須になります。 |
クライアント認証 - エラーレスポンス
本章は、上記フローの「B」がエラーであった場合に相当します。
HTTP/1.1 302 Found Location: https://client.example.com/cb?error=access_denied&state=i1WsRn1uB1
※ 分解すると以下のようになります
error = access_denied
state = i1WsRn1uB1
属性 | 必須/任意 | 説明 |
---|---|---|
error | 必須 | error は、以下のいずれかの値を返します。
|
error_description | 任意 | エラーを理解するための追加情報が提供されます。 |
error_uri | 任意 | エラーに関する URI 情報が提供されます。 |
state | 条件付き必須 | リクエストに state が含まれている場合は必須になります。 |
アクセストークン取得 - リクエスト
本章は、上記フローの「C」に相当します。
アクセストークンの取得リクエストでは、認可コードとクライアントの個別の認証情報をパラメータとして、アクセストークンを要求します。アクセストークンの取得は以下のようなリクエストで行います。
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=i1WsRn1uB1&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
※ 分解すると以下のようになります
grant_type = authorization_code
code = i1WsRn1uB1
redirect_uri = https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
属性 | 必須/任意 | 説明 |
---|---|---|
grant_type | 必須 | 値は authorization_code である必要があります。 |
code | 必須 | 認可サーバから受け取った認可コードを設定します。 |
redirect_uri | 条件付き必須 | クライアント認証リクエストに redirect_uri が含まれている場合は必須です。その場合、クライアント認証リクエストに設定した redirect_uri と同じ値である必要があります。 |
client_id | 条件付き必須 | 認可サーバによってクライアントが認証されていない場合は必須です。 |
アクセストークン取得 - レスポンス
本章は、上記フローの「D」に相当します。
アクセストークン取得リクエストが正当かつ、エンドユーザによって認可された場合、以下のようにアクセストークンの取得に成功します。リクエストが失敗、または不正であった場合は、エラーレスポンスを返します。
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
"example_parameter":"example_value"
}
アクセストークン取得 - エラーレスポンス
本章は、上記フローの「D」がエラーであった場合に相当します。
アクセストークン取得リクエストが失敗、または不正であった場合は、エラーレスポンスを返します。
HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-store
{
"error":"invalid_request"
}
属性 | 必須/任意 | 説明 |
---|---|---|
error | 必須 | error は、以下のいずれかの値を返します。
|
error_description | 任意 | エラーを理解するための追加情報が提供されます。 |
error_uri | 任意 | エラーに関する URI 情報が提供されます。 |
まとめ
OAuth は、従来のクレデンシャルによってすべての情報を参照・変更するという考え方から、認可された限定的な範囲で情報を扱える方法です。仕組みと認証方法を見ると難しく感じるかもしれませんが、一般的なサービスプロバイダではボタンをクリックするだけで簡単に終了します。しかし、OAuth を利用して API を作る場合は、認可コードやアクセストークン、リフレッシュトークンは重要な概念となるため是非覚えておきましょう。