Google OAuth 2.0 を使い、Web アプリケーションに認証機能を追加する
=== 追記 2017年6月9日 ===
この記事は Google OAuth を利用し、
自分のウェブアプリケーションに「OAuth 認証」させようという記事です。
OAuth 2.0 は本来、認証(Authentication)ではなく認可(Authorization)を行うための枠組みです。
そのあたりの知識が曖昧な時期に「OAuth 認証」のための記事を書いたため、
いま見ると古びていて、認識が間違えているところもあるかと思います。
OAuth 2.0 のきちんとした知識については、@TakahikoKawasaki 先生の記事を参考にしてください。
- OAuth 2.0 全フローの図解と動画
- OAuth 2.0 + OpenID Connect のフルスクラッチ実装者が知見を語る
- 【第二弾】OAuth 2.0 + OpenID Connect のフルスクラッチ実装者が知見を語る
=== 追記終了 ===
ユーザー登録機能のある Webサービスを作るとき、
最初に考えるものとしてログイン機能、認証機能等がある。
これは色々と考慮するべきところが多く、
いい加減に作るとセキュリティの問題を抱えてしまう。
またそうしたユーザー登録機能は、ユーザーに対し、悪いユーザー体験を与えてしまう。
Web サービスを使ってみようと思ったとしても、最初にメールアドレスやパスワードの
登録を行わなければならず、面倒である。
別のアプローチとして、独自のユーザー登録フローを実行するのではなく、
Google、Twetter、Facebook などを介し、Webサービスにログインできるようにする仕組みもある。
(OpenID などと呼ばれる)
例えば既存の Web サービスで「Trello」というものがある。
これをはじめるとき、次のような流れで Webサービスを使いはじめることができる。
この流れの中で、ユーザーが行う操作は「Google アカウントで認証する」をクリックし、
Google 側で同意ボタンを押すだけだ。
Webサービス独自のユーザー認証機能を実装するのではなく、
信頼性のある外部サービスに認証を委託して、
自分のサービス側では、ユーザー情報の管理のみを行う仕組みを、
「OAuth を使ったログイン認証」と呼ぶ。
Google アカウントを持っているならば、
その Webサービス の新規登録行わなくてもすぐに利用開始できるような機能を、
実装する方法について説明しようと思う。
今回は、次のような要件で実装を行った。
- Google の OAuth 2.0 を使う
- Ruby on Rails アプリケーションから Google に接続する
Ruby on Rails を実装例としたが、言語・フレームワークに依存している部分は少ないため、
他言語、他フレームワークにも簡単に展開可能だと思われる。
今回の実装にあたって、大部分を、下記の記事を参考に行った。
Using OAuth 2.0 for Login (OpenID Connect) - Google Accounts Authentication and Authorization
Google OAuth の認証方法をは使った記事で、日本語のチュートリアルはあまり数がなく、
あったとしても古い内容になっていたため、
こちらの記事にすることにした。
皆様のお役に立てれば幸いである。
今回掲載する内容が、古くなっているように感じたら、
この Google 本家の情報を参照していただきたい。
実装の流れ
Google OAuth 2.0 認証を使って、自分のWebサービスに認証機能を付加するには、
下記のような流れで作業を行う。
- Google アカウントを作成(作成済みならば不要)
- Google Developers Console から、プロジェクトを作成、認証情報や、OAuth 用クライアントID等を取得
- Web アプリケーションに、Google と OAuth 認証する流れを実装
Google アカウントはすでに所有していると仮定し、
ここからは Google Developers Console の設定を方法と、
Web アプリケーション側の実装を解説する。
Google Developers Console の設定について
Google Developers Console を開く。
Google Developers Console
ここで「プロジェクトを作成」を選択。
新しいプロジェクトを作成する。
作成したプロジェクトの中に入ると、画面左側にメニューがいくつか表示される。
今回の OAuth 認証で必要になるのは、主に下記の3項目である。
- API
- 認証情報
- 同意画面
Web アプリケーション側の実装について
Using OAuth 2.0 for Login (OpenID Connect) - Google Accounts Authentication and Authorization
上記の記事を参考にする。
Web アプリケーション側の実装としては、
下記のような流れで Google との連携、ユーザー登録、認証まで進める。
(ここからWebアプリケーション側の処理を「クライアント」と表記し、
Google 側の処理を「Google」と単純に表記する)
- 【クライアント】ユーザー登録、認証ボタンをクリックした時、Google に対して OAuth 認証をリクエスト
- 【Google】認証同意画面を表示し、その結果がクライアントへリダイレクトされる
- 【クライアント】認証が OK ならば、Google に認証コードとクライアント情報を送信、トークンを取得
- 【クライアント】取得したトークンを使い、Google の API へアクセス(メールアドレスを取得)
下記に Ruby on Rails で実装した認証処理の例を掲載する。
この辺りの Google との連携を簡単にするために 「Google API Client」の Ruby 版 が提供されている。多分、これを使いこなせれば、かなり楽だったんじゃないかなぁと思ったが、私にはちょっと使い方が難しすぎた...。結局、認証部分は自作してしまった。ご了承いただければと思う。
Google に対して OAuth 2.0 認証を要求する
たとえば Webアプリケーション側に下記のようなボタンを表示し、
そこをクリックすることで利用開始できるようにしたいとする。
下記のボタンをクリックした時に POST リクエストを送信する。
ここの実装については、詳細は下記の項を参照いただきたい。
2. Send an authentication request to Google
POST リクエストの実装は、具体的には下記のような処理となった。
# POST /signin def signin # 認証用トークン (安全な乱数) state_token = SecureRandom.urlsafe_base64(32) # URLにのせて送信するため # トークンをセッションに保管しておく # 自動ログインのフォーム入力内容も、保管しておく session[:oauth_google] = {:state => state_token} # URL の組み立て url = "https://accounts.google.com/o/oauth2/auth?"\ "client_id=#{Constants::GOOGLE_CLIENT_ID}&"\ "response_type=code&"\ "scope=email%20profile&"\ "redirect_uri=http://localhost:3000/oauth-google&"\ "state=#{state_token}" redirect_to url end
上記のアクションが終わると、Google の認証同意画面へと遷移する。
ここで同意すると「redirect_uri」で指定した URI にリダイレクトされる。
今回の例では「http://localhost:3000/oauth-google」へリダイレクトされる。
この URI に対するアクションの実装はこうだ。
# GET /oauth-google # Google OAuth による認証後のコールバック呼び出し def oauth_google if session[:oauth_google] && session[:oauth_google][:state].present? # CSRF 対策 if session[:oauth_google][:state] == params[:state] if params[:code].present? # code を元に token 認証する response = Net::HTTP::post_form(URI.parse("https://accounts.google.com/o/oauth2/token"), { :code => params[:code], :client_id => Constants::GOOGLE_CLIENT_ID, :client_secret => Constants::GOOGLE_CLIENT_SECRET, :redirect_uri => "http://localhost:3000/oauth-google", :grant_type => "authorization_code" }) # token レスポンス result = JSON.parse(response.body) if result["expires_in"] > 0 && result["id_token"].present? api_response = Net::HTTP.get URI.parse("https://www.googleapis.com/oauth2/v1/tokeninfo?id_token=#{result['id_token']}") api_result = JSON.parse(api_response) if api_result["issuer"] == "accounts.google.com" && api_result["issued_to"] == Constants::GOOGLE_CLIENT_ID && api_result["audience"] == Constants::GOOGLE_CLIENT_ID && api_result["audience"] == Constants::GOOGLE_CLIENT_ID && api_result["verified_email"] email = api_result["email"] puts "Signin: success Google OAuth '#{email}'" ActiveRecord::Base.transaction do # 既存のユーザーかどうか調べる user = User.where("email = ?", email).first if user.nil? # 新規ユーザー登録(users テーブルというものがあると仮定) user = User.create({:email => email}) puts "Signin: new user '#{email}'" end # セッションに認証情報を付与 session[:user_id] = user.id end# ActiveRecord::Base.transaction redirect_to root_path return end end end end end flash.now.alert = "何らかの理由で認証に失敗しました" render :template => "index" end
この辺りについて詳しくは、下記の項を参考にしている。
4. Exchange code for access token and ID token
流れとしては、リダイレクト時に取得した認証コードを使って、
Google に対して、トークン取得のリクエストを送信する。
無事にトークンが取れたら、その認証トークンを使って、任意のAPIを呼び出す。
という流れである。
OAuth による認証に最後まで成功した場合は、この session に認証情報を付加する。
まだユーザーが作られていない状態ならば、
新規ユーザー登録まで、同時に行う。
最後に
以上が Google の OAuth 2.0 認証を使って、
Webアプリケーションに認証機能を追加する流れと、実装例である。
実装としては Ruby on Rails を使っているが、
Google Client API に依存していないし、基本的には HTTPリクエストのみで完結している。
実装例の内容は OAuth 認証部分に特化するため、
実際のアプリケーションよりも処理を削って掲載しているので、
その辺りはご了承いただきたい。
また、根本的な誤りなどありましたら、
訂正いたしますので、コメント欄などでご指南頂けますと大変嬉しいです。
Enjoy Hacking