#はじめに
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認証を比較的簡単に行うことができます。
gem 'firebase-auth-rails'
を追加します。
そして、
$ bundle install
を実行します。
##3-1. 実装(プロジェクトの設定)
まずはじめに、Reidsとfirebaseプロジェクトの設定をおこないます。
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を
module Api
class V1::ApplicationController < ActionController::API
include Firebase::Auth::Authenticable
before_action :authenticate_user
end
end
としており、元のApplication Controllerについては触れられていないが、
私は下記のようにした
class ApplicationController < ActionController::Base
end
つまり、```application_controller.rb```を2つ書いたというわけだが、これで正しいのかは正直怪しい。
しかし、しっかり実装できているため、一つの参考にはなるだろう。
##3-3. ユーザー登録用のコントローラー実装
次に、ユーザー登録用のコントローラー実装する。正直名前は何でもよいが、今回は```users_controller.rb```とする。
```rails: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の追加
私は、下記の通りに実装した。
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認証で困っている人たちの助けになれば幸いである。また、どこか誤りがありましたら、伝えてもらえると助かります。最後まで閲覧していただきまして、誠にありがとうございました。