vuejs laravel authentication page blocking - vue.js

what I want to do is to prevent the user from entering the login page again if he is logged in,
Likewise, if the login process is successful, the login page will be blocked.
If the user is not logged in, he cannot go to pages such as home about. Likewise, if the login is successful, it will be possible to return to the login or register page.
app.vue
created() {
this.$store.dispatch("initAuth")
},
<template>
<div>
<h1>login</h1>
<div>
<input type="email" v-model="user.email">
</div>
<div>
<input type="password" v-model="user.password">
</div>
<div>
<button #click="login">Giriş Yap</button>
</div>
</div>
</template>
<script>
export default {
name: "Login",
data() {
return {
user: {
email: null,
password: null,
},
isUser: false
}
},
methods: {
login() {
this.$store.dispatch("login", {...this.user, isUser: this.isUser})
}
}
}
</script>
router.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import store from '../store'
Vue.use(VueRouter)
const router = new VueRouter({
mode: 'history',
routes: [
{
path: '/',
name: 'home',
component: () => import('#/views/Home.vue'),
meta: {
requiresAuth: true
},
},
{
path: '/about',
name: 'about',
component: () => import('#/views/About.vue'),
meta: {
requiresAuth: true,
},
},
{
path: '/login',
name: 'login',
component: () => import('#/views/Login.vue'),
},
{
path: '/register',
name: 'register',
component: () => import('#/views/Register.vue'),
},
{
path: '/error-404',
name: 'error-404',
component: () => import('#/views/error/Error404.vue'),
},
{
path: '*',
redirect: 'error-404',
},
],
})
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!store.getters.isAuthenticated) {
next({name: 'login'})
} else {
next()
}
} else {
next() // make sure to always call next()!
}
});
export default router
import Vue from 'vue'
import Vuex from 'vuex'
import axios from "axios";
import router from '../router';
Vue.use(Vuex)
export default new Vuex.Store({
state: {
token: "",
},
mutations: {
setToken(state, token) {
state.token = token
},
clearToken(state) {
state.token = ""
}
},
actions: {
initAuth({commit, dispatch}) {
let token = localStorage.getItem("token")
if (token) {
commit('setToken', token)
} else {
router.push('/login')
commit('clearToken')
//return false;
}
},
login({commit, dispatch, state}, autData) {
return axios.post(
'/api/login', {
email: autData.email,
password: autData.password
})
.then(response => {
commit('setToken', response.data.token)
localStorage.setItem('token', response.data.token)
router.push('/about')
console.log(response)
})
.catch(error => {
console.log(error)
})
},
register({commit, dispatch, state}, autData) {
return axios.post(
'/api/register', {
name: autData.name,
email: autData.email,
password: autData.password,
password_confirmation: autData.password_confirmation
})
.then(response => {
router.push('/about')
commit('setToken', response.data.token)
console.log(response)
})
.catch(error => {
console.log(error.response.data.errors);
})
},
logout({commit}) {
commit('clearToken')
localStorage.removeItem('token')
router.push('/');
},
setTimeoutTimer({dispatch}, expiresIn) {
setTimeout(() => {
dispatch("logout")
}, expiresIn)
}
},
getters: {
isAuthenticated(state) {
return state.token !== ""
}
},
modules: {},
})

add a new meta key requiresNotAuth for login and register routes then change beforeEach content like this
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!store.getters.isAuthenticated) {
next({name: 'login'})
} else {
next()
}
}
else if (to.matched.some(record => record.meta.requiresNotAuth)) {
if (store.getters.isAuthenticated) {
next({name: 'home'})
}
else {
next()
}
}
else {
next() // make sure to always call next()!
}

Related

Vue 3, SocketIO - won't join the room after login and router push

