読者です 読者をやめる 読者になる 読者になる

SideCI TechBlog

SideCIを作っているアクトキャットのエンジニアによる技術ブログです。


モデルがなくてもいいじゃない!deviseを使わないでユーザ認証をさくっと実現する

こんにちは!シン・ゴジラをあと3回くらい見たい @sweep3092 です。

f:id:sideci-dev:20160923132553p:plain

Railsでユーザ認証といえばもはやセットのようになっている devise + omniauth という構成が一般的ですが、 今回はありそうでなかなか情報が見つからない、Railsでモデルを持たずにさくっとユーザ認証をする話をしようと思います。

モデルを持たずにユーザ認証を実装するのは、なんと言っても既存のユーザ認証の仕組みとバッティングしにくいのがメリットです。 deviseを使うといろいろとイイカンジに設定してくれるのでとても便利ですが、別の管理のユーザ認証の仕組みを追加しようとしたりすると嵌まりがちです。 ここで原点に立ち返って、「devise使わないでもいいのでは?」となり、「そもそもモデル要らないのでは」となったのが本記事を書いたきっかけになります。

ユーザ認証の仕組みだけをさくっと提供するのが目的なので、認証したユーザを別のモデルとリレーションさせたりするのにはあまり向きませんが、 社内OAuthアカウントだけ認証できればいいから出来るだけ簡単に作りたい、といった場合にはモデルはなくても十分機能する場合も多いのではないでしょうか。

本記事では、omniauthのroutesやAPIキーの設定などの基本的な設定を済ませた前提で、モデルを持たないユーザ認証の仕組みを実現するControllerの書き方をご紹介いたします。

目的

  • deviseも使わずモデルも持たずにomniauthでユーザ認証の仕組みを作る
  • 社内アカウントのみ許可する

方法

今回はすでに User というモデルが存在すると仮定して、 Staff という別のユーザの概念をアプリケーションに持ち込むことを想定し、 Staff 専用のApplicationController, SessionsControllerを作ります。 このとき User とバッティングしないように、namespaceを明示的に分けます。

SessionsController

omniauthのcallbackが帰ってくるcreateアクションでは、セッションに使いそうなユーザ情報を雑に入れておきます。 また、このとき「社内アカウントであるか」をチェックします。 今回はメールアドレスが独自ドメインのものかを確かめるようにしていますが、ここは用途に合わせて変更して頂くのが良いかと思います。

ApplicationController側でセッションをみてログイン済みかどうかを確認するので、ログアウトの際には reset_session すればOKです。

class Staff::SessionsController < Staff::ApplicationController
  skip_action_callback :authenticate_staff!
  def login
  end

  def create
    auth = request.env['omniauth.auth']

    # 社内アカウントじゃなければログイン画面に戻してエラー表示
    unless auth.info.email.end_with? '@mycompany.co.jp'
      redirect_to staff_sessions_login_path, flash: {error: '社内アカウントのみ使用できます'}
    end

    # モデルなどに格納せず、sessionに使いそうなユーザ情報を雑に入れる
    session[:staff_uid] = auth.uid
    session[:staff_name] = auth.info.name
    session[:staff_email] = auth.info.email

    redirect_to staff_path
  end

  def destroy
    reset_session
    redirect_to staff_sessions_login_path
  end

  def failure
    redirect_to staff_sessions_login_path
  end
end

ApplicationController

ここでは、 Staff が認証済みであるかどうかを確認するメソッドを定義します。情報が欠けていたら reset_session をしてログイン画面にリダイレクトします。 また、ログイン中の Staff の情報を取得するヘルパメソッドを定義します。deviseで使える current_user などと同じようなイメージです。 同様に、 staff_signed_in? などのヘルパーメソッドも用意しておくと使い勝手が良いかと思います。

class Staff::ApplicationController < ActionController::Base
  helper_method :current_staff
  helper_method :staff_signed_in?
  before_action :authenticate_staff!, except: [:login]
  skip_action_callback :authenticate_staff!

  private

  def authenticate_staff!
    if session[:staff_uid].blank? || session[:staff_email].blank? || session[:staff_name].blank?
      reset_session
      redirect_to staff_sessions_login_path, flash: {error: 'ログインしてください'}
    end
  end

  def current_staff
    return nil if session[:staff_uid].blank? || session[:staff_email].blank? || session[:staff_name].blank?
    {
        uid: session[:staff_uid],
        email: session[:staff_email],
        name: session[:staff_name],
    }
  end

  def staff_signed_in?
    current_staff.present?
  end
end

使い方

<p>ようこそ <%= current_staff[:name] %> さん</p>

とか

<% if staff_signed_in? %>
 <%= link_to 'logout', staff_sessions_destroy_path %>
<% end %>

さいごに

やってみれば何の変哲も無い当たり前の超絶簡単なユーザ認証ですが、omniauthってどうやって使うんだっけ、と調べているとどうしてもdeviseを使うように流されてしまいがちのように思います。 ですが用途によってはRailsとは言ってもdeviseに頼らず、モデルに縛られずに原点に立ち返ってセッションのみでさくっとユーザ認証を済ませてしまうのもいいのではないでしょうか。

現在アクトキャットでは一緒に働いて頂けるメンバーを募集しています。 Webエンジニアの方、インフラエンジニアの方、Webデザイナー、マーケターの方を積極採用中です! 興味がある方はぜひWantedlyからご応募ください!

https://www.wantedly.com/companies/actcat/projects