【Rails】自作WebアプリからAPIを叩いてMastodonアカウント作成

表題について個人開発のWebアプリまちかどルートv5.3への実装メモ。プログラミングに入門して8ヶ月弱。Mastodonのアカウント作成APIに挑戦してみました。

MastodonとのOauth連携について

まちかどルートでは開発スタート当初からSNS「Mastodon」のアカウントでログイン認証できるようにしてあります。

MastodonとのOauth連携についてここでは詳しく書きません。

たとえばこちらの記事などを参考にしています。

RailsなサービスでMastodonとのOauth連携を実装する
https://qiita.com/foloinfo/items/48389328da43d73ae7a0

アカウント作成用のAPIが用意された

今回の実装に至ったきっかけは、Mastodonのv2.7.0からアカウント作成用のAPIが追加されたことでした。さらにmastodon-api v2.0.0というgemにもMastodon::REST::Client#create_accountが追加されたんです。

これらによって自作のWebアプリからの新規アカウント作成プロセスをかなり簡略化できるのではないかと思い、「よしやってみよう」となりました。

なお、APIに関するドキュメントはこちらにあります。

view : アカウント作成フォーム

<p>新規アカウント登録</p>  
<%= form_with url: '/auth/create/account', method: :post, local: true do |f| %>  
  <input type="text" name="username" placeholder="ユーザー名(英数字&空白なし)">  
  <input type="email" name="email" placeholder="メールアドレス">  
  <input type="password" name="password" placeholder="パスワード">  
  <input type="hidden" name="agreement" value="true">  
  <input type="hidden" name="locale" value="ja">  
  <%= button_tag :type => "submit", data: { disable_with: "登録中..." } do %>  
    <span>登録</span>  
  <% end %>  
<% end %>

前述のドキュメントにあるとおり、Mastodonのアカウント作成ではusername email password agreement locale という5つのパラメータが必要なので、それらの入力フォームを作ります。

agreement のデータ型はBooleanなのでここでは true としてあります。たとえば「利用規約に同意する」といったチェックボックスを用意し、ユーザーが同意してチェックしたら true にする、という用途に使えるんでしょうね。

controller : sessions_controller.rb

def create  

if params[:username] # ここで新規アカウント作成or通常のログインかを判断  

  # APIを叩くときに必要となるMastodonインスタンスの管理者情報を抽出  
  # ここでは私が管理しているMastodonインスタンス「アナザーギルド」を例にしています  
  @mastodon_master = User.where(uid: 'townsguild@another-guild.com').order(created_at: :desc).first  

  # アカウント作成フォームで入力された5つのパラメータを格納  
  username = params[:username]  
  email = params[:email]  
  password = params[:password]  
  agreement = params[:agreement]  
  locale = params[:locale]  

  # 5つのパラメータをparamsにハッシュとして格納  
  params = { "username" => username, "email" => email, "password" => password, "agreement" => agreement, "locale" => locale }  

  # APIを叩けるようにするためのclient生成  
  client = Mastodon::REST::Client.new(base_url: "https://another-guild.com", bearer_token: @mastodon_master.token)  

  # すでに登録済みのアカウントかどうかを判断するための例外処理 begin~rescue  
  begin  
    # ここでアカウント作成APIを叩きます  
    @new_user = client.create_account(params)  
  rescue => e  
    flash[:error] = "登録できませんでした。ユーザー名またはメールアドレスはすでに存在します"  
    redirect_to root_path  
    return  
  end  

  # アカウントが無事に作成されたらuserモデルに新規レコード作成&保存  
  uid = username + '@another-guild.com'  
  @user = User.find_or_create_by(uid: uid) do |user|  
    user.provider = 'mastodon'  
    user.token = @new_user.access_token  
  end  

  flash[:notice] = "登録したメールアドレスにメールが届きます。確認のうえ、まちかどルートにログインしてください"  

end  

end

ルーティング : routes.rb

post "/auth/create/account" => "sessions#create"

アカウント作成フォームから sessions_controller.rbcreate に誘導するためのルーティングとしてこの1行を追記します。

Rack Middlewareへの例外処理

以上のように書くと簡単なようですけど、じぶんにとっては難題でした。それに拍車をかけるように大きな壁が…。というのもアカウント作成までうまくいき、Mastodonから確認メールが送られてくるのですが

確認メールにある「確認し まちかどルート に戻る」ボタンを押すと

NoMethodError (undefined method `split' for nil:NilClass):  

omniauth-mastodon (0.9.3) lib/omniauth/strategies/mastodon.rb:82:in `set_options_from_identifier'  
omniauth-mastodon (0.9.3) lib/omniauth/strategies/mastodon.rb:41:in `callback_phase'  
...

というエラーが出てしまったんです。

いろいろ調べたりしたのですがスキル不足でどうにもならず、無理やり下記のような対策をしました。

具体的には、今回のケースではRailsのcontrollerで起きた問題ではなくgemのomniauth-mastodonつまりRack middlewareで発生した例外であるため exceptions_app というものを利用しました。

Rails.application.configure do  
  config.exceptions_app = self.routes  
end
class ErrorsController < ActionController::Base  
  def not_found  
    redirect_to root_path  
  end  
end
  get '500', to: 'errors#not_found'

あとがき

最後の例外処理があまりにも無理やりな感じがして、なんともしまらないメモになってしまいました。まだまだ未熟なのでこれからも学びつづけながらアウトプットしていきます。

余談ですが

Mastodonインスタンスの管理ページにある「サイト設定」で「新規登録を受け付ける」にチェックが入っていないと下記のエラーが発生してアカウント作成APIを受け付けません。

Mastodon::Error::Forbidden (Mastodon::Error::Forbidden):

これに気づくだけでも時間を要してしまいました。

けれど自作のWebアプリからの新規アカウント作成プロセスを簡略化したい!という目的はある程度まで達成できたと思います。TwitterやFacebookとかと違って(ないですよね?)アカウント作成APIまで用意しているMastodon。今後も期待しつつそのほかのAPIにも機会があったらまた挑戦したいです。