I am experiencing the following issue - once the user is logged in, and onMounted event is finished, SocketIO client side should join the room. But this doesn't happen for some reason. I have to manually refresh browser in order to join the room. What am I doing wrong here?
I have the following code for the SocketIO on the client side:
import { io } from "socket.io-client";
const token = window.localStorage.getItem('TOKEN') || window.sessionStorage.getItem('TOKEN')
console.log(token);
const ioSocket = io('https://dolphin-app-e4ozn.ondigitalocean.app', {
withCredentials: true,
transportOptions: {
polling: {
extraHeaders: {
'authorization': `${token}`,
},
},
},
});
export const socket = ioSocket
The vue 3 router logic:
import { createRouter, createWebHistory } from 'vue-router'
import Landing from '#/views/Landing.vue'
import Login from '#/views/login/Login.vue'
import ResetPassword from '#/views/login/ResetPassword.vue'
import ForgotPassword from '#/views/login/ForgotPassword.vue'
const routes = [
{
path: '/login',
name: 'login',
component: Login,
meta: {
isGuest: true,
title: 'Servant | Login'
}
},
{
path: '/resetPassword',
name: 'resetPassword',
component: ResetPassword,
meta: {
isGuest: true,
title: 'Servant | Reset Password'
}
},
{
path: '/forgotPassword',
name: 'forgotPassword',
component: ForgotPassword,
meta: {
isGuest: true,
title: 'Servant | Forgot Password'
}
},
{
path: '/',
name: 'landing',
component: Landing,
meta: {
requiresAuth: true,
title: 'Servant',
role: 'waiter'
}
},
{
path: '/:pathMatch(.*)*',
component: Landing
},
]
const router = createRouter({
history: createWebHistory(),
routes,
scrollBehavior() {
// always scroll to top
return { top: 0 }
},
})
router.beforeEach((to, from, next) => {
document.title = to.meta.title || "Servant"
let token = window.localStorage.getItem('TOKEN') || window.sessionStorage.getItem('TOKEN')
if(to.meta.requiresAuth && !token)
{
next({name: 'login'})
}
if (token && to.meta.isGuest )
{
next({ name: 'landing' })
}
next();
});
export default router
Login component logic:
function login() {
loading.value = true
formClass = ''
if (user.remember)
{
window.localStorage.setItem('remember', user.remember)
}
mainStore
.login(user)
.then((response) => {
loading.value = false
router.push({
name: 'landing',
})
})
.catch((err) => {
loading.value = false
errorMsg.value = err.response.data.messages.error
formClass = 'was-validated'
})
}
Once the component is mounter I have following logic:
onMounted(() => {
socket.emit("join", import.meta.env.VITE_SOCKET_ROOM, (message) => {
console.log(message);
});
})
On the SocketIO server side I have following logic:
io.use((socket, next) => {
const header = socket.handshake.headers["authorization"];
if(header !== 'null')
{
jwtVerify(header, secret).then((res) => {
if (res === true) {
const jwt = jwtDecode(header);
servantID = jwt.payload.iss;
return next();
}
return next(new Error("authentication error"));
});
}
});

Vue Router Warning and Router.push is not Functioning

I am trying to route to another page after getting response from adonis project. Calling to post method is working. However router.push('/') is not functioning. Only login page reloaded every time when I submitted the b-from.
async login({ commit, state }) {
console.log("Login")
try {
const response = await HTTP()
.post('/admin/login', {
email: state.loginEmail,
password: state.loginPassword
})
.then(response => {
console.log("Ok")
console.log(response.data)
if (response.data == 'UserNotFoundException') {
alert('User not found')
router.push('/')
}
if (response.data == 'PasswordMisMatchException') {
alert('password not ms')
router.push('/')
}
if (response.data.token) {
console.log(response)
//commit('setToken', response.data.token)
router.push('/')
} else {
router.push('/')
}
})
console.log(response)
//return router.push('/')
} catch (error) {
console.log(error)
}
},
Routes:
routes: [
{
name: "FullLogin",
path: "/login",
component: () => import("#/views/authentication/FullLogin"),
},
{
path: "/",
redirect: "/dashboard/docs-dashboard",
component: () => import("#/layouts/full-layout/FullLayout"),
children: [
{
name: "Dashboard",
path: "/dashboard/docs-dashboard",
component: () => import("#/views/dashboards/docsDashboard"),
},
]
}
]
router.beforeEach((to, from, next) => {
next()
})
I can't figure out why router.push('/') is not routing.
According to docs:
Note: Inside of a Vue instance, you have access to the router instance as $router. You can therefore call this.$router.push.

Vue Router catch 401 on route changes and destroy local storage JWT

