简体   繁体   中英

Devise-JWT: Using cURL I can see an Authorization header. Using Axios/Fetch there is no Authorization header

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.

此图显示使用非 AJAX 请求正常访问 /users/sign_in 端点时有一个 Authorization 标头

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 :

  1. Configure you project routes in order to override the Devise's sessions routes like I did in my template project
  2. Override the Devise's 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
  1. Add the 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.

Bonus

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM