title: How can I have a Rails API set a JWT in a cookie on a client that is running on another domain?
body: <p>Long time lurker, first time poster here.</p>
  
  <p>There are many good guides and resources about JWTs and how and where to store them. But I'm running into an impasse when it comes to securely storing and sending a JWT between a ReactJS/Flux app running on a Node server and a completely separate Rails API.</p>
  
  <p>It seems most guides tell you to just store the JWT in local storage and pluck it out for every AJAX request you make and pass it along in a header. <a href="https://stormpath.com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage/" rel="noreferrer">https://stormpath.com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage/</a> warns against this, however, since local storage is not secure and a malicious person could access that token. It recommends storing it in the cookie instead and just letting the web browser pass it along with each request.</p>
  
  <p>That sounds fine to me since from what I understand cookies get conveniently sent along with every request anyway. It means I can just make AJAX requests from my ReactJS app to my Rails API and have the API pluck it out, check it, and do it's thing.*</p>
  
  <p>The problem I'm running into is my Node application doesn't set a cookie from the response it gets back from the Rails API even though the Rails API (running on localhost:3000) returns a Set-Cookie header and sends it back to the ReactJS/Node app (running on localhost:8080).</p>
  
  <p>Here's my login controller action on my Rails API side:</p>
  
  <pre><code>class V1::SessionsController &lt; ApplicationController
    def create
      user = User.where(email: params[:user][:email]).first!
      if user &amp;&amp; user.authenticate(params[:user][:password])
        token = issue_new_token_for(user)
  
        # I've tried this too.
        # cookies[:access_token] = {
        #     :value =&gt; token,
        #     :expires =&gt; 3.days.from_now,
        #     :domain =&gt; '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
  </code></pre>
  
  <p>The gist of it is it takes an email and password, looks the user up, and generates JWT if the info checks out.</p>
  
  <p>Here's the AJAX request that is calling it from my Node app:</p>
  
  <pre><code>$.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);
      }
  })
  </code></pre>
  
  <p>Inspecting the response from the Rails API shows it has a Set-Cookie header with a value of access_token=jwt.token.here</p>
  
  <p>Screenshot:
  <a href="http://i.stack.imgur.com/70Tgr.png" rel="noreferrer">Chrome Dev Tools Inspector Screenshot</a></p>
  
  <p>However, localhost:8080 does not show any cookies set and subsequent AJAX calls from my Node/React app do not have any cookies being sent along with them.</p>
  
  <p>My question is, what piece(s) am I misunderstanding. What would I have to do to make storing JWTs in cookies work in this scenario?</p>
  
  <p>A follow-up question: assuming storing the JWT in a cookie is not an option, what potential security risks could there be with storing the JWT in local storage (assuming I don't put any sensitive info in the JWT and they all expire in some arbitrary amount of time)?</p>
  
  <p>*this may be a fundamental misunderstanding I have. Please set me straight if I have this wrong.</p>
  
  <p>Side-notes that may be of interest:</p>
  
  <ul>
  <li>My Rails API has CORS setup to only allow traffic from localhost:8080
  in development.</li>
  <li>In production, the Node/React app will probably be
  running on a main domain (example.com) and the Rails API will be
  running on a sub domain (api.example.com), but I haven't gotten that
  far yet.</li>
  <li>There's nothing sensitive in my JWT, so local storage is an
  option, but I want to know why my setup doesn't work with cookies.</li>
  </ul>
  
  <p><strong>Update</strong> elithrar submitted an answer that worked:</p>
  
  <p>I needed to modify my AJAX request with xhrFields and crossDomain as well as tell jQuery to support cors:</p>
  
  <pre><code>$.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);
      }
  })
  </code></pre>
  
  <p>And I added credentials: true and expose: true to my Rack Cors configuration on my Rails API (the * is only for my development environment):</p>
  
  <pre><code>config.middleware.insert_before 0, 'Rack::Cors' do
  allow do
    origins '*'
    resource '*', :headers =&gt; :any, :methods =&gt; [:get, :post, :put, :path, :options], credentials: true, expose: true
  end
  </code></pre>
  
  <p>end</p>