Having a dashboard app created with vuejs and vue-router most of my routes are requiring authentication and I want to catch globally if token is expired and route back in that case to login component.
Right now router looks as it follows
import Vue from 'vue'
import VueRouter from 'vue-router'
import AttendersView from '#/views/AttendersView.vue'
import LoginView from '#/views/LoginView.vue'
import store from '../store'
import log from '#/middleware/log'
Vue.use(VueRouter)
const routes = [
{
path: '*',
meta: {
name: '',
requiresAuth: true
},
redirect: {
path: '/attenders'
}
},
{
path: '/login',
component: LoginView,
meta: {
guest: true
},
children: [
{
path: '',
component: () => import('#/components/LoginForm.vue')
}
]
},
{
path: '/',
meta: {
name: 'Dashboard View',
requiresAuth: true
},
component: () => import('#/views/DashboardView.vue'),
children: [
{
path: '/attenders',
name: 'Anmeldungen',
component: AttendersView,
meta: {
requiresAuth: true,
middleware: log
}
},
{
path: '/organizations',
name: 'Verbände',
meta: {
requiresAuth: true,
middleware: log
},
component: () => import(/* webpackChunkName: "about" */ '../views/OrganizationsView.vue')
},
{
path: '/workgroups',
name: 'Arbeitsgruppen',
meta: {
requiresAuth: true,
middleware: log
},
component: () => import(/* webpackChunkName: "about" */ '../views/WorkgroupsView.vue')
},
{
path: '/status',
name: 'Status',
meta: {
requiresAuth: true,
middleware: log
},
component: () => import(/* webpackChunkName: "about" */ '../views/StatusView.vue')
}
]
}
]
const router = new VueRouter({
mode: 'history',
base: 'dashboard',
routes,
scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
}
if (to.hash) {
return { selector: to.hash }
}
return { x: 0, y: 0 }
}
})
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
if (store.getters.authorized) {
next()
return
}
next('/login')
} else {
next()
}
})
export default router
and here is my custom request.js
import axios from 'axios'
class HttpClient {
constructor (token) {
if (localStorage.getItem('token')) {
token = localStorage.getItem('token')
}
const service = axios.create({
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
}
})
this.service = service
}
redirectTo = (document, path) => {
document.location = path
}
get (path) {
return this.service.get(path)
}
patch (path, payload, callback) {
return this.service
.request({
method: 'PATCH',
url: path,
responseType: 'json',
data: payload
})
}
post (path, payload) {
return this.service.request({
method: 'POST',
url: path,
responseType: 'json',
data: payload
})
}
delete (path, payload) {
return this.service.request({
method: 'DELETE',
url: path,
responseType: 'json',
data: payload
})
}
download (path) {
return this.service.request({
method: 'POST',
url: path,
responseType: 'blob'
})
}
}
const HttpRequests = new HttpClient()
export default HttpRequests
and right now I'm doing like this in component to catch 401
methods: {
fetchInitialData: function () {
this.isLoading = true
HttpClient.get(API_ATTENDERS_ENDPOINT).then(resp => {
this.attenders = resp.data.attenders
this.organizations = resp.data.organizations
this.workgroups = resp.data.workgroups
this.isLoading = false
}).catch(error => {
if (error.response.status === 401) {
this.$store.dispatch('logout')
}
})
}
but I need something generic.
What is the best approach and where should I place an axios interceptor ?
What you can do is use navigation guards for this task: https://router.vuejs.org/guide/advanced/navigation-guards.html#global-before-guards
Before the user enters any of the routes you want Authentication attached to then you can have a before enter, and then check their privileges.
Pass then on with a next() if they succeed or throw them to the login.

loggedIn state reverts back to false after Logging in and doesn't allow me to guard route

I'm trying to guard my routes with state: { loggedIn: false }, when I login from my Login.vue component the goal is to trigger an action this.$store.dispatch('setLogin') that mutates the state of loggedIn to true. There is then navigation guard that is suppose to prevent me form seeing my Login.vue and Regester.vue components. The problem is that it seems like the state changes to true, but not the base state: allowing me to keep hitting the /auth/login and /auth/register routes.
Routes
const routes = [
{
path: '/auth',
name: 'auth',
component: Auth,
children: [
{ name: 'login', path: 'login', component: Login },
{ name: 'register', path: 'register', component: Register },
],
meta: {
requiresVisitor: true,
}
},
{
path: '/logout',
name: 'logout',
component: Logout
}
]
Login Component
login() {
this.$http.get('/sanctum/csrf-cookie').then(response => {
this.$http.post('/login', {
email: this.username,
password: this.password,
}).then(response2 => {
this.$store.dispatch('setLogin')
this.$store.dispatch('getUser')
alert(this.$store.state.loggedIn)
this.$router.push({ name: 'Home' })
}).catch(error => {
console.log(error.response.data);
const key = Object.keys(error.response.data.errors)[0]
this.errorMessage = error.response.data.errors[key][0]
})
});
}
Vuex
export default new Vuex.Store({
state: {
loggedIn: false,
user: JSON.parse(localStorage.getItem('user')) || null,
},
mutations: {
setLogin: (state) => {
state.loggedIn = true
},
SET_USER_DATA (state, userData) {
localStorage.setItem('user', JSON.stringify(userData))
state.user = userData;
},
removeUser(state) {
localStorage.removeItem('user');
state.user = null;
}
},
actions: {
getUser(context) {
if (context.state.loggedIn) {
alert('hit');
return new Promise((resolve, reject) => {
axios.get('api/user')
.then(response => {
context.commit('SET_USER_DATA', response.data.data)
resolve(response)
})
.catch(error => {
reject(error)
})
})
}
},
setLogin(context){
context.commit('setLogin')
}
},
modules: {
}
})
It's strange because alert(this.$store.state.loggedIn) renders true, but when I go back the auth link there's a mounted state alert that comes back false.
Here's my navigation guards as well:
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!store.state.loggedIn) {
next({
name: 'login',
})
} else {
next()
}
} else if (to.matched.some(record => record.meta.requiresVisitor)) {
if (store.state.loggedIn) {
next({
name: 'Home',
})
return
} else {
next()
}
} else {
next()
}
})
You need to store the loggedIn user in local storage:
setLogin: (state) => {
state.loggedIn = localStorage.setItem('loggedIn', 'true')
state.loggedIn = true
},
Then your state should look like:
state: {
loggedIn: localStorage.getItem('loggedIn') || null,
},

