Nuxt middlewares does not work when page reloaded - vue.js

I added middlewares to default.vue component:
export default {
components: {
TheHeader
},
middleware: ['auth'],
}
My auth.js:
export default function ({ app, store }) {
if (app.$cookies.get('AUTH_TOKEN')) {
var AUTH_TOKEN = app.$cookies.get('AUTH_TOKEN')
app.$axios.$post('https://example.com/api', {
email: Buffer.from(AUTH_TOKEN[0], 'base64').toString(),
password: Buffer.from(AUTH_TOKEN[1], 'base64').toString(),
}).then(response => {
store.dispatch('changeAuthStatus', {
authStatus: true,
userData: {
id: response.data.id,
login: response.data.login,
email: response.data.email,
firstName: response.data.first_name,
lastName: response.data.last_name,
}
})
})
}
}
So I can't understand why my middlewares don't load when the page is reloaded or with direct access to the page. Also mode: 'universal' and ssr: true are set in nuxt.config.js

Here is the documentation for the middlewares: https://nuxtjs.org/docs/2.x/directory-structure/middleware/
Few steps to have a working middleware:
use it only in a page (/pages/hello.vue) or layout (/layouts/MyFancyLayout.vue)
put the middleware in the proper directory (/middleware/test.js)
call it properly in the .vue file like middleware: 'test'
You can also try to debug and see if something like this works
export default {
middleware() {
console.log('working!')
}
}
It is working on client transitions and should be good on initial page load aswell.

As a more accurate way, you should do this in the Vuex Store using the 'context' attribute.
middleware/auth.js
export default function(context) {
if (process.client) {
context.store.dispatch('initAuth', null);
}
context.store.dispatch('initAuth', context.req);
}
store/index.js
import Cookie from 'js-cookie'
store/index.js
actions:{
initAuth(vuexContext,state){
let token;
let jwtCookie;
if (req) {
if (!req.headers.cookie) {
return;
}
jwtCookie = req.headers.cookie
.split(";")
.find(c => c.trim().startsWith("jwt="));
if (!jwtCookie) {
return;
}
token = jwtCookie.split('=')[1];
} else {
token = localStorage.getItem('token');
}
vuexContext.commit('setToken', token);
return $axios.post('https://example.com/api', {
email: Buffer.from(AUTH_TOKEN[0], 'base64').toString(),
password: Buffer.from(AUTH_TOKEN[1], 'base64').toString(),
}).then(response => {
vuexContext.commit('changeAuthStatus', {
authStatus: true,
userData: {
id: response.data.id,
login: response.data.login,
email: response.data.email,
firstName: response.data.first_name,
lastName: response.data.last_name,
}
})
})
}
}
this way it will work smoothly and understandably

Related

how to keep user authenticated after refreshing the page in nuxtjs?

