title: JWT Authentication with Vue causes Fatal Error: JavaScript heap out of memory
body: <p>This is a fairly complicated problem that involves ALOT of pieces, so I posted the <a href="https://github.com/JoshHadik/stack_nuxt_authentication_question" rel="nofollow noreferrer">full source code</a> to GitHub for anyone who wants to help.</p>
  
  <p><strong>The Stack</strong></p>
  
  <p>Ok, so I'm trying to setup authentication with a Nuxt.js (Vue framework) client-side app and a Rails server-side API. Here's my project stack:</p>
  
  <ol>
  <li>Rails API backend</li>
  <li>Devise &amp; devise-jwt ruby gems for serving JWT.</li>
  <li>Postgres Database.</li>
  <li>Nuxt.js front end application.</li>
  <li>@nuxtjs/axios package for connecting Nuxt app with Rails API.</li>
  <li>@nuxtjs/auth package to handle client side authentication logic in Nuxt.</li>
  <li>Nginx proxy to route requests to the proper service (Rails API or Nuxt App)</li>
  <li>Docker and docker-compose to connect all four services together (Rails, Nginx, Nuxt, Postgres)</li>
  </ol>
  
  <p>Like I said, a lot of parts. And to make matters even worse, I don't really even know which service is causing problems, though I do have an idea. I'll come back to that later though.</p>
  
  <p><strong>The Problem</strong></p>
  
  <p>The Vue App works perfectly fine until I attempt to sign in as an existing user. Once I do, something interesting happens.</p>
  
  <p>It appears to work at first, and even redirects to the proper page on sign in. However, as soon as I reload the page or try clicking on any links, the website freezes and the loading bar hangs indefinitely.</p>
  
  <p>After the website tries to load for a bit, I get this error:</p>
  
  <pre><code>FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
  </code></pre>
  
  <p>Along with this error, I get a JS Stack Trace, however, the stack trace isn't always the same. Here are the three I've gotten so far...</p>
  
  <p>JS Stacktrace 1:</p>
  
  <pre><code>view_1   | &lt;--- JS stacktrace ---&gt;
  view_1   | 
  view_1   | ==== JS stack trace =========================================
  view_1   | 
  view_1   |     0: ExitFrame [pc: 0x26fd9624fb5d]
  view_1   | Security context: 0x140a9a59d921 &lt;JSObject&gt;
  view_1   |     1: on [0x1c685bd61259] [_stream_readable.js:~825] [pc=0x26fd96bd1601](this=0x27476ca40f19 &lt;Socket map = 0x2513dd2aaca1&gt;,0x1c685bd160d9 &lt;String[5]: close&gt;,0x27476ca41619 &lt;JSBoundFunction (BoundTargetFunction 0x118981190661)&gt;)
  view_1   |     2: connectionListenerInternal(aka connectionListenerInternal) [0x118981190561] [_http_server.js:~325] [pc=0x26fd9706ea91](this=...
  view_1   | 
  view_1   | FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
  </code></pre>
  
  <p>JS Stacktrace 2:</p>
  
  <pre><code>view_1   | &lt;--- JS stacktrace ---&gt;
  view_1   | 
  view_1   | ==== JS stack trace =========================================
  view_1   | 
  view_1   |     0: ExitFrame [pc: 0x3595a694fb5d]
  view_1   | Security context: 0x016ed8d9d921 &lt;JSObject&gt;
  view_1   |     1: parse(aka parse) [0x34dc366765a9] [/app/node_modules/cookie/index.js:~49] [pc=0x3595a77ee3a0](this=0x297ef7d825b1 &lt;undefined&gt;,0x15d707f39c31 &lt;String[265]: auth._token.local=Bearer%20eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwic2NwIjoidXNlciIsImF1ZCI6bnVsbCwiaWF0IjoxNTQ3MDczMjQyLCJleHAiOjE1NDcwNzY4NDIsImp0aSI6ImUwMWRlNjFkLTRkYzItNDRlMi1iZDY1LWQwYmRmZTcwNGNhZS...
  view_1   | 
  view_1   | FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
  </code></pre>
  
  <p>JS Stacktrace 3:</p>
  
  <pre><code>view_1   | &lt;--- JS stacktrace ---&gt;
  view_1   | 
  view_1   | ==== JS stack trace =========================================
  view_1   | 
  view_1   |     0: ExitFrame [pc: 0x2bda0b2cfb5d]
  view_1   | Security context: 0x1e6ec149d921 &lt;JSObject&gt;
  view_1   |     1: setContext(aka setContext) [0x3ef9c763dc89] [server-bundle.js:~2584] [pc=0x2bda0bbaa2d1](this=0x0a10aa8025b1 &lt;undefined&gt;,0x28d2a207a4c1 &lt;Object map = 0x10d1f0bf3091&gt;,0x28d2a207a5b1 &lt;Object map = 0x10d1f0bf2cd1&gt;)
  view_1   |     2: createApp(aka createApp) [0x27c44ed85199] [server-bundle.js:~1745] [pc=0x2bda0bb9d782](this=0x0a10aa8025b1 &lt;undefined&gt;,0x2bee19762ce9 ...
  view_1   | 
  view_1   | FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
  </code></pre>
  
  <p><strong>My Theory</strong></p>
  
  <p>I think the problem has something to do with how @nuxtjs/axios or @nuxtjs/auth works with an Nginx proxy.</p>
  
  <p>The reason I believe this is because when I use the exact same setup for Vue and Rails, but connect the two together directly via axios without an Nginx proxy in between them, everything works perfectly. </p>
  
  <p>Which means I know for a fact that the backend authentication and JWT logic works and returns a token as expected, and I know the front end works when connected directly with the backend. This leads me to think that, like I said above, the problem stems from how I have setup The Vue App to communicate with the Nginx Proxy.</p>
  
  <p>Even more specifically, I think the problem might have to do with how @nuxtjs/axios or @nuxtjs/auth works with an Nginx proxy when using cookies.</p>
  
  <p>I think this might be the case because the app only hangs when the JWT cookies and session storage is set. If I delete the cookies in the safari browser and refresh the page, The Vue Website works fine again.</p>
  
  <p>Assuming that my theory is right, here is the relevant code:</p>
  
  <p><strong>Nginx Configuration:</strong></p>
  
  <pre><code>upstream view {
    server view:3000;
  }
  
  upstream api {
    server api:8080;
  }
  
  server {
    listen 80;
  
    location / {
      proxy_pass http://view;
    }
  
    # FOR DEVELOPMENT ONLY
    location /sockjs-node {
      proxy_pass http://view;
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "Upgrade";
    }
  
    location /api {
      rewrite /api/(.*) /$1 break;
      proxy_pass http://api;
    }
  }
  </code></pre>
  
  <p><strong>Nuxt Auth &amp; Axios Config:</strong></p>
  
  <pre><code>axios: {
    proxy: true,
    prefix: "/api"
  },
  auth: {
    strategies: {
      local: {
        endpoints: {
          login: { url: '/auth/users/sign_in' },
          logout: { url: '/auth/users/sign_out', method: 'delete' },
          user: { url: '/auth/users/current' }
        }
      }
    }
  },
  </code></pre>
  
  <p><strong>Rails Routes:</strong></p>
  
  <pre class="lang-rb prettyprint-override"><code>Rails.application.routes.draw do
    resources :atoms
  
    scope '/auth', defaults: {format: :json} do
      devise_for :users, controllers: {sessions: 'sessions'}
      devise_scope :user do
        get 'users/current', to: 'sessions#show'
      end
    end
  end
  </code></pre>
  
  <p><strong>Rails SessionController:</strong></p>
  
  <pre class="lang-rb prettyprint-override"><code>class SessionsController &lt; Devise::SessionsController
    def create
      super { @token = current_token }
    end
  
    def show
    end
  
    private
  
    def current_token
      request.env['warden-jwt_auth.token'] if current_user
    end
  end
  </code></pre>
  
  <p><strong>Rails Session Views:</strong></p>
  
  <pre class="lang-rb prettyprint-override"><code># app/views/devise/sessions/create.json.jbuilder
  json.token @token
  
  # app/views/devise/sessions/show.json.jbuilder
  if user_signed_in?
    json.user do
      json.(current_user, :id, :email)
    end
  end
  </code></pre>
  
  <p><strong>Nuxt Login Page (uses Vuetify):</strong></p>
  
  <pre><code>&lt;template&gt;
    &lt;v-layout&gt;
      &lt;v-flex&gt;
        &lt;v-card v-if="$auth.loggedIn"&gt;
          &lt;v-alert type="error" :value="error"&gt;{{error}}&lt;/v-alert&gt;
          &lt;v-card-text&gt;
            Logged in as {{$auth.user.email}}
          &lt;/v-card-text&gt;
          &lt;v-card-actions&gt;
            &lt;v-btn @click="logout"&gt;Log out&lt;/v-btn&gt;
          &lt;/v-card-actions&gt;
        &lt;/v-card&gt;
        &lt;v-card v-else&gt;
          &lt;v-alert type="error" :value="error"&gt;{{error}}&lt;/v-alert&gt;
          &lt;v-card-text&gt;
            &lt;v-form&gt;
              &lt;v-text-field v-model="email" label="Email" /&gt;
              &lt;v-text-field v-model="password" label="Password" type="password" /&gt;
            &lt;/v-form&gt;
            &lt;v-card-actions&gt;
              &lt;v-btn @click="login"&gt;Log in&lt;/v-btn&gt;
            &lt;/v-card-actions&gt;
          &lt;/v-card-text&gt;
        &lt;/v-card&gt;
      &lt;/v-flex&gt;
    &lt;/v-layout&gt;
  &lt;/template&gt;
  
  &lt;script&gt;
    export default {
      data () {
        return {
          email: '',
          password: '',
          error: null
        }
      },
      methods: {
        login () {
          this.$auth.login({
            data: {
              user: {
                email: this.email,
                password: this.password
              }
            }
          }).catch(e =&gt; {this.error = e + ''})
        },
        logout () {
          this.$auth.logout().catch(e =&gt; {this.error = e + ''})
        }
      }
    }
  
  &lt;/script&gt;
  </code></pre>
  
  <p><strong>Docker Compose:</strong></p>
  
  <pre><code>version: '3'
  services:
    db:
      image: postgres
      volumes:
        - ./.tmp/db:/var/lib/postgresql/data
      ports:
        - "5432"
    proxy:
      build: proxy
      restart: always
      ports:
        - '5000:80'
    api:
      build: api
      environment:
        - JWT_SECRET=${JWT_SECRET}
      volumes:
        - ./api:/app
      depends_on:
        - db
    view:
      build: view
      volumes:
        - /app/node_modules
        - ./view:/app
  </code></pre>
  
  <p><strong>View The Code</strong></p>
  
  <p>Since this is a super complex project and has a lot of pieces, I expect it will be pretty hard for anyone to help with just the given code. For that reason, I setup a GitHub repository with all of the code in it's current state. You can view it <a href="https://github.com/JoshHadik/stack_nuxt_authentication_question" rel="nofollow noreferrer">here</a>.</p>
  
  <p>To run the repository, simply clone the project</p>
  
  <pre><code>$ git clone https://github.com/JoshHadik/stack_nuxt_authentication_question.git
  </code></pre>
  
  <p>Move into the directory </p>
  
  <pre><code>$ cd stack_nuxt_authentication_question/ 
  </code></pre>
  
  <p>And run docker-compose up --build (assuming you have docker and docker-compose on your computer.</p>
  
  <pre><code>$ docker-compose up --build
  </code></pre>
  
  <p>Setup database in new terminal tab</p>
  
  <pre><code>$ docker-compose run api rails db:create db:migrate db:seed
  </code></pre>
  
  <p>You should then be able to access The Vue app @ localhost:5000. Additionally, the database should contain a user with the following credentials:
  email: test@example.com 
  password: password</p>
  
  <p>The login route is at localhost:5000/login</p>
  
  <p><em>No Proxy</em></p>
  
  <p>I mentioned above that the app works when the API and view are connected directly without a proxy in between. to showcase how that works I created an additional branch in the project called no-proxy. If you want to check it out just open a new directory (not the one you previously cloned to) and run </p>
  
  <pre><code>$ git clone -b no-proxy https://github.com/JoshHadik/stack_nuxt_authentication_question.git
  </code></pre>
  
  <p>move into the directory</p>
  
  <pre><code>$ cd stack_nuxt_authentication_question/
  </code></pre>
  
  <p>then run docker-compose again</p>
  
  <pre><code>$ docker-compose up --build
  </code></pre>
  
  <p>and setup the database in a new terminal tab:</p>
  
  <pre><code>$ docker-compose run api rails db:create db:migrate db:seed
  </code></pre>
  
  <p>You can view the Nuxt app for the no-proxy branch @ localhost:3000 and the login route @ localhost:3000/login. You should be able to sign in with the same credentials as above.</p>
  
  <p><strong>Thanks</strong></p>
  
  <p>Thanks in advance to anyone and everyone who tries to help me out with this!</p>