OmniAuth (Ruby)
認証ライブラリ
OmniAuth (Ruby)
概要
OmniAuthは、Rubyアプリケーション向けの柔軟な認証システムです。Rackミドルウェアを活用し、OAuth、OpenID、その他多数の認証プロバイダーとの統合を簡単に実現できます。戦略パターンを使用して様々な認証サービス(Google、Facebook、Twitter、GitHub等)をプラグイン方式で追加でき、RailsをはじめとするRack対応フレームワークでの認証実装を大幅に簡素化します。
詳細
OmniAuthは、Ruby Webアプリケーション開発における事実上の標準認証ライブラリとして長年にわたって信頼されています。主な特徴として、戦略(Strategy)ベースのアーキテクチャによる拡張性、複数認証プロバイダーの同時サポート、統一されたAPIインターフェース、CSRF保護機能があります。
OmniAuth 2.0以降では、セキュリティ強化のためCSRF保護がデフォルトで有効になり、Rails環境ではomniauth-rails_csrf_protection gemとの組み合わせが推奨されています。また、開発環境向けの:developer戦略や、本格的なプロダクションでの外部プロバイダー戦略など、用途に応じた柔軟な設定が可能です。
認証フローは標準化されており、すべての戦略が共通の/auth/:provider/callbackエンドポイントを使用し、認証情報はrequest.env['omniauth.auth']として一貫した形式で提供されます。
メリット・デメリット
メリット
- 戦略パターン: プラグイン形式で多数の認証プロバイダーをサポート
- 統一API: プロバイダーが異なっても一貫したインターフェース
- Rack統合: RailsやSinatraなど主要Rubyフレームワークでの使用が容易
- 豊富なエコシステム: 100を超える認証戦略が利用可能
- CSRF保護: セキュリティベストプラクティスに対応
- 設定の柔軟性: 開発環境から本番環境まで適応可能
デメリット
- Ruby限定: Ruby以外の言語では使用不可
- セットアップの複雑さ: 初期設定と戦略選択が複雑になる場合がある
- CSRF設定: Rails環境でのCSRF保護設定が必要
- セッション管理: アプリケーション側でのセッション管理実装が必要
参考ページ
書き方の例
Gemfileの設定
# Gemfile
gem 'omniauth'
gem 'omniauth-rails_csrf_protection' # Rails用CSRF保護
# 必要な戦略を追加
gem 'omniauth-google-oauth2'
gem 'omniauth-facebook'
gem 'omniauth-twitter'
gem 'omniauth-github'
Rails初期化設定
# config/initializers/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
# 開発環境用の簡易戦略
provider :developer unless Rails.env.production?
# Google OAuth2
provider :google_oauth2,
ENV['GOOGLE_CLIENT_ID'],
ENV['GOOGLE_CLIENT_SECRET']
# Facebook
provider :facebook,
ENV['FACEBOOK_APP_ID'],
ENV['FACEBOOK_APP_SECRET']
# GitHub
provider :github,
ENV['GITHUB_CLIENT_ID'],
ENV['GITHUB_CLIENT_SECRET']
end
# CSRF保護の設定(rack_csrf gemを使用する場合)
OmniAuth::AuthenticityTokenProtection.default_options(
key: "csrf.token",
authenticity_param: "_csrf"
)
ルート設定
# config/routes.rb
Rails.application.routes.draw do
# OmniAuthコールバック
get '/auth/:provider/callback', to: 'sessions#create'
# ログインページ
get '/login', to: 'sessions#new'
# ログアウト
delete '/logout', to: 'sessions#destroy'
root 'home#index'
end
セッションコントローラー
# app/controllers/sessions_controller.rb
class SessionsController < ApplicationController
def new
# ログインページの表示
end
def create
# OmniAuth認証後のコールバック処理
auth_data = request.env['omniauth.auth']
user = User.find_or_create_by(email: auth_data.info.email) do |u|
u.name = auth_data.info.name
u.provider = auth_data.provider
u.uid = auth_data.uid
u.avatar_url = auth_data.info.image
end
if user.persisted?
session[:user_id] = user.id
redirect_to root_path, notice: 'ログインしました'
else
redirect_to login_path, alert: 'ログインに失敗しました'
end
end
def destroy
session[:user_id] = nil
redirect_to root_path, notice: 'ログアウトしました'
end
end
Userモデル
# app/models/user.rb
class User < ApplicationRecord
validates :email, presence: true, uniqueness: true
validates :name, presence: true
validates :provider, presence: true
validates :uid, presence: true
def self.from_omniauth(auth_data)
where(provider: auth_data.provider, uid: auth_data.uid).first_or_create do |user|
user.email = auth_data.info.email
user.name = auth_data.info.name
user.avatar_url = auth_data.info.image
end
end
def display_name
name.presence || email.split('@').first
end
end
# マイグレーション例
# rails generate migration CreateUsers email:string name:string provider:string uid:string avatar_url:string
ログインビュー
<!-- app/views/sessions/new.html.erb -->
<div class="login-page">
<h1>ログイン</h1>
<div class="login-options">
<!-- 開発環境用 -->
<% unless Rails.env.production? %>
<%= form_tag('/auth/developer', method: 'post', data: {turbo: false}) do %>
<button type='submit' class="btn btn-developer">
開発者ログイン
</button>
<% end %>
<% end %>
<!-- Google -->
<%= link_to '/auth/google_oauth2', method: :post,
data: {turbo: false},
class: 'btn btn-google' do %>
<i class="fab fa-google"></i> Googleでログイン
<% end %>
<!-- Facebook -->
<%= link_to '/auth/facebook', method: :post,
data: {turbo: false},
class: 'btn btn-facebook' do %>
<i class="fab fa-facebook"></i> Facebookでログイン
<% end %>
<!-- GitHub -->
<%= link_to '/auth/github', method: :post,
data: {turbo: false},
class: 'btn btn-github' do %>
<i class="fab fa-github"></i> GitHubでログイン
<% end %>
</div>
</div>
ApplicationController認証ヘルパー
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_action :authenticate_user!, except: [:index, :show]
private
def current_user
@current_user ||= User.find(session[:user_id]) if session[:user_id]
end
helper_method :current_user
def user_signed_in?
current_user.present?
end
helper_method :user_signed_in?
def authenticate_user!
unless user_signed_in?
redirect_to login_path, alert: 'ログインが必要です'
end
end
def require_admin
unless current_user&.admin?
redirect_to root_path, alert: '管理者権限が必要です'
end
end
end
複数プロバイダーサポート
# ユーザーが複数の認証プロバイダーを使用できる場合
class UserAuth < ApplicationRecord
belongs_to :user
validates :provider, presence: true
validates :uid, presence: true
validates :user_id, uniqueness: { scope: [:provider, :uid] }
end
class User < ApplicationRecord
has_many :user_auths, dependent: :destroy
def self.from_omniauth(auth_data)
# 既存の認証があるかチェック
auth = UserAuth.find_by(provider: auth_data.provider, uid: auth_data.uid)
if auth
auth.user
else
# メールアドレスで既存ユーザーを検索
user = User.find_by(email: auth_data.info.email)
if user
# 既存ユーザーに新しい認証を追加
user.user_auths.create!(
provider: auth_data.provider,
uid: auth_data.uid
)
else
# 新しいユーザーを作成
user = User.create!(
email: auth_data.info.email,
name: auth_data.info.name,
avatar_url: auth_data.info.image
)
user.user_auths.create!(
provider: auth_data.provider,
uid: auth_data.uid
)
end
user
end
end
end
カスタム戦略の作成
# lib/omniauth/strategies/custom_provider.rb
module OmniAuth
module Strategies
class CustomProvider < OmniAuth::Strategies::OAuth2
option :name, 'custom_provider'
option :client_options, {
site: 'https://api.customprovider.com',
authorize_url: '/oauth/authorize',
token_url: '/oauth/token'
}
uid { raw_info['id'] }
info do
{
name: raw_info['name'],
email: raw_info['email'],
image: raw_info['avatar_url']
}
end
extra do
{
raw_info: raw_info
}
end
private
def raw_info
@raw_info ||= access_token.get('/me').parsed
end
end
end
end
# config/initializers/omniauth.rb で使用
Rails.application.config.middleware.use OmniAuth::Builder do
provider :custom_provider, ENV['CUSTOM_CLIENT_ID'], ENV['CUSTOM_CLIENT_SECRET']
end
エラーハンドリング
# config/initializers/omniauth.rb
OmniAuth.config.on_failure = Proc.new do |env|
# 失敗時のカスタム処理
SessionsController.action(:failure).call(env)
end
# app/controllers/sessions_controller.rb
class SessionsController < ApplicationController
def failure
error = params[:message] || '認証に失敗しました'
redirect_to login_path, alert: "ログインエラー: #{error}"
end
end
# config/routes.rb
get '/auth/failure', to: 'sessions#failure'
Sinatra での使用例
# app.rb
require 'sinatra'
require 'omniauth'
require 'omniauth-google-oauth2'
class MyApp < Sinatra::Base
use Rack::Session::Cookie, secret: 'your-secret-key'
use OmniAuth::Builder do
provider :google_oauth2, ENV['GOOGLE_CLIENT_ID'], ENV['GOOGLE_CLIENT_SECRET']
end
get '/' do
if session[:user]
"Hello, #{session[:user]['name']}!"
else
'<a href="/auth/google_oauth2">Sign in with Google</a>'
end
end
get '/auth/:provider/callback' do
auth = request.env['omniauth.auth']
session[:user] = {
'name' => auth.info.name,
'email' => auth.info.email
}
redirect '/'
end
get '/logout' do
session[:user] = nil
redirect '/'
end
end