failed to configure axios with django rest auth to login and get auth token

I tried these codes:
src/api/auth.js
import session from "./session";
export default {
login(Username, Password) {
return session.post("/auth/login/", { Username, Password });
},
logout() {
return session.post("/auth/logout/", {});
},
createAccount(username, password1, password2, email) {
return session.post("/registration/", {
username,
password1,
password2,
email
});
},
changeAccountPassword(password1, password2) {
return session.post("/auth/password/change/", { password1, password2 });
},
sendAccountPasswordResetEmail(email) {
return session.post("/auth/password/reset/", { email });
},
resetAccountPassword(uid, token, new_password1, new_password2) {
// eslint-disable-line camelcase
return session.post("/auth/password/reset/confirm/", {
uid,
token,
new_password1,
new_password2
});
},
getAccountDetails() {
return session.get("/auth/user/");
},
updateAccountDetails(data) {
return session.patch("/auth/user/", data);
},
verifyAccountEmail(key) {
return session.post("/registration/verify-email/", { key });
}
};
src/session.js
import axios from "axios";
const CSRF_COOKIE_NAME = "csrftoken";
const CSRF_HEADER_NAME = "X-CSRFToken";
const session = axios.create({
xsrfCookieName: CSRF_COOKIE_NAME,
xsrfHeaderName: CSRF_HEADER_NAME
});
export default session;
src/store/auth.js
import auth from "../api/auth";
import session from "../api/session";
import {
LOGIN_BEGIN,
LOGIN_FAILURE,
LOGIN_SUCCESS,
LOGOUT,
REMOVE_TOKEN,
SET_TOKEN
} from "./types";
const TOKEN_STORAGE_KEY = "TOKEN_STORAGE_KEY";
const initialState = {
authenticating: false,
error: false,
token: null
};
const getters = {
isAuthenticated: state => !!state.token
};
const actions = {
login({ commit }, { username, password }) {
commit(LOGIN_BEGIN);
return auth
.login(username, password)
.then(({ data }) => commit(SET_TOKEN, data.key))
.then(() => commit(LOGIN_SUCCESS))
.catch(() => commit(LOGIN_FAILURE));
},
logout({ commit }) {
return auth
.logout()
.then(() => commit(LOGOUT))
.finally(() => commit(REMOVE_TOKEN));
},
initialize({ commit }) {
const token = localStorage.getItem(TOKEN_STORAGE_KEY);
if (token) {
commit(SET_TOKEN, token);
} else {
commit(REMOVE_TOKEN);
}
}
};
const mutations = {
[LOGIN_BEGIN](state) {
state.authenticating = true;
state.error = false;
},
[LOGIN_FAILURE](state) {
state.authenticating = false;
state.error = true;
},
[LOGIN_SUCCESS](state) {
state.authenticating = false;
state.error = false;
},
[LOGOUT](state) {
state.authenticating = false;
state.error = false;
},
[SET_TOKEN](state, token) {
localStorage.setItem(TOKEN_STORAGE_KEY, token);
session.defaults.headers.Authorization = `Token ${token}`;
state.token = token;
},
[REMOVE_TOKEN](state) {
localStorage.removeItem(TOKEN_STORAGE_KEY);
delete session.defaults.headers.Authorization;
state.token = null;
}
};
export default {
namespaced: true,
state: initialState,
getters,
actions,
mutations
};
src/views/Login.vue
<template>
<div>
<form class="login" #submit.prevent="login">
<h1>Sign in</h1>
<label>Username</label>
<input required v-model="Username" type="text" placeholder="Name" />
<label>Password</label>
<input
required
v-model="Password"
type="password"
placeholder="Password"
/>
<hr />
<button type="submit">Login</button>
</form>
</div>
</template>
<script>
import axios from "axios";
export default {
data() {
return {
Username: "",
Password: ""
};
},
methods: {
/*login() {
let Username = this.Username;
let Password = this.Password;
this.$store
.dispatch("auth/login", { Username, Password })
.then(() => this.$router.push("/"))
.catch(err => console.log(err));*/
login({ Username, Password }) {
const API_URL = "http://127.0.0.1:8000";
const url = `${API_URL}/auth/login/`;
return axios
.post(url, { Username, Password })
.then(() => this.$router.push("/"))
.catch(err => console.log(err));
}
},
mounted() {
this.login();
}
};
</script>
first, I tried using the auth service but the failure was OPTIONS not POST.
then i used axios directly to post user and get token, but then the failure was method not allowed, I do not know what is the hidden process, can you explain please?
app.vue
<template>
<div id="app">
<navbar v-if="isAuthenticated"></navbar>
<router-view></router-view>
</div>
</template>
<script>
import { mapGetters } from "vuex";
import Navbar from "./components/Navbar";
export default {
name: "app",
components: {
Navbar
},
computed: mapGetters("auth", ["isAuthenticated"])
};
</script>
router.js
import Vue from "vue";
import Router from "vue-router";
import About from "./views/About";
import Home from "./views/Home";
import Login from "./views/Login";
import Lost from "./views/Lost";
import PasswordReset from "./views/PasswordReset";
import PasswordResetConfirm from "./views/PasswordResetConfirm";
import Register from "./views/Register";
import VerifyEmail from "./views/VerifyEmail";
import store from "./store/index";
const requireAuthenticated = (to, from, next) => {
store.dispatch("auth/initialize").then(() => {
if (!store.getters["auth/isAuthenticated"]) {
next("/login");
} else {
next();
}
});
};
const requireUnauthenticated = (to, from, next) => {
store.dispatch("auth/initialize").then(() => {
if (store.getters["auth/isAuthenticated"]) {
next("/home");
} else {
next();
}
});
};
const redirectLogout = (to, from, next) => {
store.dispatch("auth/logout").then(() => next("/login"));
};
Vue.use(Router);
export default new Router({
saveScrollPosition: true,
routes: [
{
path: "/",
redirect: "/home"
},
{
path: "/about",
component: About,
beforeEnter: requireAuthenticated
},
{
path: "/home",
component: Home,
beforeEnter: requireAuthenticated
},
{
path: "/password_reset",
component: PasswordReset
},
{
path: "/password_reset/:uid/:token",
component: PasswordResetConfirm
},
{
path: "/register",
component: Register
},
{
path: "/register/:key",
component: VerifyEmail
},
{
path: "/login",
component: Login,
beforeEnter: requireUnauthenticated
},
{
path: "/logout",
beforeEnter: redirectLogout
},
{
path: "*",
component: Lost
}
]
});
main.js
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store/index";
import axios from "axios";
import Axios from "axios";
import VueAxios from "vue-axios";
import Vuex from "vuex";
Vue.use(Vuex);
Vue.use(VueAxios, axios);
//Vue.config.productionTip = false;
Vue.prototype.$http = Axios;
const token = localStorage.getItem("token");
if (token) {
Vue.prototype.$http.defaults.headers.common["Authorization"] = token;
}
new Vue({
router,
store,
render: h => h(App)
}).$mount("#app");
references:
https://www.techiediaries.com/vue-axios-tutorial/
https://scotch.io/tutorials/handling-authentication-in-vue-using-vuex
https://github.com/jakemcdermott/vue-django-rest-auth
Most likely CORS issues.
If you're serving the vue server on different port then the django server, then you need to set the proper CORS settings.
Please refer to https://github.com/ottoyiu/django-cors-headers/
while it seems my code is little ugly because I mixed bunch of repositories, but at least I made it make POST successfully. I used
npm install qs
then import it, then:
var username = payload.credential.username;
var password = payload.credential.password;
then editing
axios.post(url, qs.stringify({ username, password })
and it worked.