lib/rodauth/features/jwt.rb
# frozen-string-literal: true require 'jwt' module Rodauth Feature.define(:jwt, :Jwt) do depends :json translatable_method :invalid_jwt_format_error_message, "invalid JWT format or claim in Authorization header" auth_value_method :jwt_algorithm, "HS256" auth_value_method :jwt_authorization_ignore, /\A(?:Basic|Digest) / auth_value_method :jwt_authorization_remove, /\ABearer:?\s+/ auth_value_method :jwt_decode_opts, {}.freeze auth_value_method :jwt_session_key, nil auth_value_method :jwt_symbolize_deeply?, false auth_value_methods( :jwt_secret, :use_jwt? ) auth_methods( :jwt_session_hash, :jwt_token, :session_jwt, :set_jwt_token ) def_deprecated_alias :json_check_accept?, :jwt_check_accept? def session return @session if defined?(@session) return super unless use_jwt? s = {} if jwt_token unless session_data = jwt_payload json_response[json_response_error_key] ||= invalid_jwt_format_error_message _return_json_response end if jwt_session_key session_data = session_data[jwt_session_key] end if session_data if jwt_symbolize_deeply? s = JSON.parse(JSON.fast_generate(session_data), :symbolize_names=>true) elsif scope.opts[:sessions_convert_symbols] s = session_data else session_data.each{|k,v| s[k.to_sym] = v} end end end @session = s end def clear_session super if use_jwt? session.clear set_jwt end end def jwt_secret raise ArgumentError, "jwt_secret not set" end def jwt_session_hash jwt_session_key ? {jwt_session_key=>session} : session end def session_jwt JWT.encode(jwt_session_hash, jwt_secret, jwt_algorithm) end def jwt_token return @jwt_token if defined?(@jwt_token) if (v = request.env['HTTP_AUTHORIZATION']) && v !~ jwt_authorization_ignore @jwt_token = v.sub(jwt_authorization_remove, '') end end def set_jwt_token(token) response.headers['Authorization'] = token end def use_jwt? use_json? end def use_json? jwt_token || super end def valid_jwt? !!(jwt_token && jwt_payload) end private def check_csrf? return false if use_jwt? super end def _jwt_decode_opts jwt_decode_opts end def jwt_payload return @jwt_payload if defined?(@jwt_payload) @jwt_payload = JWT.decode(jwt_token, jwt_secret, true, _jwt_decode_opts.merge(:algorithm=>jwt_algorithm))[0] rescue JWT::DecodeError => e rescue_jwt_payload(e) end def rescue_jwt_payload(_) @jwt_payload = false end def set_session_value(key, value) super set_jwt if use_jwt? value end def remove_session_value(key) value = super set_jwt if use_jwt? value end def return_json_response set_jwt super end def set_jwt set_jwt_token(session_jwt) end end end