OAuth 2.0による認証を試してみた
OAuth 2.0で認証してみた。
まずOAuth 2.0は認可フレームワークで認証は直接サポートされない。 だけどユーザー属性参照への認可(アクセストークン)で似たことができる。
実装が簡単なためOAuth 2.0で定義されているAuthorization Code Grantを利用して認証をする人が増えた。 これは野良ハックであって認証として合意は取られていない。 なので認証としての機能拡充や仕様として整理するためにOAuth 2.0上の認証機構OpenID Connect 1.0が定められた(と思われる)。
OpenID Connect 1.0のクライアントは利用するだけなら簡単との事だけど仕様を理解するのにだいぶ時間が掛かりそうだったので OAuth 2.0のハック版の認証を試してみた。
Authorization Code GrantにはセキュリティリスクがありRFC7636で対策が策定されている。 だけどgithubのドキュメントで対応を見つけられず今回は実装していない。
資料
まず概要をつかむための読み物として下を読んだ。
- OAuth 2.0 の仕様を読むために
- セキュリティトークンとOAuth 2.0
- implicit grant flowは危険
- OAuth 2.0 + OpenID Connect のフルスクラッチ実装者が知見を語る
- OAuth & OpenID Connect 関連仕様まとめ
- OAuth & OpenID Connect の不適切実装まとめ
- 非技術者のためのOAuth認証(?)とOpenIDの違い入門
仕様
OAuth 2.0では第三者(Client)にリソースへのアクセスを認可するフローが定義されている。 ここではOAuth 2.0の要素とエンドポイントとフローを整理する。
仕様書
まず仕様書だがRFCで検索すると出て来る。 仕様は複数のドキュメントで構成されている。
今回ためしたAuthorization Code Grantにはセキュリティリスクがあり RFC7636で対策が策定されている。
- 仕様本体(トークン取得)
- トークンの使い方
- トークン操作(オプショナル)
- セキュリティ
認可フロー
5つの認可フローが定義されている。 これらの認可フローを実現するために認可サーバーは以下の2つのエンドポイントを提供する必要がある。
- Authorization Endpoint
- リソースオーナーが認証するエンドポイント
- Token Endpoint
- アクセストークンを取得するエンドポイント
Implicit GrantのみAuthorizationエンドポイントがアクセストークンを提供する。 それ以外のフローではtokenエンドポイントがアクセストークンを提供する。
5つのフローは以下である。RFC6749での各節へのリンクと特徴的なパラメタを整理しておく。
- Authorization Code Grant(4.1)
- Implicit Grant(4.2)
- Resource Owner Password Credentials Grant(4.3)
- Client Credentials Grant(4.4)
- Extension Grants(4.5)
普段はAuthorization Code Grant 以外はあまり気にしなくていい。
認証済みリクエスト
認可を得られたら実際にリソースへアクセスする。 トークンの使い方はBearer Token Usage(RFC6750)で定義されている。 認可トークンを用いて実際にリソースにアクセスするにはリソースサーバーには認証済みリクエストを送ることが必要で、認証済みリクエストは以下の3種類が定義されていている。
- Authorization Request Header Field
- Form-Encoded Body Parameter
- URI Query Parameter
Authorization Request Header Field
RFCのリクエストサンプルをそのまま下に書くと以下の様になる。
|
|
AuthorizationヘッダのスキーマをBearerにしてトークンを続けて送る。 クライアントはSHOULDでこの形式を利用する。リソースサーバーはMUSTでこの形式をサポートする。
Form-Encoded Body Parameter
RFCのリクエストサンプルをそのまま下に書くと以下の様になる。
|
|
Content-Type にapplication/x-www-form-urlencoded と厳格に一致させる。また値はaccess_token に格納しW3C.REC-html401-19991224に従う。 request-bodyが使えるメソッドでリクエストする(特にGETはMUST NOT)。 など色々従う。参加しているAuthorizationヘッダへアクセス出来ない場合を除いてクライアントがこの形式を使うのはSHOULD NOTとされ推奨されない。 サーバー側がサポートするのは良い(MAY)。
URI Query Parameter
URIクエリパラメタを用いたアクセス形式もある。 サンプルリクエストは下でaccess_token パラメタを経由して渡される。
|
|
このリクエストはセキュリティ的な欠点があるためできるだけ利用するべきではなく(SHOULD NOT)。 利用する場合はセキュリティの目的でCache-Controlについて強く推奨される設定がある。
クライアントはno-storeオプションを有効にして送信するべき(SHOULD)。 サーバはprivateオプションを有効にして送信するべき(SHOULD)。
リダイレクト
Authorization Endpointへリソースオーナーを導いたり、認証後に特定コンテンツに遷移したりはリダイレクトなどで実現する。 RFC5749では302を使う例で書かれているが実装の詳細に関わるのでリダイレクトできれば何でも良いって書かれていた。 聞いてみたけどUAの対応を考えて実装者が頑張れJavaScriptでもmetaタグでも何でも良いって感じらしい。 またネイティブアプリでリダイレクトだとブラウザに白い画面が残るらしい。
実装
oauth2ライブラリを利用してクライアントを認証機構を実装してみた。 設計不備がある。これくらいの観点は雑に書いても無意識のうちに満たしていたかったけど職人ではありませんでした。
- 1つのリソースオーナーしか利用できない
- サイト内のログインページがない
- ログイン・ユーザーなどの状態管理をメモリでやっていて差し替えられない
- ログイン・ユーザーなどのセッション情報用Cookieのパスやドメインをカスタムできない
とりあえず下の様に使う。
|
|
自分でワークフローを書いてみて勉強にはなったけど不完全に終わった。 どこかに柔軟で標準ライブラリと相性が良いミドルウェアみたいな実装が転がってないだろうか。 gothicをforkしたなんて方もいました。 まだまだGithub検索(oauth2), GolangLibs検索(oauth2)あたりで探すフェーズ。