OAuth & OpenID Connect の不適切実装まとめ

# はじめに

世の中の OAuth & OpenID Connect の不適切実装の事例をリストしています。公式ドキュメントに「ドラフト段階の仕様をサポート」と断り書きが書いてあっても、最終仕様に違反している場合はリストしています。内容は適宜更新していきます。OAuth & OpenID Connect を実装する際の注意事項として参照していただければと思います。

<sup>仕様書を読むのは面倒だけど、OAuth & OpenID Connect をちゃんと実装しないといけない立場にある方は、是非 **[Authlete](https://www.authlete.com/)** の使用を検討してください。(by Authlete 創業者)</sup>

# 事例

## 1. リダイレクト URI を正しく検証していない

[John Bradley](https://plus.google.com/113758384073095653585) 氏の記事「[Covert Redirect and its real impact on OAuth and OpenID Connect](http://www.thread-safe.com/2014/05/covert-redirect-and-its-real-impact-on.html)」を参照してください。

- Facebook (実装ぼろぼろ)

また、「[OAuth 2.0 + OpenID Connect のフルスクラッチ実装者が知見を語る](http://qiita.com/TakahikoKawasaki/items/f2a0d25a4f05790b3baa)」の「[リダイレクトURI](http://qiita.com/TakahikoKawasaki/items/f2a0d25a4f05790b3baa#%E3%83%AA%E3%83%80%E3%82%A4%E3%83%AC%E3%82%AF%E3%83%88-uri)」もご参照ください。


## 2. スコープ名の区切文字の間違い

[認可エンドポイント](https://tools.ietf.org/html/rfc6749#section-3.1)や[トークンエンドポイント](https://tools.ietf.org/html/rfc6749#section-3.2)に対するリクエストの `scope` パラメーターは、スコープ名を複数列挙することができます。その際、区切り文字としてスペースを使うことになっています ([RFC 6749, 3.3. Access Token Scope](https://tools.ietf.org/html/rfc6749#section-3.3))。しかし、下記の OAuth 実装ではカンマを区切文字としています。

- Facebook
- GitHub
- Sportify
- Discus
- Todoist


## 3. アクセストークン有効期間秒数パラメーター名の間違い

認可サーバーは、アクセストークンを発行する際、その有効時間に関する情報を秒単位で返してもかまいません。その際、パラメーター名は `expires_in` にすることになっています ([RFC 6749, 5.1. Successful Response](https://tools.ietf.org/html/rfc6749#section-5.1))。

- Facebook (`expires` というパラメーター名になっている)


## 4. トークンエンドポイントからの応答フォーマットの間違い

[トークンエンドポイント](https://tools.ietf.org/html/rfc6749#section-3.2)からの応答は JSON です ([RFC 6749, 5.1. Successful Response](https://tools.ietf.org/html/rfc6749#section-5.1))。

- Facebook (`application/x-www-form-urlencoded` になっている)
- Bitly (`application/x-www-form-urlencoded` になっている)
- GitHub (デフォルトが `application/x-www-form-urlencoded` になっている)


## 5. トークンエンドポイントの応答に token_type が含まれていない

[トークンエンドポイント](https://tools.ietf.org/html/rfc6749#section-3.2)からの成功応答には `token_type` というパラメーターを含める必要があります ([RFC 6749, 5.1. Successful Response](https://tools.ietf.org/html/rfc6749#section-5.1))。

- <strike>Salesforce (`token_type` が含まれない。参考: [OAuth Access Token Response Missing token_type](https://developer.salesforce.com/forums/?id=906F00000009BCRIA2))</strike> <sup>※1</sup>
- Slack (`token_type` が含まれない)

[2016/05/15] <sup>※1</sup> 本不具合は修正済みとの情報をいただきました。ありがとうございました。


## 6. トークンエンドポイントで grant_type が必須でない

[トークンエンドポイント](https://tools.ietf.org/html/rfc6749#section-3.2)に対するリクエストでは、`grant_type` パラメーターは必須です。しかし、下記の実装では `grant_type` パラメーターを要求していません。

- GitHub
- Slack
- Todoist


## 7. エラー応答の error の値が不適切

- GitHub (`application_suspended` など、好き勝手にやっている)
- Todoist (トークンエンドポイントに提示された認可コードが誤っていた場合、`error` が `bad_authorization_code` となる。`invalid_grant` であるべき。)


## 8. トークンエンドポイントへの client_secret パラメーターが必須

[トークンエンドポイント](https://tools.ietf.org/html/rfc6749#section-3.2)にクライアントシークレットを渡さなければならないのは、クライアントアプリケーションのクライアントタイプが confidential であるか、もしくは、クライアントタイプが confidential でなくともクライアントシークレットを発行されている場合のみです。仕様上は、認可サーバーは public クライアントに対してもクライアント認証を求めてもよいことになっていますが、「クライアントシークレットを保っておくことができないクライアントを public クライアントと呼ぶ」との定義を踏まえると、トークエンドポイントへのリクエストで必ずクライアントシークレットを求めることは、間違いとは言えないものの、不適切です。 ([RFC 6749, 2.3. Client Authentication](https://tools.ietf.org/html/rfc6749#section-2.3))

- Todoist (`client_secret` パラメーターが必須となっている)


## 9. アクセストークンを取り消せない

実装間違いとは言えませんが、アクセストークンの意図を考えると、アクセストークンを取り消せないというのは不適切な実装です。

- 某世界的な SIer のパッケージ


## 10. アクセストークン大量累積

アクセストークンの消し込み処理を考えずに実装すると、使われていないが消すに消せないアクセストークンが大量に累積することになります。

- 某国内大手サービスの実装


## 11. PKCE 非サポート

これを不適切実装というのは酷ではありますが、もしもスマートフォンアプリ等の public クライアントに対して[認可コードフロー](https://tools.ietf.org/html/rfc6749#section-4.1)でアクセストークンを発行するのであれば、認可コード横取り攻撃に対する対抗策である [RFC 7636](https://tools.ietf.org/html/rfc7636) (Proof Key for Code Exchange by OAuth Public Clients; PKCE) のサポートは、必須と言ってよいでしょう。実際、(ドラフト段階ではありますが) 「[OAuth 2.0 for Native Apps](OAuth 2.0 for Native Apps)」という文書には次のように書いてあり、

> Both the client and the Authorization Server MUST support PKCE [[RFC7636](https://tools.ietf.org/html/rfc7636)] to use custom URI schemes, or localhost redirects.

認可コードを受け取る際に、カスタムスキームや localhost へのリダイレクトを用いるのであれば、PKCE のサポートは必須とされています。

英文ですが、PKCE の図解は[こちら](https://www.authlete.com/documents/article/pkce)にあります。


## 12. token_type 不整合

アクセストークン発行時に token type の値として Bearer を返しているにも関わらず、リソースエンドポイント群が [RFC  6750](https://tools.ietf.org/html/rfc6750) (The OAuth 2.0 Authorization Framework: Bearer Token Usage) で定義された方法でアクセストークンを受け取らないという実装があります。

- GitHub (`Authorization: token OAUTH-TOKEN` というフォーマットでアクセストークンを受け取る)


## 13. エラーコードのパラメーター名が error ではない

- LINE (認可リクエストでエラーが発生すると、`error` ではなく `errorCode` というパラメーターでエラーコードを通知する)


### 続く (予定)


# 参考

- [OAuth 2.0 + OpenID Connect のフルスクラッチ実装者が知見を語る](http://qiita.com/TakahikoKawasaki/items/f2a0d25a4f05790b3baa)
- [【第二弾】OAuth 2.0 + OpenID Connect のフルスクラッチ実装者が知見を語る](http://qiita.com/TakahikoKawasaki/items/30fbd546935cea914e4f)