I'm using laravel passport for API's and nuxt.js for frontend after a successful login if I refresh the page the user is not authenticated anymore and loggedIn returns false, its my first nuxt.js project so I have no idea how to deal with that, any advise is appreciated
login.vue
<script>
import { mapActions } from 'vuex'
export default {
data() {
return {
email: "",
password: ""
}
},
methods:{
async login(){
const succesfulLogin = await this.$auth.loginWith('local', {
data: {
email: this.email,
password: this.password
},
})
this.$store.commit("saveUser",succesfulLogin.data)
this.$store.commit("saveToken", succesfulLogin.data.token)
if (succesfulLogin) {
await this.$auth.setUser({
email: this.email,
password: this.password,
})
this.$router.push('/profile')
}
}
}
}
</script>
store/index.js
export const state = () => ({
user:{},
token: ""
})
export const mutations = {
saveUser(state, payload) {
state.user=payload;
},
saveToken(state, token) {
state.token= token
}
}
export const actions = {
saveUserAction({commit}, UserObject){
commit('saveUser');
},
logoutUser({commit}){
commit('logout_user')
}
}
export const getters = {
getUser: (state) => {
return state.user
},
isAuthenticated(state) {
return state.auth.loggedIn
},
loggedInUser(state) {
return state.user.user
}
}
after a successful login
after refreshing the page
We do use a global middleware right after my auth module authentication
/middleware/global.js
export default async ({ app, store }) => {
if (store?.$auth?.$state?.loggedIn) {
if (!app.$cookies.get('gql.me_query_expiration')) {
// do some middleware logic if you wish
await app.$cookies.set('gql.me_query_expiration', '5min', {
// maxAge: 20,
maxAge: 5 * 60,
secure: true,
})
}
}
}
nuxt.config.js
router: {
middleware: ['auth', 'global'],
},
We're using cookie-universal-nuxt for handling secure cookies quickly, working great!
While accessing or refreshing the webapp (we do redirect to the /login page if not authenticated) and we use this basic GraphQL configuration where the cookie is needed.
/plugins/nuxt-apollo-config.js
export default ({ app }) => {
const headersConfig = setContext(() => ({
credentials: 'same-origin',
headers: {
Authorization: app.$cookies.get('auth._token.local'), // here
},
}))
[...]
}
Checking gql.me_query_expiration allows us to see if the user has authenticated lately/is currently authenticated or if he needs to refresh his token.
And auth._token.local is our actual JWT token, provided by the auth module.
As told above, it is more secure to have a secure cookie than some localStorage, this is also why we are not using it
nuxt.config.js
auth: {
localStorage: false, // REALLY not secure, so nah
...
}
You can just use localStorage and implement it yourself e.g.:
saveToken(state, token) {
localStorage.setItem("authToken", token);
state.token= token
},
saveUser(state, payload) {
localStorage.setItem("authUser", payload);
state.user=payload;
},
And then retrieving the localStorage when initializing your store you need to do something like this:
export const state = () => {
const localUser = localStorage.getItem("authToken")
const localToken = localStorage.getItem("authUser")
let user = {}
let token = ""
if (localUser) user = localUser
if (localToken) token = localToken
return {
user: user,
token: token
}
}
As #mbuechmann pointed out, be aware of the security risk when storing sensitive information in localStorage. Better to use cookies for tokens, but localStorage is the 'simple' solution.
or use a package like nuxt-vuex-localstorage

How to get user info after a successful authentication with nuxt-auth

I'm using nuxt.js, after I send a login request with email and password to the backend I get a response which contains a message, token and user informations, how can I access user informations in the response and save it inside some state in store.js after a successful login?
I wanted to save user object in user state down in store/index.js using an action saveUserAction which might be dispatched after a successful login, i dont know if thats right or not, any advise would be very helpful
Response
{
"message":"success",
"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiIxIiwianRpIjoiNzFkYjA1MWM2MTYxMmE4YzAyNWI2YjU3N2xMzJiNzJjMjI0MzRlY2IzNzYwNTg2N2NjOWQ5ZWEwY2MiMJM3uYEiZ8GSlPlQhIctVErO2KzwXOBxifWWoM7et_qT-mgvfsk3ljwiQF9iPQw-WeekBx8J8lcmxDLESa3tfE1Re1Xk2flkcBLmiI4JN2YHh08U1U",
"user":{
"id":1,
"role_id":4587,
"firstname":"Hans",
"lastname":"newman",
"email":"newman#gmail.com",
"email_verified_at":null,
"phone":"89498",
"skype":"gdgdfg",
"birthdate":"2021-05-02",
"address":"asdfaf",
"postalcode":14984,
"city":"jisf",
"country":"isfisf",
"status":"mfof",
"created_at":"2021-06-16T09:33:08.000000Z",
"updated_at":"2021-06-16T09:39:41.000000Z",
"image":"1623835988-carlsen.png",
"description":"sfdgg",
"geo_lat":5.5,
"geo_lng":8.1
}
}
login.vue
<script>
import { mapActions } from 'vuex'
export default {
data() {
return {
auth: false,
email: '',
password: '',
}
},
methods: {
async login() {
const succesfulLogin = await this.$auth.loginWith('local', {
data: {
email: this.email,
password: this.password,
},
})
if (succesfulLogin) {
await this.$auth.setUser({
email: this.email,
password: this.password,
})
this.$router.push('/profile')
}
},
},
}
</script>
store/index.js
export const state = () => ({
user:{}
})
export const mutations = {
saveUser(state, payload) {
state.user=payload;
}
}
export const actions = {
saveUserAction({commit}, UserObject){
commit('saveUser');
}
}
Go to your vue devtools, vuex tab and look for auth, it should already be available. This answer may help you during your debugging: https://stackoverflow.com/a/68081536/8816585
Since you do have your user object in the response, this kind of configuration should do it, as shown in the documentation. No need to make some other vuex actions.
auth: {
strategies: {
local: {
token: {
property: 'token',
global: true,
},
user: {
property: 'user', // the name of your object in your backend response payload
},
endpoints: {
[...]
}
}
}
}

