Torihaji's Growth Diary

Little by little, no hurry.

Rails APIモード, jwt tokenの認証について

はじめに

よくあるjwt 認証についてそこらたりをまとめました

前提

Railsのjwt等を使用します。

主な流れとしては以下です

login, signupが成功すると同時にRails側でCookieaccess_tokenをセット。

frontend側からはcookieに格納しておき、api通信時にcookieから取り出し

リクエストヘッダのAuthorizationヘッダにBearerトークンとして添付し送る

backend側はそのBearerトークンを読み取り、decodeして

中身を読み取り、中に入っているuserIdを取得してUserを取得できたら認証完了

コード

今回はフロントから想定通りの出力が渡ってくる前提で行います。

まずapplication_controller.rbにおいてauthenticate_userを実装

成功時、インスタンス変数として@current_userを格納

access_tokenの正常チェックはjwt gemのdecodeが行う。

  def authenticate_user!
    token = extract_token
    return render_error("認証に失敗しました", :unauthorized) unless token

    payload = Infrastructure::JwtService.decode(token)[0]
    @current_user = User.find(payload['user_id'])
  end

  def extract_token
    request.headers['Authorization']&.split(' ')&.last
  end
module Infrastructure
  class JwtService
    class TokenExpiredError < StandardError; end
    class InvalidTokenError < StandardError; end

    def self.encode(payload)
      payload[:exp] = 1.hours.from_now.to_i
      JWT.encode(payload, Rails.application.credentials.secret_key_base, 'HS256')
    end

    def self.decode(token)
      JWT.decode(token, Rails.application.credentials.secret_key_base, true, { algorithm: 'HS256' })
    rescue JWT::ExpiredSignature
      raise TokenExpiredError.new("トークンの有効期限が切れています")
    rescue JWT::DecodeError
      raise InvalidTokenError.new("トークンが無効です")
    end
  end
end

簡易的ですが、こんな感じです。

例外補足は全てapplication_contrllerのrescue_fromで行います。

終わりに

環境ごとにcredentialsを使っていたのですが、一度developmentとか作ってしまうとdefaultのcredentialsを読む術って無くなるんすね。

読み出す優先順序としては環境別のものが優先されるとは聞いてましたが、環境別のものを作ると勝手にmergeしてくれたりしないんすね。

それで朝詰まったので良い勉強となりました。