今年は OAuth2 の仕組みを使ってユーザーに認可を求める Web アプリをたくさん書きました。この振る舞いを実現するために、難しいことは必要ないのですが、いくつかの仕組みをまたいだ作業が必要です。ということで手順のまとめ記事です。
ところで、 ruby で google-apis-ruby-client を使うのが半端なく面倒くさいという記事があるようですが、たぶんどの言語、どのライブラリを使ってもこの手順になるんじゃないかと思っています。
ここでは、 hampton という名前のプロジェクトを生成していきます。以下のコマンドは、 Rails のプロジェクトを生成し、コードを一式そろえてくれます。Rails プロジェクトのフォルダ構造や、どこになにが書いてあるとかは、 https://guides.rubyonrails.org/getting_started.html を読むと良いと思いますが、いまは気にせずに作業していきましょう。
プロジェクトを生成してエディタで開く
以下のコマンドを実行します。
rails new hampton
画面がこういう感じになると思います。
生成したプロジェクトの中に cd
で移動して、テキストエディタ (Visual Code Studio) を起動します。
cd hampton
code .
以下のコマンドを実行して、開発用サーバーを起動します。
rails server
このような画面になったら、サーバーが起動しています。ブラウザで http://localhost:3000 を開きましょう。
この画面が表示されていれば、正しく動作しています。ここまでで準備ができたので、これからコードを書いていきます。
まずユーザーを登録できるようにする
rails generate scaffold user email:string name:string
rake db:migrate
さきほどと同じくブラウザで開発用のサーバーを開くのですが、先ほどの URL と違い、 http://localhost:3000/users を開きます。すると下の画面が表示されます。
ユーザーを登録しましょう。
google-api-client という gem を追加する
gem 'google-api-client'
bundle install
飛ばす部分をつくる
rails generate controller oauth2_sessions
これが
resources :users
こうなる
resources :users do
get "oauth2_sessions" => "oauth2_sessions#new"
get "oauth2_callback" => "oauth2_sessions#callback", on: :collection
end
これが
class Oauth2SessionsController < ApplicationController
end
class Oauth2SessionsController < ApplicationController
def new
end
end
さっきの画面
この画面の URL の末尾に /oauth2_sessions
を付け足します。
この画面が出ると思います。これはエラー画面ですが、正解です。
Token の保存場所をつくっておく
(この部分は以前にActiveRecord を googleauth の TokenStore として使う方法という記事に書いたものとほぼ同じ内容です)
rails generate model google_auth_token token_key:string client_id:string
class GoogleAuthToken < ApplicationRecord
validates :token_key, presence: true, uniqueness: true
validates :client_id, presence: true
end
class ActiveRecordTokenStore < Google::Auth::TokenStore
def load id
record = GoogleAuthTokenStore.find_by! token_key: id
record.to_json
end
def store id, token
json = JSON.parse(token)
client_id = json["client_id"]
access_token = json["access_token"]
refresh_token = json["refresh_token"]
expiration_time_millis = json["expiration_time_millis"]
record = GoogleAuthToken.find_or_initialize_by token_key: id, client_id: client_id, access_token: access_token, refresh_token: refresh_token, expiration_time_millis: expiration_time_millis
record.save
end
def delete id
record = GoogleAuthToken.find_by! token_key: id
record.destroy
end
end
class Oauth2SessionsController < ApplicationController
def new
@user = User.find(params[:user_id])
key = ENV.fetch 'GOOGLE_CREDENTIALS_KEY'
secret = ENV.fetch 'GOOGLE_CREDENTIALS_SECRET'
callback = ENV.fetch 'GOOGLE_CREDENTIALS_CALLBACK'
token_store = ActiveRecordTokenStore.new
client_id = Google::Auth::ClientId.new key, secret
user_authorizer = Google::Auth::UserAuthorizer.new client_id, token_store, callback
redirect_to user_authorizer.get_authorization_url
end
end
GCP で鍵を生成して、環境変数から読み込ませる
- ID
- Secret
- コールバック URL に http://localhost:3000/users/oauth2_callback を追加しておく
GOOGLE_CREDENTIALS_KEY=xxxxxxxxxxxxxxxxxxxxx GOOGLE_CREDENTIALS_SECRET=xxxxxxxxxxxxxxxxxxxxxxx GOOGLE_CREDENTIALS_CALLBACK=http://localhost:3000/users/oauth2_callback rails server
コールバック側を実装する
def callback
key = ENV.fetch 'GOOGLE_CREDENTIALS_KEY'
secret = ENV.fetch 'GOOGLE_CREDENTIALS_SECRET'
callback = ENV.fetch 'GOOGLE_CREDENTIALS_CALLBACK'
token_store = ActiveRecordTokenStore.new
client_id = Google::Auth::ClientId.new key, secret
user_authorizer = Google::Auth::UserAuthorizer.new client_id, token_store, callback
user_authorizer.get_and_store_credentials_from_code code: params[:code]
redirect_to root_path
end