5

Hi I found an old question similar to mine with no answer on StackOverFlow : nuxtjs/auth axios not sending cookie

Also here on GitHub, without a valid solution: https://github.com/nuxt-community/auth-module/issues/478

So the problem is that if I call $auth.loggedIn in any page, it works like a charm but if I do it in my custom authentication middleware (or if I use the default auth middleware), it always return false.

my auth configuration in nuxt.config.js

auth: {
    strategies: {
      local: {
        endpoints: {
          login: {
            url: '/rest-auth/login/',
            method: 'post',
            propertyName: 'key',
          },
          logout: { url: '/rest-auth/logout/', method: 'post' },
          user: {
            url: '/rest-auth/user/',
            method: 'get',
            propertyName: false,
          },
        },
        tokenType: 'Token',
        tokenName: 'Authorization',
      },
      redirect: {
        login: '/user_dashboard',
        home: '/',
      },
    },
  },

my custom auth middleware

export default async function ({ $auth, redirect }) {
  const user = await $auth.loggedIn
  console.log(user) // <-- this always return false for some reason :(
  if (user) {
    // let the user see the page
  } else {
    // redirect to homepage
    redirect('/')
  }
}

EDIT:

As requested, this is my package.json:

{
  "name": "<MY_APP_NAME>",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "dev": "nuxt",
    "build": "nuxt build",
    "start": "nuxt start",
    "generate": "nuxt generate",
    "lint:js": "eslint --ext .js,.vue --ignore-path .gitignore .",
    "lint:style": "stylelint **/*.{vue,css} --ignore-path .gitignore",
    "lint": "npm run lint:js && npm run lint:style",
    "test": "jest"
  },
  "lint-staged": {
    "*.{js,vue}": "eslint",
    "*.{css,vue}": "stylelint"
  },
  "husky": {
    "hooks": {
      "commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
      "pre-commit": "lint-staged"
    }
  },
  "dependencies": {
    "@nuxt/content": "^1.9.0",
    "@nuxtjs/auth": "^4.9.1",
    "@nuxtjs/axios": "^5.12.2",
    "@nuxtjs/pwa": "^3.0.2",
    "cookie-universal-nuxt": "^2.1.4",
    "core-js": "^3.6.5",
    "nuxt": "^2.14.7",
    "nuxt-buefy": "^0.4.3"
  },
  "devDependencies": {
    "@commitlint/cli": "^11.0.0",
    "@commitlint/config-conventional": "^11.0.0",
    "@nuxtjs/eslint-config": "^3.1.0",
    "@nuxtjs/eslint-module": "^3.0.0",
    "@nuxtjs/style-resources": "^1.0.0",
    "@nuxtjs/stylelint-module": "^4.0.0",
    "@vue/test-utils": "^1.1.0",
    "babel-core": "7.0.0-bridge.0",
    "babel-eslint": "^10.1.0",
    "babel-jest": "^26.5.0",
    "eslint": "^7.10.0",
    "eslint-config-prettier": "^6.12.0",
    "eslint-plugin-nuxt": "^1.0.0",
    "eslint-plugin-prettier": "^3.1.4",
    "husky": "^4.3.0",
    "jest": "^26.5.0",
    "lint-staged": "^10.4.0",
    "node-sass": "^4.14.1",
    "prettier": "^2.1.2",
    "sass-loader": "^10.0.3",
    "stylelint": "^13.7.2",
    "stylelint-config-prettier": "^8.0.2",
    "stylelint-config-standard": "^20.0.0",
    "vue-jest": "^3.0.4"
  }
}

Fabio Magarelli
  • 1,031
  • 4
  • 14
  • 47

4 Answers4

10

Unfortunately I wasn't able to make nuxtjs/auth work in the middleware but I was able to solve the issue by using cookie-universal-nuxt in combination with nuxtjs/auth:

You can leave your axios version as it is, no need to downgrade for this solution

  1. npm install --save cookie-universal-nuxt
  2. add cookie-universal-nuxt in your nuxt.config.js file:
modules: [
    // other modules ...
    '@nuxtjs/auth',
    'cookie-universal-nuxt',
  ],
  1. create a custom auth middleware. I called mine auth-user in the middleware folder:
export default async function ({ app, redirect }) {
  // the following look directly for the cookie created by nuxtjs/auth
  // instead of using $auth.loggedIn
  const user = await app.$cookies.get('auth._token.local')
  if (user) {
    // let the user see the page
  } else {
    // redirect to homepage
    redirect('/')
  }
}
  1. then declare your middleware in the root page of your application that you want to be accessible only by authenticated users:
<script>
export default {
  middleware: ['auth-user'],
}
</script>

If this doesn't work, check the cookie name where your user credential are saved by opening the developer tools / inspector in the browser.

Fabio Magarelli
  • 1,031
  • 4
  • 14
  • 47
5

had the same problem, and I solved it now with a lot of trial & error.