Redirect after logged in

guys. Can someone help me to fix this problem with redirect?
I have this router file:
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!store.state.auth.is_authenticated) {
next({
path: '/login',
query: {
redirect: to.fullPath
}
})
} else {
next()
}
} else if (to.matched.some(record => record.meta.requiresGuest)) {
if (store.state.auth.is_authenticated) {
next({
path: '/',
query: {
redirect: to.fullPath
}
})
} else {
next()
}
} else {
next()
}
})
Current auth status is stored in vuex state. When I`m logging in status is changing but nothing is happening with page, only Header component updates.
<script>
import { mapGetters, mapActions } from "vuex";
export default {
name: "Login",
data: () => {
return {
email: "",
password: ""
};
},
computed: mapGetters(["currentUser"]),
methods: {
...mapActions(["loginUser"]),
login: function(event) {
event.preventDefault();
this.loginUser({ email: this.email, password: this.password });
}
},
updated() {
if (this.currentUser.is_authenticated) {
this.$router.go({ path: this.$router.path });
}
}
};
</script>
Please modify your code like this and check
helper.js
export function initialize(store, router)
{ router.beforeEach((to,from,next)=>{
const requiresAuth= to.matched.some(record=>record.meta.requiresAuth)
const currentUser = store.state.auth.user;
if(requiresAuth && !currentUser){ next('/login');}
else if(to.path=='/login' && currentUser){ next('/')}
//if user already authenticated and user tries to open login page
//redirect to / path , it can be modified as per config
else { next()}
})
}
Add this in your main.js
import {initialize} from './helper';
initialize(store, router);
In you login page updated part not required as router.beforeEach code in helper.js will be able to take care of that.
methods: {
...mapActions(["loginUser"]),
login: function(event) {
event.preventDefault();
this.loginUser({ email: this.email, password: this.password })
.then(()=>{ this.$router.replace({name:'dashboard'})
//forward to this path after login
}).catch(()=>{console.log('login fail') });
}
},
If store.state.auth.is_authenticated is true means that you logged in you must do after that flag go true something like this this.$router.go(); if you make this with no argument thats means that you will go in exact same page that you are (login page) but then it will triggered by your guards and it will redirect you to panel page or so

Access Vue app (this) from non vue file

I'm new to vue (started using vue 2) I'm using Store (vuex) and I'm trying to acheive something.
basically I managed to install the vue-auth plugin : I have this.$auth that I can call from within .vue files.
Now using the store I wanna call the userLogin function by dispatching the call like this from a vue file :
<script>
export default {
computed: {
comparePasswords() {
return this.password === this.passwordConfirm
? true
: "Passwords don't match";
}
},
methods: {
userSignUp() {
if (this.comparePasswords !== true) {
return;
}
this.$store.dispatch("userSignUp", {
email: this.email,
password: this.password
});
}
},
data() {
return {
email: "",
password: "",
passwordConfirm: ""
};
}
};
</script>
in the store/index I'm trying to access the 'this.$auth' I do understand is some kind of context switching but I don't know how to access the vue app instance. :
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
let app = this
export const store = new Vuex.Store({
state: {
appTitle: 'LiveScale Dashboard',
user: null,
error: null,
loading: false
},
mutations: {
setUser(state, payload) {
state.user = payload
},
setError(state, payload) {
state.error = payload
},
setLoading(state, payload) {
state.loading = payload
}
},
actions: {
userLogin({ commit }, payload) {
commit('setLoading', true)
var redirect = this.$auth.redirect(); // THIS IS WRONG.
this.$auth.login({ // THIS IS WRONG.
body: payload, // Vue-resource
data: payload, // Axios
rememberMe: this.data.rememberMe,
redirect: { name: redirect ? redirect.from.name : 'account' },
fetchUser: this.data.fetchUser
})
.then(() => {
commit('setUser', this.context)
commit('setLoading', false)
router.push('/home')
}, (res) => {
console.log('error ' + this.context);
commit('setError', res.data)
commit('setLoading', false)
});
},
userSignUp({ commit }, payload) {
// ...
}
},
getters: {}
})
Thanks for your help
try using Vue.$auth in index.js it should work
The idea (so far) is to pass the instance as an argument to the function as follows :
this.$store.dispatch("userSignUp", {
email: this.email,
password: this.password,
auth: this.$auth //added this line
});
and then in the store -> actions , payload.auth will contain my auth plugin :
userLogin({ commit }, payload) {
commit('setLoading', true)
var redirect = payload.auth.redirect();
payload.auth.login({
body: payload, // Vue-resource
data: payload, // Axios
rememberMe: this.data.rememberMe,
redirect: { name: redirect ? redirect.from.name : 'account' },
fetchUser: this.data.fetchUser
})
.then(() => {
commit('setUser', this.context)
commit('setLoading', false)
router.push('/home')
}, (res) => {
console.log('error ' + this.context);
commit('setError', res.data)
commit('setLoading', false)
});
},
I don't know if it's the best practice or not, but this is how I managed to do it. Please feel free to suggest anything.

Nuxt Auth Module Authenticated User Data

I have an API api/auth that is used to log users in. It expects to receive an access_token (as URL query, from Headers, or from request body), a username, and a password. I've been using the Vue Chrome Developer Tool and even though I get a 201 response from the server, the auth.loggedIn state is still false. I think that might be the reason why my redirect paths on the nuxt.config.js isn't working as well. Can anyone point me to the right direction on why it doesn't work?
This is a screenshot of the Vue Chrome Developer Tool
This is the JSON response of the server after logging in. The token here is different from the access_token as noted above.
{
"token": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"user": {
"user_name": "xxxxxxxxxxxxxxxxxx",
"uid": "xxxxxxxxxxxxxxxxxx",
"user_data": "XXXXXXXXXXXXXXXXXXXXXXXXX"
}
}
Here is the relevant part of nuxt.config.js
export default {
modules: [
'#nuxtjs/axios',
'#nuxtjs/auth',
['bootstrap-vue/nuxt', { css: false }]
],
router: {
middleware: [ 'auth' ]
},
auth: {
strategies: {
local: {
endpoints: {
login: {
url: '/api/auth?access_token=XXXXXXXXXXXXXXXXXXXXXX',
method: 'post',
propertyName: 'token'
},
logout: {
url: '/api/auth/logout',
method: 'post'
},
user: {
url: '/api/users/me',
method: 'get',
propertyName: 'user'
}
}
}
},
redirect: {
login: '/',
logout: '/',
home: '/home'
},
token: {
name: 'token'
},
cookie: {
name: 'token'
},
rewriteRedirects: true
},
axios: {
baseURL: 'http://localhost:9000/'
}
}
And my store/index.js
export const state = () => ({
authUser: null
})
export const mutations = {
SET_USER: function (state, user) {
state.authUser = user
}
}
export const actions = {
nuxtServerInit ({ commit }, { req }) {
if (req.session && req.user) {
commit('SET_USER', req.user)
}
},
async login ({ commit }, { username, password }) {
const auth = {
username: username,
password: password
}
try {
const { user } = this.$auth.loginWith('local', { auth })
commit('SET_USER', user)
} catch (err) {
console.error(err)
}
}
}
The login action in the store is triggered by this method in the page:
export default {
auth: false,
methods: {
async login () {
try {
await this.$store.dispatch('login', {
username: this.form.email,
password: this.form.password
})
} catch (err) {
this.alert.status = true
this.alert.type = 'danger'
this.alert.response = err
}
}
}
}
P.S. I realize I'm explicitly including the access_token in the URL. Currently, I don't know where a master_key or the like can be set in the Nuxt Auth Module.
Try this in your store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = () => new Vuex.Store({
state: {
authUser: null
},
mutations: {
SET_USER: function (state, user) {
state.authUser = user
}
},
actions: {
CHECK_AUTH: function(token, router) {
if (token === null) {
router.push('/login')
}
}
}
})
export default store
And for the router, this should work globally:
$nuxt._router.push('/')