0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

devise_token_authでログイン機能を実装する

0
Last updated at Posted at 2025-11-11

はじめに

この記事はプログラミング初学者が他の記事を参考にしたり、実際に実装してみたりして、アウトプットの一環としてまとめたものです。内容に不備などあればご指摘いただますと幸いです。

今回、Rails(API) x ReactでX(旧Twitter)のクローンサイトを作る際にログイン機能を実装しました。
そのログイン機能の実装方法について備忘録も兼ねて記事を作成していきます。

この記事では、devise_token_authを使って新規登録機能を実装した後、ログイン機能を追加します。必要なgemや設定がまだ済んでいない方は、先に下記の記事を参考に設定をお願いします。
devise_token_authで新規登録機能を実装する

ルーティングの設定

config/routes.rb
Rails.application.routes.draw do
  namespace :api do
    namespace :v1 do
      mount_devise_token_auth_for 'User', at: 'users', controllers: {
        registrations: 'api/v1/registrations'
      }
    end
  end
end

これで api/v1/users/sign_in や /api/v1/users/sign_out が生成されます。

CORSの設定

config/initializers/cors.rbで下記の設定をします。

config/initializers/cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins 'http://localhost:5173'

    resource '*',
             headers: :any,
             # exposeの行を追記
             expose: %w[access-token expiry token-type uid client],
             methods: %i[get post put patch delete options head]
  end
end

expose にトークン関連ヘッダーを含めるのがポイントです。
これをしないと、フロント側からヘッダーの値が取得できません。

devise_token_auth.rbの設定

config/initializers/devise_token_auth.rbで下記のコードをコメントアウトします。

config/initializers/devise_token_auth.rb
DeviseTokenAuth.setup do |config|
  # 認証トークンをリクエストごとに更新するかどうかを指定する設定
  config.change_headers_on_each_request = false # trueからfalseに変更

  # トークンの有効期間を決める設定
  config.token_lifespan = 2.weeks

  # トークン認証に使うHTTPヘッダーのキー名を定義する設定
  config.headers_names = {
    :'authorization' => 'Authorization',
    :'access-token' => 'access-token',
    :'client' => 'client',
    :'expiry' => 'expiry',
    :'uid' => 'uid',
    :'token-type' => 'token-type'
  }
end

フロントエンド側の実装

src/apis/apiClient.js
import axios from "axios";

const apiClient = axios.create({
  baseURL: "http://localhost:3000/api/v1",
});

export default apiClient;

上記では、baseURLをセットしたaxiosインスタンスを作成して、以下のコンポーネント内で axios を使用する際のURLを簡略化しています。
Axios インスタンス

import Modal from "react-modal";
import XLogo from "./XLogo";
import ModalCloseButton from "./ModalCloseButton";
import TextInput from "./TextInput";
import { useState } from "react";
import apiClient from "../../apis/apiClient";
import { useNavigate } from "react-router-dom";

const LogInModal = ({ isOpen, onClose }) => {
  const initialLogInData = {
    email: "",
    password: "",
  };

  const [logInData, setLogInData] = useState(initialLogInData);
  const navigate = useNavigate();

  const handleChange = (e) => {
    setLogInData({ ...logInData, [e.target.name]: e.target.value });
  };

  const handleSubmit = async (e) => {
    e.preventDefault();

    try {
      const res = await apiClient.post("/users/sign_in", logInData);
      localStorage.setItem("access-token", res.headers["access-token"]);
      localStorage.setItem("client", res.headers["client"]);
      localStorage.setItem("uid", res.headers["uid"]);
      navigate("/home");
    } catch (e) {
      console.error(e);
    }
  };

  return (
    <Modal
      isOpen={isOpen}
      className="relative mx-auto mt-16 flex h-163 w-150 flex-col items-center rounded-2xl bg-black px-20 pt-2"
      overlayClassName="fixed inset-0 bg-sky-200/20"
    >
      <ModalCloseButton onClick={onClose} />
      <XLogo size={31} />
      <div className="flex h-full w-full flex-col">
        <h1 className="py-7 text-3xl font-semibold">Xにログイン</h1>
        <form onSubmit={handleSubmit} className="flex flex-1 flex-col">
          <div className="flex flex-col gap-7">
            <div>
              <TextInput
                type="email"
                name="email"
                placeholder="メールアドレス"
                value={logInData.email}
                onChange={handleChange}
              />
            </div>
            <div>
              <TextInput
                type="password"
                name="password"
                placeholder="パスワード"
                value={logInData.password}
                onChange={handleChange}
              />
            </div>
          </div>
          <button
            type="submit"
            className="my-6 mt-auto w-full cursor-pointer rounded-full border bg-white py-3.5 font-bold text-black transition-colors duration-200 hover:bg-gray-200"
          >
            ログイン
          </button>
          <div className="mb-6 text-gray-500">
            アカウントをお持ちでない場合は
            <span className="text-blue-400">登録</span>
          </div>
        </form>
      </div>
    </Modal>
  );
};

export default LogInModal;

上記の処理では、フォームから入力された値を state に保持し、axios を使用して POST メソッドを実行することで、email などのパラメーターをバックエンド(Rails)側に送信しています。サーバー(Rails)がログインに成功すると、レスポンスヘッダーに認証トークン情報を含めて返します。そのトークン類をログアウト機能などの今後の API 通信に利用するため、ローカルストレージに保存しています。

おわりに

最後まで読んでいただきありがとうございました。

参考にしたサイト

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?