How can I configure the Rails API for JWT in a cookie on a client that runs in a different domain?

Long time lurker, first poster here.

There are many good guides and resources about the JWT and how and where to store them. But I'm at a dead end when it comes to safely storing and sending JWT between a ReactJS / Flux application running on a Node server and a completely separate Rails API.

Most manuals seem to tell you to just store the JWT in local storage and pull it out for every AJAX request you make, and pass it in the header. https://stormpath.com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage/ warns about this, since local storage is unsafe and a malicious person can gain access to this token. He recommends that you save it in a cookie and simply allow the web browser to transmit it along with each request.

This sounds great to me, because from what I understand, cookies are conveniently sent along with every request. This means that I can just make AJAX requests from my ReactJS application into my Rails API and get the API, pull it out, check it out and do it. *

The problem I am facing is that the Node application does not set the cookie from the response it returns from the Rails API, even if the Rails API (runs on localhost: 3000) returns the Set-Cookie header and sends it back to the ReactJS / Node application ( runs on localhost: 8080).

Here is my login controller action on my side of the Rails API:

class V1::SessionsController < ApplicationController def create user = User.where(email: params[:user][:email]).first! if user && user.authenticate(params[:user][:password]) token = issue_new_token_for(user) # I've tried this too. # cookies[:access_token] = { # :value => token, # :expires => 3.days.from_now, # :domain => 'https://localhost:8080' # } response.headers['Set-Cookie'] = "access_token=#{token}" render json: { user: { id: user.id, email: user.email }, token: token }, status: 200 else render json: { errors: 'username or password did not match' }, status: 422 end end end 

Its essence is that it takes an email address and password, scans the user and generates a JWT if the information is verified.

Here's the AJAX request calling it from my Node application:

 $.ajax({ url: 'http://localhost:3000/v1/login', method: 'post', dataType: 'json', data: { user: { email: data.email, password: data.password }, callback: '' //required to get around ajax CORS }, success: function(response){ console.log(response); }, error: function(response) { console.log(response); } }) 

Checking the response from the Rails API shows that it has a Set-Cookie header with access_token = jwt.token.here

Screenshot: Screenshot of Chrome Dev Tools browser

However, localhost: 8080 does not display cookies, and subsequent AJAX calls from my Node / React application do not contain any cookies with them.

My question is which things I misunderstand. What do I need to do to store JWT in cookies in this scenario?

Next question: assuming storing the JWT in a cookie is not an option what potential security risks might be when storing the JWT in local storage (provided that I do not put any confidential information in the JWT and they all expire after some time) ?

* This may be a fundamental misunderstanding that I have. Please put me straight if I'm wrong.

Side notes that may be of interest:

  • My Rails API has a CORS setting that allows traffic with only localhost: 8080 in development.
  • During production, the Node / React application is likely to run on the main domain (example.com), and the Rails API will be running on the subdomain (api.example.com), but I have not got this far.
  • There is nothing sensitive in my JWT, so there is local storage, but I want to know why my installation does not work with cookies.

The elithrar update sent a response that worked:

I needed to modify my AJAX request using xhrFields and crossDomain, and also tell jQuery to support cors:

 $.support.cors = true; $.ajax({ url: 'http://localhost:3000/v1/login', method: 'post', dataType: 'json', xhrFields: { withCredentials: true }, crossDomain: true, data: { user: { email: data.email, password: data.password } }, success: function(response){ console.log(response); }, error: function(response) { console.log(response); } }) 

And I added the credentials: true and expose: true for my Rack Cors configuration in my Rails API (* only for my development environment):

 config.middleware.insert_before 0, 'Rack::Cors' do allow do origins '*' resource '*', :headers => :any, :methods => [:get, :post, :put, :path, :options], credentials: true, expose: true end 

end

+5
source share

Source: https://habr.com/ru/post/1234070/


All Articles