Railsにこれから初めて触れる方を対象にしたチュートリアルです
RailsにMastodonでのログイン機能を実装するチュートリアルになります
まず、rails newを実行し、アプリのひな型を作成します
rails new mastodon_login
次に、作成したアプリのディレクトリへと移動します
cd mastodon_login
Twitterでのログイン機能を実装するにあたって、Deviseを使用します
まず、Gemfileにgem 'devise'を追加してbundle installを実行します
gem 'devise'
bundle install
この時、sqlite3がインストールできないエラーが発生するかもしれません
その場合は以下のようにsqlite3のバージョンを修正してbundle installを実行してください
gem 'sqlite3', '1.3.13'
bundle install
bundle install後、以下のコマンドを実行してDeviseをインストールします
rails g devise:install
次に、Deviseで使用するViewファイルを作成します
rails g devise:views
そして、Deviseで使用するModelを作成し、マイグレーションを実行します
rails g devise user
rails db:migrate
これでDeviseの導入は完了です!
Mastodonでのログインを実装するためには、Mastodonへアプリケーションの登録が必要になります
自分のアカウントがあるMastoonインスタンスにてユーザー設定をクリックし、開発という項目をクリックします。
新規アプリをクリックし、アプリ名を入力の上送信をクリックします。
その後、作成したアプリをクリックするとクライアントキーとシークレットキーを取得できます。
この二つは後々で使いますのでわかるようにメモしておきます(ただし、第三者とは絶対に共有しないでください!)
Mastodonから取得したクライアントキーとシークレットキーをソースコードにそのまま貼り付けてしまうと悪意のある第三者に利用されてしまう可能性があります
実際に、発生した事例としては以下のようなものがあります
AWSが不正利用され300万円の請求が届いてから免除までの一部始終
こういったことを回避するためにもDotenvなどを使用してソースコードに直接書かないようにする必要があります
まず、Gemfileにgem 'dotenv-rails'を追加します
gem 'dotenv-rails'
その後、bundle installを実行します
bundle install
そして、先ほど取得したキーなどを.envに書き込みます
.envはGemfileと同じ位置に作成します
# Using Mastodon API
MASTODON_CLIENT_KEY=`クライアントキー`
MASTODON_SECRET_KEY=`シークレットキー`
最後に、.gitignoreを編集し、.envをGitの管理対象から外します
# See https://help.github.com/articles/ignoring-files for more about ignoring files.
#
# If you find yourself ignoring temporary files generated by your text editor
# or operating system, you probably want to add a global ignore instead:
#   git config --global core.excludesfile '~/.gitignore_global'
# Ignore bundler config.
/.bundle
# Ignore the default SQLite database.
/db/*.sqlite3
/db/*.sqlite3-journal
# Ignore all logfiles and tempfiles.
/log/*
/tmp/*
!/log/.keep
!/tmp/.keep
# Ignore uploaded files in development
/storage/*
!/storage/.keep
/node_modules
/yarn-error.log
/public/assets
.byebug_history
# Ignore master key for decrypting credentials and more.
/config/master.key
.env
これでDotenvの導入は完了です!
Mastodonアカウントでのログイン機能はOauthという認証を使用します
まず、必要なgemをGemfileに追加します
gem 'omniauth'
gem 'omniauth-mastodon'
gem 'mastodon-api', require: 'mastodon'
bundle installでgemをインストールします
bundle install
Userモデルにカラムを追加してマイグレーションを行います
rails g migration AddColumnsToUsers uid:string provider:string
rails db:migrate
次に、Mastodonのドメイン情報などを記憶するモデルMastodonClientを以下のコマンドで作成します。
rails generate model MastodonClient domain:string client_id:string client_secret:string 
config/initializers/devise.rbを編集してDevise側の設定を変更します
Devise.setup do |config|
  #<省略>
  # ==> OmniAuth
  # Add a new OmniAuth provider. Check the wiki for more information on setting
  # up on your models and hooks.
  # config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo'
  config.omniauth :mastodon, ENV['MASTODON_CLIENT_KEY'], ENV['MASTODON_SECRET_KEY']
  config.omniauth :mastodon, scope: 'read follow', credentials: lambda { |domain, callback_url|
    client = MastodonClient.where(domain: domain).first_or_initialize(domain: domain)
    return [client.client_id, client.client_secret] unless client.new_record?
    new_client = Mastodon::REST::Client.new(base_url: "https://#{domain}").create_app('MastodonLoginSample', callback_url, 'read follow')
    client.client_id = new_client.client_id
    client.client_secret = new_client.client_secret
    client.save
    [client.client_id, client.client_secret]
  }
 #<以下省略>
end
Dotenv経由でMastodonのクライアントキーなどを読み取っています
その後、Userモデルに:omniauthableを追加します
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable, :omniauthable
end
その後、self.find_for_oauth(auth)というメソッドを追加します
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable, :omniauthable
  def self.find_for_oauth(auth)
    user = User.where(uid: auth.uid, provider: auth.provider).first
    unless user
      user = User.create(
        uid:      auth.uid,
        provider: auth.provider,
        email:    User.dummy_email(auth),
        password: Devise.friendly_token[0, 20]
      )
      user.save!
    end
    current_user = user
  end
  private
    def self.dummy_email(auth)
      "#{auth.uid}"
    end
end
このメソッドにより、アカウントが既に作成されているかをチェックし、アカウントがない場合は新しく作成することができるようになります
app/controllers/users/omniauth_callbacks_controller.rbというコールバック処理を行うコントローラーを作成します
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def mastodon
    callback_from :mastodon
  end
  private
  def callback_from(provider)
    provider = provider.to_s
    @user = User.find_for_oauth(request.env['omniauth.auth'])
    if @user.persisted?
      flash[:notice] = I18n.t('devise.omniauth_callbacks.success', kind: provider.capitalize)
      sign_in_and_redirect @user, event: :authentication
    else
      session["devise.#{provider}_data"] = request.env['omniauth.auth']
      redirect_to new_user_registration_url
    end
  end
end
最後に、config/routes.rbにコールバック先のルーティングを追加します
Rails.application.routes.draw do
  devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' }
end
これでTwitterでのログイン機能は実装できました!
最後に、Mastodonログインが実際に機能しているかどうかを確認するためのページを作成します
rails g controller web index
app/views/web/index.html.erbにTwitterログインのリンクなどを追加します
<h1>Web#index</h1>
<p>Find me in app/views/web/index.html.erb</p>
<%= link_to 'Mastodon Login', user_mastodon_omniauth_authorize_path %>
<% if user_signed_in? %>
    Your Welcome!
<% end %>
また、config/routes.rbを以下のように編集します
Rails.application.routes.draw do
  root 'web#index'
  devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' }
end
Mastodon Loginをクリックしてログインできるようになっています
また、ログインするとYour Welcome!の文字が画面に表示されるようになっています
rails sでローカルサーバを起動し、localhost:3000にアクセスします
その後、Mastodon LoginのリンクをクリックするとMastodonのアプリ認証の画面が表示されます
認証後、localhost:3000にYour Welcome!と表示されていればOKです!