RailsのAPIでJWTを使って単一のuserの認証システムを作っていましたが、途中でuser -> workerとなり、新たにrecruiterというのが出てくることになりました。
JWTの認証にはKnockというgemを使っていましたが、単一のuserに対して認証することが前提に作られていたので、少しカスタマイズします。
modelの変更点
新たに出てきたrecruiterに関しても公式の手順に従ってtoken controllerなどを作成しておきます。
公式ページにも載っているように、カスタマイズするにはこの2つのmethodをいじればいいです。
まずKnockが生成するJWTの形を変更します。
defaultではsubというkeyにidが入っていますが、さらにclassを追加してあげることで、JWTを持っているのがworkerなのかrecruiterなのかという判別ができるようになります。
class User < ActiveRecord::Base
def to_token_payload
# Returns the payload as a hash
{ sub: id, class: self.class.to_s }
end
end
ここではworker or recruiterを探す時にJWTにclassが入っているかを確認するように変更しています。
class User < ActiveRecord::Base
def self.from_token_payload payload
# Returns a valid user, `nil` or raise
# e.g.
# defaultではこうなっている
# self.find payload["sub"]
find payload['sub'] if payload['class'] && payload['class'] == to_s
end
end
認証メソッドの変更点
defaultのKnockではworkerに対して、認証するにはauthenticate_worker
を使えば認証が動くようになっていました。
ここではrecruiterも追加されたので、2つを認証する方法について解説します。
実装は以下のようになります。
authenticate_user
というメソッドの内部でJWTの中身のclassに応じて条件分岐して、authenticate_xxx
メソッドを実行するようにします。
class ApplicationController < ActionController::API
include Knock::Authenticable
before_action :authenticate_user
def authenticate_user
class_name = Knock::AuthToken.new(token: token).payload['class']
return unless class_name
case class_name.constantize
when Worker
authenticate_worker
when Recruiter
authenticate_recruiter
end
end
もし、あるcontrollerだけはrecruiterからは操作できないようにしたい時があれば、以下のようにします。
class XxxController < ActionController::API
skip_before_action :authenticate_user
before_action :authenticate_worker
end