Torihaji's Growth Diary

Little by little, no hurry.

Rails7でdevise-token-authを使って登録するまでの奮闘記-1章

はじめに

こんにちは、torihaziです

ただいま朝の8時半です。

今、Rails React のSPAで

devise-token-authを使って認証周りを作成中です

ただcurlでPOSTリクエスト使って登録やろうとしたんですけど

うまくいかず。

なんででしょうね。これ書いてる今も解決してません。

ということでゆるくいきます。

現状

gemにdeviseとdevise-token-auth、rack-cors入れてbundle installやりました。

その後で

rails generate devise:install
rails generate devise_token_auth:install User auth
=> 作成されたマイグレーションファイルいじった。
rails db:migrate

migrationファイル(抜粋)

## User Info
t.string :name, null: false, unique: true, limit: 50
t.string :email, null: false, unique: true
t.string :tel, null: false, unique: true
t.string :profile, limit: 160
t.string :place
t.string :web
t.boolean :is_active, null: false, default: false

user.rb

# frozen_string_literal: true

class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable,
         :confirmable
  include DeviseTokenAuth::Concerns::User
end

cors.rb

Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins "localhost:3000", "127.0.0.1:3000"

    resource "*",
      headers: :any,
      expose: ['access-token', 'expiry', 'token-type', 'uid', 'client'],
      methods: [:get, :post, :put, :patch, :delete, :options, :head]
  end
end

routes.rb

# frozen_string_literal: true

Rails.application.routes.draw do
  namespace :api do
    namespace :v1 do
      mount_devise_token_auth_for 'User', at: 'auth'
    end
  end
end

やったこと

まずapi機能するか試しにcurlで叩きました

curl localhost:3001/api/v1/auth -i -X POST -d '{"email":"test@example.com", "password":"password"}' -H "content-type:application/json"

するとレスポンスが

HTTP/1.1 422 Unprocessable Entity
~~~
{"success":false,"errors":["Missing 'confirm_success_url' parameter."],"status":"error","data":{"id":null,"provider":"email","uid":"","allow_password_change":false,
"name":null,"email":"test@example.com","tel":null,"profile":null,
"place":null,"web":null,"is_active":false,
"created_at":null,"updated_at":null}}

422はアレです。

サーバがリクエストは理解したけど処理ができないってやつで。

原因調査

怪しいとこは

"errors":["Missing 'confirm_success_url' parameter."],

だと思う。

他の人が次でやってたのでとりあえずこれを実行。

curl localhost:3001/api/v1/auth -i -X POST -d '{"email":"test@example.com", "password":"password", "password_confirmation": "password"}' -H "content-type:application/json"

結果変わらず。やっぱりconfirm_success_urlとかいうやつが悪そう。

なんか見つけた。 https://qiita.com/mtoyopet/items/076b623ac72f4f83c5f6

POSTのauthにリクエストを送信しますが、confirmableを設定すると、リクエストパラメターにconfirm_success_urlを追加する必要があります。

こいつっぽい。

追加。王手かね。

https://zenn.dev/shogo_matsumoto/articles/c6485b39c5f621

{
    "email": "送信先のメールアドレス",
    "password": "パスワード",
    "password_confirmation": "パスワード確認",
    "confirm_success_url": "メール内のリンクからのリダイレクト先のURL"
}

email→ 送信先のメールアドレスを設定(サインアップ時に入力した際にそのメールアドレスに認証メールが届く)
confirm_success_url→ 本認証が完了した際にリダイレクトする URL を指定する。今回のアプリではログインページに遷移するようにするが、フロントはこれから作るので、API 確認の際は適当にhttps://google.comなどと入力しておけばいい。ちゃんとそのページに遷移したら成功ということになる。

原因

migrationファイルにてconfirmableの設定を追加し、

user.rbにおいて:confirmableを追加したことによって、

confirm_success_urlというのをpostリクエストのパラメータに

含めなければならなかったのにやらずにやっていたから。

試しに入れて適当なものやってみよう。

まだメールの設定入れてないので本登録はできないけど、正常に処理はされるはず。

ということで修正して、curl実行。

curl localhost:3001/api/v1/auth -i -X POST -d '{"email":"test@example.com", "password":"password", "confirm_success_url": "http://localhost:3001"}' -H "content-type:application/json"

てすればいくでしょう。

知らんけど。

あ、だめだ。でも500で帰ってきた。

てことはリクエストとしてはOK

サーバ側で跳ねられただけ。進展。

NotNullViolation

あー。nameとかtelか。not_nullてしたもんね。

じゃ入れたらいけるかな。

その前にmigrationファイルで 誕生日 birthカラム入れるの忘れてた。

rails db:rollbackで戻して追加して、rails db:migrateして修正。

ということでcurlリベンジ。

