LoginSignup
13
6

More than 3 years have passed since last update.

Next.js、Rails とFirebase AuthenticationでJWT認証を実装する

Last updated at Posted at 2021-04-12

はじめに

JWT認証に関して、Next.js Rails Firebase Authenticationという組み合わせでの実装をした記事があまりないとのことなので、今回は実際にやっていこうと思う。
ちなみに、今回は
https://simple-minds-think-alike.hatenablog.com/entry/rails-firebase-authentication
を参考にしながらやっていく。

実際のファイル構成等は下記から閲覧していただけると幸いです。
(まだアプリ自体は作成中なので悪しからず・・・)
https://github.com/yumaasato/m-api
https://github.com/yumaasato/m-frontend

環境

Ruby: 2.6.5
Rails 6.0.3.6
firebase-auth-rails
Redis

*ちなみに今回、user認証でよく用いられるdeviseは必須ではありません。
今回は使わずに実装を進めていきます。

1. Redisをインストールする

mac版の場合は、

$ brew install redis-server

Linuxでは、

$ sudo apt install redis-server

ですかね。
内部でfirebase_id_tokenを使っているためこのRedisをインストールする必要があるとのことです。

2. firebase-auth-railsの追加

今回、firebase-auth-railsというgemを利用することでjwt認証を比較的簡単に行うことができます。

Gemfile
gem 'firebase-auth-rails'

を追加します。

そして、

$ bundle install

を実行します。

3-1. 実装(プロジェクトの設定)

まずはじめに、Reidsとfirebaseプロジェクトの設定をおこないます。

config/initializers/firebase_id_token.rb
FirebaseIdToken.configure do |config|
  config.redis = Redis.new
  config.project_ids = [ENV['FIREBASE_PROJECT_ID']]
end

3-2. uidを追加

firebaseのUIDを保存するカラムを追加します。

$ rails g migration AddUidToUsers uid:string

その後、

$ rails db:migrate

を実行する。

3-3. Application Controller
api/v1側にApplication Controllerを

app/controllers/api/v1/application_controller.rb
module Api
  class V1::ApplicationController < ActionController::API
    include Firebase::Auth::Authenticable
    before_action :authenticate_user
  end
end

としており、元のApplication Controllerについては触れられていないが、
私は下記のようにした
```rails:app/controllers/application_controller.rb

 class ApplicationController < ActionController::Base

 end

つまり、application_controller.rbを2つ書いたというわけだが、これで正しいのかは正直怪しい。
しかし、しっかり実装できているため、一つの参考にはなるだろう。

3-3. ユーザー登録用のコントローラー実装

次に、ユーザー登録用のコントローラー実装する。正直名前は何でもよいが、今回はusers_controller.rbとする。

app/controllers/pi/v1/auth/users_controller.rb
require_dependency 'api/v1/application_controller'
module Api
  module V1
    module Auth
      class UsersController < V1::ApplicationController
        skip_before_action :authenticate_user

        def create
          FirebaseIdToken::Certificates.request
          raise ArgumentError, 'BadRequest Parameter' if payload.blank?
          @user = User.find_or_initialize_by(uid: payload['sub']) do |user|
            user.name = payload['name']
          end
          if @user.save
            render json: @user, status: :ok
          else
            render json: @user.errors, status: :unprocessable_entity
          end
        end

        private

        def token
          params[:token] || token_from_request_headers
        end

        def payload
          @payload ||= FirebaseIdToken::Signature.verify token
        end
      end
    end
  end
end

今回は、Userのnameにnull:falseと設定しているため、

 @user = User.find_or_initialize_by(uid: payload['sub']) do |user|
            user.name = payload['name']
          end

という記述になっている。
そうでない場合は、参考記事通りに
@user = User.find_or_initialize_by(uid: payload['sub'])
でもよいかと思われる。

3-4. Routeの追加

私は、下記の通りに実装した。

app/config/routes.rb
Rails.application.routes.draw do
  namespace 'api' do
    namespace 'v1' do
      resources :players
      resources :games, only: %i(index)
        namespace 'auth' do
          post 'users' => 'users#create'
        end
    end
  end
end

4-1. 実装後のオペレーション(重要)

実装後、$ rails cとして、firebase_id_tokenのDownloading Certificates以下に記載してある内容を実行する必要がある。

irb(main):001:0>FirebaseIdToken::Certificates.request
・
・
・
irb(main):002:0>FirebaseIdToken::Certificates.present?
=> true

となってはじめて、jwt認証を使うことができる。

5. Next側の実装(補足)

次にNext.js側の実装を行う。フロント側の実装方法が省かれている記事が多いが、ここではNext側についても実装方法を記述する。
詳しい記述方法は https://github.com/yumaasato/m-frontend 
を参照していただきたい。
あくまで参考だが、pages/login/index.tsx

export default function LoginPage() => {
  const [user, loading] = useAuthState(getAuth());
  const router = useRouter();
  ・
  ・

  // google認証
  const signInGoogle = async () => {
    await auth.signInWithPopup(provider).catch((err) => alert(err.message));
    router.push('/')
  };

  const handleGoogleLogin = () => {
    const request = async () => {
      const user = await signInGoogle();
      const auth = getAuth();
      const currentUser = auth.currentUser;
      if (auth && auth.currentUser) {
        const token = await currentUser.getIdToken(true);
        const config = { token };
        try {
          await axios.post('/api/v1/auth/users', config);
        } catch (error) {
          console.log(error);
        }
      }
    };
    request();
  };
}

return (
    <div className='w-2/3 py-20 mx-auto'>
      <p className='font-bold text-4xl py-5 text-center'>Login</p>
      <Button
        fullWidth
        variant="contained"
        color="default"
        className={classes.submit}
        startIcon={<CameraIcon />}
        onClick={handleGoogleLogin}
      >
        SignIn with Google
            </Button>
    </div>
  );

とすればいいのではないか。
これは、signInGoogleでfirebase AuthでのGoogle認証を行い、
handleGoogleLoginでRails側にAPIのデータを送るというものである。
ちなみに、ここでは
axios.defaults.baseURL ='http://localhost:3000'
としている。

これで一通りの実装は終えたことになる。

補足

Railsにおいて、.envファイルで環境変数を管理する際には、

gem 'dotenv-rails'

をインストールする必要があるため、忘れずに設定しておこう。
(これで認証に時間がかかったので・・・)

おわりに

エンジニアの方からすれば、この認証は大したことはないのかもしれないが、記事を参考にしながら実装を進めている初学者からすれば、なかなか骨の折れる作業である。事実、私もこの実装だけでかなりの時間を費やしてしまった。

私と同じようにNext.js、Rails とFirebase AuthenticationでのJWT認証で困っている人たちの助けになれば幸いである。また、どこか誤りがありましたら、伝えてもらえると助かります。最後まで閲覧していただきまして、誠にありがとうございました。

13
6
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
13
6