I have a very typical Rails backend with Devise and Devise-JWT. When I make a raw cURL request to the endpoint /users/sign_in
I can see in the headers that it is setting an Authorization header with a token.
When I do the same request on my React frontend (which is on a different port, so cross origin configuration is necessary) the only headers I'm seeing in the result are cache-control and content-type.
Now, I have installed the 'cors' gem. I have created an initializer called config/initializers/cors.rb
and I have put this configuration inside:
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins '*'
resource('*',
headers: :any,
expose: ["Authorization"],
methods: :any
)
end
end
Yet when I make the request in my React frontend, I see no Authorization header.
I can see that Rails is responding with a 302 redirect. Is this part of the problem? Do I need to configure devise to stop responding with a 302?
I am utterly lost as to what the problem could be.
If you observe the screenshot that follows, logging in the normal way via plain form login gives me an Authorization header, but if I do it via AJAX there is no header.
This is the frontend (AJAX) code which doesn't give me any Authorization header, only "cache-control" and "content-type":
async function submitLogin(email, password) {
let formData = new FormData();
formData.append("user[email]", email)
formData.append("user[password]", password)
let result = await Axios.post(`${process.env.API_URL}/users/sign_in`, formData, {maxRedirects: 0})
console.log(result)
}
the result:
DISCLAMER: This is not a 100% answer to your question, but it will work for sure!
In the case devise-jwt
is not mandatory in your project, I would suggest you to follow what I did in my Rails 6 / React template :
SessionController
in order to make it responding with the JSON format and returning a JWT:# frozen_string_literal: true
class SessionsController < Devise::SessionsController
clear_respond_to
respond_to :json
def create
super do |resource|
if user_signed_in?
resource.token = JwtWrapper.encode(user_id: current_user.id)
end
end
end
end
JwtWrapper
in app/helpers/jwt_wrapper.rb
:# frozen_string_literal: true
module JWTWrapper
extend self
def encode(payload, expiration = nil)
expiration ||= Rails.application.secrets.jwt_expiration_hours
payload = payload.dup
payload['exp'] = expiration.to_i.hours.from_now.to_i
JWT.encode payload, Rails.application.secrets.jwt_secret
end
def decode(token)
decoded_token = JWT.decode token, Rails.application.secrets.jwt_secret
decoded_token.first
rescue StandardError
nil
end
end
Now you'll get your valid JWT in the response body.
I suggest you to have a look at the restful-json-api-client package which can handle JWT for you as it look for the token
attribute in the responses and store and pass it to your requests.
It also handle JWT renewal automatically! So when you API replies with a 401 error, if you configure the renewal path, it will tries to renew the JWT. If it succeeded, it will redo the query with the new JWT and it's transparent for your app, otherwise if it fails to renew it, it returns the failure to your app so that you can do something with it like redirecting your user to a login page.
Hopefully this time I'm answering a little bit more to your question. 😇
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.