curl localhost:3001/api/v1/auth -i -X POST -d '{"name": "hoge", "tel": "09012345678", "birth": "2024-04-04","email":"test@example.com", "password":"password", "confirm_success_url": "http://localhost:3001"}' -H "content-type:application/json"

およ。500だ。

なぜ。

入れたけど、サーバ側でもらえてないっぽい?

"error":"Internal Server Error","exception":"#\u003cActiveRecord::NotNullViolation:\"PG::NotNullViolation: ERROR: null value in column \\"name\\" of relation \\"users\\" ~~~

あー、あれか。

ストロングパラメータだっけか。

params.requireなんとかとかいう、コントローラに設定する

指定したものしか取得しないとかいうやつ。

さっきのqiitaの人が書いてた。

class Auth::RegistrationsController < DeviseTokenAuth::RegistrationsController

  private

  def sign_up_params
    params.permit(:name, :email, :password, :password_confirmation)
  end
end

device-token-auth本家にはこう書いてあった。

github.com

Accepted params can be customized using the devise_parameter_sanitizer system.

param追加したきゃなんかしろということらしい。

そこから飛べるdevise本家にはその追加方法が書いてあった。

github.com

class ApplicationController < ActionController::Base
  before_action :configure_permitted_parameters, if: :devise_controller?

  protected

  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:username])
  end
end

ということでqiitaのじゃなくて本家採用して書いてみよう。

class ApplicationController < ActionController::API
  before_action :configure_permitted_parameters, if: :devise_controller?
  include DeviseTokenAuth::Concerns::SetUserByToken

  protected

  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:name, :tel, :birth, :confirm_success_url])
  end
end

ということで書いてみた。いけるかな。

ならなかったら泣きたい

curl localhost:3001/api/v1/auth -i -X POST -d '{"name": "hoge", "tel": "09012345678", "birth": "2024-04-04","email":"test@example.com", "password":"password", "confirm_success_url": "http://localhost:3001"}' -H "content-type:application/json"

失敗。500。nandeya

{"status":500,"error":"Internal Server Error","exception":"#\u003cActiveModel::UnknownAttributeError: unknown attribute 'birth' for User

ただメッセージは変わったし、さっきの問題はクリアしたっぽい。

内容はbirthなんてものはUserモデルにはないよ、てことだと思う。

嘘コケ、さっき追加したばっかやんけ。

再起動してみよか。

docker compose restart

リベンジ。

"error":"Internal Server Error","exception":"#\u003cActiveModel::UnknownAttributeError: unknown attribute 'confirm_success_url' for User

今度はこっちがないと。

直感でさっきコントローラに書いた:confirm_success_urlを削除して持っ回やってみた。

{"status":500,"error":"Internal Server Error","exception":"#\u003cActionView::Template::Error: Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true\u003e"

なんか変わった。

調べてみると確認用メール送りたいんだけどメール送れない、ってエラー。

要はメールの設定をしろと。

そうなんすか。知らなかったです。

ということで今回は指定があるので、letter_opener_webで。

(かれこれ1時間くらいやってるなー。疲れた。)

入れる手順は公式を見てやると

gem入れて。buildして。

develoment.rbに追加して。

  config.action_mailer.delivery_method = :letter_opener_web
  config.action_mailer.default_url_options = { host: 'localhost:3001' }

routes.rb修正して

Rails.application.routes.draw do

  mount LetterOpenerWeb::Engine, at: "/letter_opener" if Rails.env.development?
  
  namespace :api do
    namespace :v1 do
      mount_devise_token_auth_for 'User', at: 'auth'
    end
  end
end

とりあえずletter_openerは出た。

ということでリリリリリベンジ。

curl localhost:3001/api/v1/auth -i -X POST -d '{"name": "hoge", "tel": "09012345678", "birth": "2024-04-04","email":"test@example.com", "password":"password", "confirm_success_url": "http://localhost:3001"}' -H "content-type:application/json"

なんか変わった。結果は422

{"status":"error","data":{"id":null,"provider":"email","uid":"","allow_password_change":false,"name":"hoge","email":"test@example.com","tel":"09012345678","profile":null,"place":null,"web":null,"birth":"2024-04-04","is_active":false,"created_at":null,"updated_at":null},"errors":{"email":["has already been taken"],"full_messages":["Email has already been taken"]}}%

emailがすでにありますよ。

一旦リセットしよう。

docker compose downしてから

docker compose run --rm api rails db:migrate:reset

再度 curl

200だーーー!

メールも来たー。

添付されたurlクリックして無事飛んだ。

なんか変なパラメータついてるけど、ここをやるのはまた後で。

終わりに

ただいま朝の11時です。

2時間かかりました。

登録自体はまだかもしれませんが、それは第2章で。

一旦休憩。