Fusic Tech Blog

Fusion of Society, IT and Culture

Railsアプリのユーザ認証をAzureADと連携する
2021/04/20

Railsアプリのユーザ認証をAzureADと連携する

Railsアプリを作るときの認証周りはすっかりdevise頼みとなってしまった今日このごろ。

最近、Azureを使ったシステムを開発しています。他のクラウドサービスでもそうですが、Azureが提供するサービスとの連携が強くなればなるほど、認証周りをAzure ADと連携する必要性が出てきます。

GitHubやTwitter, Facebookと連携したOAuth2認証の方法は調べるとすぐに見つかるのですが、Azure ADとのOAuth2認証の方法はなかなか見つからなかったので本記事にまとめます。

やりたいこと

認証画面で「Azure ADで認証」を選択できるようにします。

通常のID/PASS認証を実装

まずはdeviseを使って普通にID/PASS認証を実装します。 既にご存知の方は読み飛ばしても大丈夫です。

rails new

まずはrains newします。 DBはPostgreSQLとしていますが、他のDBでも問題ありません。

# Railsアプリを作成
$ rails new devise_omniauth_azure_ad -d postgresql
$ cd devise_omniauth_azure_ad

# DB接続を確認
$ vi config/database.yml # 適宜編集
$ bin/rails db:create rails db:migrate

Deviseの導入

以下コマンドを実行してdeviseをインストールします。

$ bundle add devise
$ bin/rails g devise:install

インストール後、セットアップ手順が表示されるのでそれぞれ実施します。

default_url_optionslocalhost:3000 を追加します。

config/environments/development.rb

require "active_support/core_ext/integer/time"

Rails.application.configure do
  # 省略
  config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
end

ルーティングに root を追加。

config/routes.rb

Rails.application.routes.draw do
  root to: 'home#index'
end

root に相当するController, VIEWを作成。

$ bin/rails g controller home index

home#index へのアクセスを認証必須にする。

app/controllers/home_controller.rb

class HomeController < ApplicationController
  before_action :authenticate_user!

  def index
  end
end

Flashメッセージが表示されるようHTMLを修正。

app/views/layouts/application.html.erb

<!DOCTYPE html>
<html>
  <head>
    <title>DeviseOmniauthAzureAd</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

  <body>
    <p class="notice"><%= notice %></p>
    <p class="alert"><%= alert %></p>
    <%= yield %>
  </body>
</html>

deviseのVIEWファイルを作成。

$ bin/rails g devise:views

Userモデルの作成

以下コマンドを実行して、Userモデルを作成します。

$ bin/rails g devise user

モデルを作成したらマイグレーションも実行しておきましょう。

$ bin/rails db:migrate

動作確認

予めRails Consoleにてダミーユーザーを作成します。

$ bin/rails c
irb(main):001:0> User.create(email: 'test@example.com', password: 'password1!', password_confirmation: 'password1!')

Railsサーバーを起動し、ブラウザで http://localhost:3000 へアクセスします。

$ bin/rails s

以下のようにログインページへリダイレクトされます。 ID, Passwordを入力して「Log in」をクリックしましょう。

ログインに成功しました。

Azure ADと認証を連携させる

ここからがAzure ADと認証を連携させる手順です。 手順はdeviseのWikiにも記載されているので、迷ったらこちらも併せて参照ください。

Gemを追加する

以下コマンドを実行して必要なGemをインストールします。

$ bundle add omniauth -v 1.9.1
$ bundle add omniauth-azure-activedirectory-v2
$ bundle add dotenv-rails

2021-4-20現在、omniauth の最新バージョンをインストールするとRailsの起動時に次のようなエラーが発生します。

$ bin/rails c
/devise_omniauth_azure_ad/vendor/bundle/ruby/3.0.0/gems/devise-4.7.3/lib/devise/omniauth.rb:12:in `<main>': You are using an old OmniAuth version, please ensure you have 1.0.0.pr2 version or later installed. (RuntimeError)

このため、下記Issueが解決するまでは上記のように 1.9.1 をインストールするようにしましょう。

https://github.com/heartcombo/devise/issues/5326

認証プロバイダを追加

config/initializers/devise.rb に以下のように追記することで認証プロバイダを追加します。

config/initizlizers/devise.rb

Devise.setup do |config|
  # 省略
  config.omniauth :azure_activedirectory_v2,
                  client_id: ENV['AZURE_CLIENT_ID'],
                  client_secret: ENV['AZURE_CLIENT_SECRET'],
                  tenant_id: ENV['AZURE_TENANT_ID']
end

Userモデルの認証設定を追加

OAuth2認証に対応するため、認証設定を追加します。

app/models/user.rb

class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable,
         :omniauthable, omniauth_providers: [:azure_activedirectory_v2]
end

また、この後追加するコントローラから使用するメソッドを追加します。

app/models/user.rb

class User < ApplicationRecord
  # 省略
  
  def self.from_omniauth(auth)
    where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
      user.email = auth.info.email
      user.password = Devise.friendly_token[0, 20]

      # MEMO: 氏名やアバター画像等を取得したい場合は有効化する
      # user.name = auth.info.name
      # user.image = auth.info.image
    end
  end
end

OAuthで使用するカラムが増えるので、追加します。

$ bin/rails g migration AddColumnsToUsers uid:string provider:string

以下のようにマイグレーションファイルを編集します。

db/migrate/xxxxxxxxxxxxxx_add_columns_to_users.rb

class AddColumnsToUsers < ActiveRecord::Migration[6.0]
  def change
    add_column :users, :uid, :string, null: false
    add_column :users, :provider, :string, null: false, default: ""
    add_index :users, [:uid, :provider], unique: true
  end
end

カラムを追加した都合上、既にユーザーが存在しているとマイグレーションエラーとなるため、以下のようにリセット→マイグレーションを実行します。

$ bin/rails db:migrate:reset

OAuth2コールバック用のコントローラを追加

OAuth2認証におけるコールバックを処理するコントローラを追加します。

$ mkdir app/controllers/users
$ touch app/controllers/users/omniauth_callbacks_controller.rb

次のように実装します。

app/controllers/users/omniauth_callbacks_controller.rb

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  skip_before_action :verify_authenticity_token, only: :azure_activedirectory_v2

  def azure_activedirectory_v2
    @user = User.from_omniauth(request.env["omniauth.auth"])

    if @user.persisted?
      sign_in_and_redirect @user, event: :authentication
      set_flash_message(:notice, :success, kind: "azure_activedirectory_v2") if is_navigational_format?
    else
      session["devise.azure_activedirectory_v2_data"] = request.env["omniauth.auth"].except(:extra)
      redirect_to new_user_registration_url
    end
  end

  def failure
    redirect_to root_path
  end
end

OAuth2コールバック用のroutesを追加

config/routes.rb を次のように修正します。

config/routes.rb

Rails.application.routes.draw do
  root to: 'home#index'
  devise_for :users, controllers: { omniauth_callbacks: "users/omniauth_callbacks" }
end

Azure ADの設定

Azure Portalにログインし、Azure Active Directory→App registrationsへ移動します。

「+ New registration」をクリックしましょう。

NameおよびRedirect URIを入力します。Redirect URIは http://localhost:3000/users/auth/azure_activedirectory_v2/callback とします。

アプリケーションの登録が完了したら、Client IDおよびTenant IDが表示されるので控えておきます。(後ほど使用します)

「Certificates & secrets」→「+ New client secret」の順でクリックします。

説明と期限を入力して、「Add」をクリックします。

作成が完了するとClient Secretが表示されるので控えておきます。(後ほど使用します)

Railsのルートディレクトリに .env を作成し、Client ID, Tenant ID, Client Secretを記載します。

.env

AZURE_CLIENT_ID=xxxx
AZURE_CLIENT_SECRET=xxxx
AZURE_TENANT_ID=xxxx

このファイルは秘匿情報ですので、.gitignore に追記しておきます。

$ echo '.env' >> .gitignore

動作確認

Railsサーバーを起動し、ブラウザで http://localhost:3000 へアクセスします。

$ bin/rails s

以下のようにログインページへリダイレクトされます。 「Sign in with AzureActivedirectoryV2」をクリックしましょう。

Microsoftのログイン画面が表示されます。使用するテナントに属するアカウントを選択しログインしましょう。

アクセス許可する内容を確認し「承諾」をクリックします。

ログイン成功しました。

まとめ

いくつかハマりどころはありますが、公式のドキュメントの手順をAzure用にカスタマイズすることで、OAuth2認証を実現できました。

Azureと密接に連携したサービスを実装する際、ぜひお試しください。

参考

yuuu

yuuu

2018年の年明けに組込み畑からやってきた、2児の父 兼 Webエンジニアです。 mockmockの開発・運用を担当しており、組込みエンジニア時代の経験を活かしてデバイスをプログラミングしたり、簡易的なIoTシステムを作ったりしています。主な開発言語はRuby、時々Go。