The server side had no knowledge of the loggedIn state, because they did not implement it(?).

So I created my own middleware that sets the state for the server side.

Create: middleware/auth-ssr.ts

import { Context, Middleware } from '@nuxt/types';
import { parse as parseCookie } from 'cookie';
import jsonwebtoken from 'jsonwebtoken';
import { IJwtPayload } from '../../api/_types/types';

/**
 * This middleware is needed when running with SSR
 * it checks if the token in cookie is set and injects it into the nuxtjs/auth module
 * otherwise it will redirect to login
 * @param context
 */
const debugAuthMiddleware: Middleware = async (context: Context) => {
  if (process.server && context.req.headers.cookie != null) {
    try {
      const cookies = parseCookie(context.req.headers.cookie);
      const token = cookies['auth._token.local'] || '';
      const tokenWithoutBearer = token.replace('Bearer ', '');
      // console.log('headers.cookie token', token);
      // console.log('debugAuthMiddleware $auth 1', context.$auth.$state);
      if (!token || token.includes('false')) {
        // sometimes it stores 'Bearer false' when it unsets
        return;
      }
      const jwt: IJwtPayload = (jsonwebtoken.decode(tokenWithoutBearer) as unknown) as IJwtPayload;
      // console.log('jwt payload', jwt);
      if (!jwt) {
        return;
      }
      // console.log('set token ✅', jwt);
      await context.$auth.setToken('locale', tokenWithoutBearer);
      await context.$auth.setUser(jwt);
      context.$auth.$state.loggedIn = true;
    } catch (e) {
      console.error('debugAuthMiddleware', e);
    }
    // console.log('debugAuthMiddleware $auth 2', context.$auth.$state);
  }
};

export default debugAuthMiddleware;

then in nuxt.config.ts I have this middleware:

router: {
    middleware: ['user-agent', 'auth-ssr', 'auth'],
},

and this my auth config:

auth: {
    redirect: {
      logout: '/?signedOut=1',
      home: '/dashboard',
    },
    strategies: {
      local: {
        endpoints: {
          login: { url: '/api/v1/auth/login', method: 'post', propertyName: 'token' },
          logout: { url: '/api/v1/auth/logout', method: 'post' },
          user: { url: '/api/v1/user', method: 'get', propertyName: 'user' },
        },
        autoFetchUser: false, // do not fetch automatically! user object is coming from login api call
        rewriteRedirects: true, // If enabled, user will redirect back to the original guarded route instead of redirect.home.
        fullPathRedirect: true, // If true, use the full route path with query parameters for redirect
      },
    },
  },

Then the server side nodejs/auth-middleware gets the correct loggedIn state + user (my JWT token includes userId, email, name, scope: []).

Dharman
  • 30,962
  • 25
  • 85
  • 135
phips28
  • 232
  • 2
  • 14
  • 1
    This seems a lot of work but probably is a more proper way of solving the issue. good job! – Fabio Magarelli Nov 07 '20 at 18:58
  • 2
    Imo this should be implemented directly within nuxtjs/auth to male this work out of the box for SSR. Maybe I have time in the future to propose a PR. Or someone else sees this and implements it ;) – phips28 Nov 08 '20 at 00:23
0

Its already "old" post but i was facing the same problem as you. As i can see you are using DRF. So, i changed urls.py and created mylogin url with obtain_auth_token according to Django-documentation-authentication or you can create your own LoginView. Also, i created my subclass of TokenAuthentication with keyword Bearer and finally worked! Example: urls.py

from rest_framework.authtoken import views
path('api/login/',views.obtain_auth_token)

Create a file like authtoken.py

from rest_framework.authentication import TokenAuthentication
class TokenAuthentication(TokenAuthentication):
    keyword = 'Bearer'

Settings.py

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'myapp.authtoken.TokenAuthentication'
]

Now on nuxt.config.js

 auth: {
    strategies: {
      local: {
        user: {
          property: 'username',
        },
        endpoints: {
          login: { url: 'api/login/', method: 'post' },
          logout: { url: 'rest-auth/logout/', method: 'post' },
          user: { url: 'rest-auth/user/', method: 'get' }
        },
        tokenRequired:true,
        tokenType:'Bearer',
        autoFetchUser: false
      }
    }
  }

Thats all, i hope i helped !

update

on my nuxt.config.js i tried this and its working without touching anything else!

auth: {
    strategies: {
        local: {
          user: {
            property: 'username',
            autoFetch: true
          },

          endpoints: {

            login: { url: '/rest-auth/login/', method: 'post' },
            logout: { url: '/rest-auth/logout/', method: 'post' },
            user: { url: '/rest-auth/user/', method: 'get' },
          },
          token: {
            property: 'key',
            type: 'Token',
            name: 'Authorization',
          },
 
        }
      }
    }```
AlexDotis
  • 324
  • 4
  • 11
-1

Have you tried using auth without the $ sign? That should solve your problem