github 認証や google 認証など、アプリケーションへ既存の所有アカウントを利用してのログインを提供する機能に OAuth 認証があります。
今回は github 認証の実装を確認し、独自のプロバイダとそこへのクライアントを作成してみます。
目次
参考
github 認証するクライアントを作る devise を導入したユーザー認証する環境を用意 毎度、User モデルを認証する環境を作っているので、割愛。Ruby on Rails での認証(Devise ライブラリ) を参照してもらえるといい。
Client id と Client secret を入手 github にログインし、設定を開き「Developer settings」に進みます。
「Register a new application」からアプリケーションを登録します。
必要情報を登録します。
作成できた Client id と Client secret を控えておきます。
omniauth を導入する それでは、github 認証を行うにあたって必要な gem omniauth を導入してみる。 以下を Gemfile に追記する。
Gemfile 1 2 gem 'omniauth' gem 'omniauth-github', github: 'omniauth/omniauth-github', branch: 'master'
config/initializers/devise.rbを編集してプロバイダに github を登録します。
config/initializers/devise.rb 1 2 3 4 5 6 7 8 Devise .setup do |config | config.omniauth :github , 'Client id' , 'Client secret' , scope: 'user,public_repo' end
User モデルに、omniauth 用のカラムを追加します。 以下の内容でマイグレーションをします。
マイグレーションファイル 1 2 3 4 5 6 class AddColumnsToUsers < ActiveRecord::Migration [5.2 ] def change add_column :users , :provider , :string add_column :users , :uid , :string end end
app/models/user.rbを以下のように編集します。
app/models/user.rb 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class User < ApplicationRecord devise :database_authenticatable , :registerable , :recoverable , :rememberable , :validatable , :omniauthable def self .find_for_github(auth) user = User .find_or_create_by(provider: auth.provider, uid: auth.uid) return user unless user.new_record? user.uid = auth.uid user.email = "#{auth.uid} -#{auth.provider} @fromgithub.com" user.password = Devise .friendly_token[0 , 20 ] user.save user end end
続いて github 認証後に実行されるコールバック処理を作成します。app/controllers/omniauth_collbacks_contoroller.rbを以下の通り作成します。
app/controllers/omniauth_callbacks_contoroller.rb 1 2 3 4 5 6 7 8 9 10 11 12 class OmniauthCallbacksController < Devise::OmniauthCallbacksController def github @user = User .find_for_github(request.env['omniauth.auth' ]) if @user .persisted? sign_in_and_redirect @user , event: :authentication else session["devise.github_data" ] = request.env["omniauth.auth" ] redirect_to new_user_registration_url end end end
作成したコールバック処理に転送されるように以下のようにルーティングを設定します。
config/routes.rb 1 2 3 4 5 Rails .application.routes.draw do devise_for :users , :controllers => { :omniauth_callbacks => "omniauth_callbacks" } end
github 認証確認 アプリケーションを起動し、http://localhost:3000/users/sign_inにアクセスします。
「Sign in with Github」をクリックします。 すると、以下の画面に進みます。
認証できると、devise でアプリケーションで設定したログイン後のリダイレクト先(何もしなければ root)にリダイレクトされます。
データベースを参照すると、User が 1 件増えていることが確認できます。
github 認証を実装できました。
プロバイダーを作ってみる github 認証を実装できました。 今度は、github の代わりに認証自体をしてくれるプロバイダを作成してみます。
devise を導入したユーザー認証する環境を用意 こちらでも devise を使ったユーザー認証をする環境が必要なので、作成します。Ruby on Rails での認証(Devise ライブラリ) を参照してもらえるといい。
ポート変更 ポート 3000 番で立ち上げるクライアントと同居させるので、プロバイダ側のポートを変更します。 今回は、5000 番にします。
config/puma.rb 1 port ENV .fetch("PORT" ) { 5000 }
OAuth で使うカラムを追加 User モデルに、uidカラムを追加します。 以下の内容でマイグレーションをします。
マイグレーションファイル 1 2 3 4 5 class AddColumnsToUsers < ActiveRecord::Migration [5.2 ] def change add_column :users , :uid , :string end end
追加した uid を設定するため、データが追加されたときに、uid を設定するようにしておきます。
app/models/user.rb 1 2 3 4 5 6 7 8 9 10 11 12 class User < ApplicationRecord devise :database_authenticatable , :registerable , :recoverable , :rememberable , :validatable after_create :set_uid def set_uid update(uid: Digest : :SHA512 .hexdigest(id.to_s)) end end
doorkeeper を導入 OAuth のプロバイダの仕組みを提供する gem、doorkeeperを導入します。
Gemfile
gem が導入できたら、以下の手順で doorkeeper 用のマイグレーションまで進めます。
1 2 3 bundle exec rails g doorkeeper:install bundle exec rails g doorkeeper:migration bundle exec rails db:migrate
config/initializers/doorkeeper.rbが作成されているので、 以下のように編集します。
config/initializers/doorkeeper.rb 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Doorkeeper .configure do orm :active_record resource_owner_authenticator do session[:return_to ] = request.fullpath current_user | | redirect_to(new_user_session_url) end admin_authenticator do end access_token_expires_in 2 .hours force_ssl_in_redirect_uri false end
セッション作成時に、認証を要求したクライアントへリダイレクトする設定を作成します。
1 bundle exec rails g controller users /sessions
app/controllers/users/sessions_controller.rbが作成されるので、以下の様に編集します。 参考元から、そのまま使わせてもらいます。
app/controllers/users/sessions_controller.rb 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Users::SessionsController < Devise::SessionsController def create self .resource = warden.authenticate!(auth_options) set_flash_message(:notice , :signed_in ) if is_flashing_format? sign_in(resource_name, resource) yield resource if block_given? if session[:return_to ] redirect_to session[:return_to ] session[:return_to ] = nil else respond_with resource, location: after_sign_in_path_for(resource) end end end
続いて、データを提供する API を作成します。
app/controllers/api/api_controller.rbapp/controllers/api/users_controller.rb の 2 つを作成します。
app/controllers/api/api_controller.rb 1 2 3 4 5 6 class Api::ApiController < ApplicationController private def current_resource_owner User .find(doorkeeper_token.resource_owner_id) if doorkeeper_token end end
app/controllers/api/users_controller.rb 1 2 3 4 5 6 7 8 class Api::UsersController < Api::ApiController before_action :doorkeeper_authorize! respond_to :json def info respond_with current_resource_owner end end
最後に、ルーティングの設定を行います。config/routes.rbを以下のように編集します。
config/routes.rb 1 2 3 4 5 6 7 8 9 Rails .application.routes.draw do use_doorkeeper devise_for :users , controllers: { sessions: 'users/sessions' } namespace :api do get '/info' => 'users#info' end end
プロバイダ確認 アプリケーションを起動し、http://localhost:5000/users/sign_inにアクセスします。 ユーザーを作成します。
テーブルを確認して、uidが追加されていることを確認しておきます。
続いてhttp://localhost:5000/oauth/applicationsにアクセスします。 すると、以下の画面になります。
以下のように設定して、登録しておきます。
UID と Secret を控えておきます。
プロバイダを作成して、次に作るアプリケーションを登録できました。
自作プロバイダに対して、OAuth 認証してみる 最初に作成した github 認証したアプリケーションを自作したプロバイダでも認証できるようにします。
自作プロバイダを認証できるように調整する。 必要な gem omniauth-oauth2 を導入します。 以下を Gemfile に追記して、インストールします。
Gemfile
omniouth が提供しないプロバイダで認証するには、ストラテジを自作する必要があります。 lib/omniauth/strategies/myapp.rb を作成します。
lib/omniauth/strategies/myapp.rb 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 require 'omniauth-oauth2' module OmniAuth module Strategies class Myapp < OmniAuth::Strategies::OAuth2 option :name , 'myapp' RAW_INFO_URL = 'api/info' uid { raw_info['uid' ] } info do { email: raw_info['email' ], name: raw_info['name' ] } end extra do { raw_info: raw_info } end def raw_info @raw_info | |= JSON .parse(access_token.get(RAW_INFO_URL ).response.body) end end end end
config/initializers/devise.rb を編集して myapp をプロバイダとして登録します。
config/initializers/devise.rb 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 require File .expand_path('../../../lib/omniauth/strategies/myapp' , __FILE__ )Devise .setup do |config | config.omniauth :github , 'Client id' , 'Client secret' , scope: 'user,public_repo' config.omniauth :myapp , 'UID' , 'Secret' , client_options: { site: 'http://localhost:5000' }, strategy_class: OmniAuth : :Strategies : :Myapp , scope: "test" end
app/models/user.rb を編集して、myapp 用の処理を作ります。
app/models/user.rb 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class User < ApplicationRecord def self .find_for_myapp(auth) user = User .find_or_create_by(provider: auth.provider, uid: auth.uid) return user unless user.new_record? user.uid = auth.uid user.email = "#{auth.uid} -#{auth.provider} @myapp.com" user.password = Devise .friendly_token[0 , 20 ] user.save user end end
app/controllers/omniauth_callbacks_controller.rb を編集して myapp 用の処理を追加します。
app/controllers/omniauth_callbacks_controller.rb 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class OmniauthCallbacksController < Devise::OmniauthCallbacksController def github @user = User .find_for_github(request.env['omniauth.auth' ]) if @user .persisted? sign_in_and_redirect @user , event: :authentication else session["devise.github_data" ] = request.env["omniauth.auth" ] redirect_to new_user_registration_url end end def myapp @user = User .find_for_myapp(request.env['omniauth.auth' ]) if @user .persisted? sign_in_and_redirect @user , event: :authentication else session["devise.github_data" ] = request.env["omniauth.auth" ] redirect_to new_user_registration_url end end end
自作プロバイダで認証確認 クライアント側のアプリケーションで/users/sign_inにアクセスすると、「Sign in with Myapp」というリンクが増えているので、こちらにアクセスします。
すると、次のようにアクセスを許可するかの画面が表示されます。 「Authorize」をクリックします。
すると、クライアントのログイン後のリダイレクト先に戻ります。
データベースを確認すると、クライアントの user にプロバイダの user と uid の同じものが作成されています。
自作のプロバイダで認証できました。
github と自作プロバイダで OAuth 認証をしてみました。 最終的にできる動作確認ができるまで足かけ 3 日程かけてしまいました。
参照している資料そのままの部分の多いのですが、自身で認証のプロバイダの実装をしてみることで、ただ使っているだけよりも理解が深まったと感じます。
何かサービスを作ることがあれば、今回のようにユーザーの管理をプロバイダに任せ、アプリケーションを分離させるのもよさそうです。 ただ、OAuth での認証を提供しているのは Google や github の様に大規模なサービスばかりです。 だからこそ認証の機能を任せる意味があるのではと考えたところでした。
認証を以外の使い方も試みてみたいところです。
ではでは。