Have to click login button twice; Using vue-router and firebase authentication - javascript

I have setup up a router guard so when I login instead of my router pushing it to the dashboard the split second time it requires firebase to authenticate it thinks im not logged in and I have to wait and click the login button again.
Is there any way to wait for it to log in then the router pushes me to the dashboard.
Im new to this any help would be appreciated.
//routes
export const routes = [
{
path: "/adduser",
component: AddUser,
meta: {
requiresAuth: true
}
},
{
name: "details",
path: "/details/:id",
component: User,
meta: {
requiresAuth: true
}
},
{
path: "/register",
component: Register,
meta: {
requiresGuest: true
}
},
{
path: "/login",
component: Login,
meta: {
requiresGuest: true
}
},
{
path: "/dashboard",
component: Dashboard,
meta: {
requiresAuth: true
}
},
{
path: "/",
component: Dashboard,
meta: {
requiresAuth: true
}
},
{
name: "editUser",
path: "edituser/:id",
component: EditUser,
meta: {
requiresAuth: true
}
}
];
//the login function
emailLogin(email, password) {
firebase
.auth()
.signInWithEmailAndPassword(email, password)
.then(this.$router.push("/dashboard"))
.then(() => {
this.$store.dispatch("auth/login");
});
}
//the router guard
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!firebase.auth().currentUser) {
next({
path: "/login",
query: {
redirect: to.fullPath
}
});
} else {
next();
}
} else if (to.matched.some(record => record.meta.requiresGuest)) {
if (firebase.auth().currentUser) {
next({
path: "/"
});
} else {
next();
}
} else {
next();
}
});

inside the then(this.$router.push("/dashboard")) the push gives a promise which should be returned to an arrow function.
So the new login function would be:
emailLogin(email, password) {
firebase
.auth()
.signInWithEmailAndPassword(email, password)
.then(() => {
this.$router.push("/dashboard");
})
.then(() => {
this.$store.dispatch("auth/login");
});
}

Related

Redirection based on user type - vue router

I am a beginner in vue and I need your help please. I am creating an application where the login is connected to firebase. I would like to use vue-router to redirect a user to a particular page. When a person logs in whose user.role = "admin" it should be redirected to "/admin". Every other logged person to "/" and non-logged in people are redirected to "/login" page.
Here are parts of my code:
main.js
router.beforeEach((to, from, next) => {
const currentUser = firebase.auth().currentUser;
const requiresAuth = to.meta.requiresAuth;
if (requiresAuth && !currentUser){ next({ name: 'Login' })}
else if (!requiresAuth && currentUser) {next({name: 'Dashboard'}), console.log(currentUser)}
else next();
});
authStore.js
const actions = {
logIn({ dispatch,commit,rootGetters }){
firebase.auth().onAuthStateChanged(async (user) => {
if (user) {
commit('SET_USER', user);
var uid = user.uid;
db.collection('users').doc(uid)
.get()
.then((doc)=>{
commit('gutscheinStore/SET_USER_ROLE', doc.data().role, {root:true})
commit('gutscheinStore/SET_USER_STANDORT_ID', doc.data().standortID, {root: true})
commit('gutscheinStore/SET_USER_BETREIBER_ID', doc.data().betreiberID, {root: true})
//console.log(rootGetters['gutscheinStore/getUserRole'])
})
router.push('/')
} else {
console.log("No entry without login")
}
})
},
index.js in router
const routes = [
{
path: '/',
name: 'Dashboard',
component: Dashboard,
meta: {
requiresAuth: true,
}
},
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '/admin',
name:'AdminDashboard',
component: AdminDashboard,
meta: {
requiresAuth: true,
}
In authStore.js where you currently have the line router.push("/"):
const actions = {
...
// snipped for clarity
router.push('/')
} else {
console.log("No entry without login")
}
})
},
You could instead change this as follows:
const actions = {
...
// snipped for clarity
if(user.role === "admin") {
router.push("/admin")
} else {
router.push("/")
}
} else {
// router.push("/login")
router.go()
}
})
},

Protect a vue route with multiple guards

I have a vue router like this:
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
function guard (to, from, next) {
if (localStorage.getItem('jwt')) {
next()
} else {
next('/login')
}
}
function admin (to, from, next) {
if (localStorage.getItem('admin') && localStorage.getItem('admin') !== 'false' && localStorage.getItem('jwt')) {
next()
} else {
next('/noadmin')
}
}
export default new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'home',
beforeEnter: guard,
meta: { layout: 'default' },
component: () => import('#/components/dashboard.vue')
},
{
path: '/datacollector',
name: 'datacollector',
beforeEnter: guard,
meta: { layout: 'default' },
component: () => import('#/components/datacollector.vue')
},
{
path: '/licensing',
beforeEnter: admin,
name: 'licensing',
component: () => import('#/components/licensing.vue')
},
{
path: '*',
name: '404',
component: require('#/pages/404.vue').default
}
]
})
My problem now is the following, if there is an entry localStorage.getItem('datacollector') it should only point to the path /datacollector and /logout. So when he logs in, the login page throws him to the path /datacollector. But he should also be able to call logout. For the other users, it should be that they are allowed to call everything. How do I adjust the function guard, no matter what I try I always end up with a loop. Thank you.
Assuming you want to throw the user to /datacollector route from any route, if there is an entry for localStorage.getItem('datacollector'), except for /logout route.
You can simply check for to.path and redirect the user accordingly:
function guard (to, from, next) {
if (localStorage.getItem('jwt')) {
if (localStorage.getItem('datacollector')) {
if (to.path != '/datacollector' && to.path != '/logout'){
next('/datacollector');
} else {
next();
}
} else {
next();
}
} else {
next('/login')
}
}

Page routing doesn't work properly with Angular 8

I am trying to redirect after login is successful. But it works sometimes , it doesn't work sometimes.
I couldn't understand why it is happening like this.
I'm using metronic theme.
So here is my codes:
Auth module rotes:
const routes: Routes = [
{
path: '',
component: AuthComponent,
children: [
{
path: '',
redirectTo: 'login',
pathMatch: 'full'
},
{
path: 'login',
component: LoginComponent,
data: {returnUrl: window.location.pathname}
},
{
path: 'register',
component: RegisterComponent
},
{
path: 'forgot-password',
component: ForgotPasswordComponent,
}
]
}
];
App module routes:
const routes: Routes = [
{path: 'auth', loadChildren: () => import('app/views/pages/auth/auth.module').then(m => m.AuthModule)},
{
path: '',
component: BaseComponent,
canActivate: [AuthGuard],
children: [
{
path: 'dashboard',
loadChildren: () => import('app/views/pages/dashboard/dashboard.module').then(m => m.DashboardModule),
},
{
path: 'po-admin',
loadChildren: () => import('app/views/pages/po-admin/po-admin.module').then(m => m.POAdminModule),
},
{
path: 'mail',
loadChildren: () => import('app/views/pages/apps/mail/mail.module').then(m => m.MailModule),
},
{
path: 'ecommerce',
loadChildren: () => import('app/views/pages/apps/e-commerce/e-commerce.module').then(m => m.ECommerceModule),
},
{
path: 'ngbootstrap',
loadChildren: () => import('app/views/pages/ngbootstrap/ngbootstrap.module').then(m => m.NgbootstrapModule),
},
{
path: 'material',
loadChildren: () => import('app/views/pages/material/material.module').then(m => m.MaterialModule),
},
{
path: 'wizard',
loadChildren: () => import('app/views/pages/wizard/wizard.module').then(m => m.WizardModule),
},
{
path: 'error/403',
component: ErrorPageComponent,
data: {
type: 'error-v6',
code: 403,
title: '403... Access forbidden',
desc: 'Looks like you don\'t have permission to access for requested page.<br> Please, contact administrator',
},
},
{path: 'error/:type', component: ErrorPageComponent},
{path: '', redirectTo: 'dashboard', pathMatch: 'full'},
{path: '**', redirectTo: 'dashboard', pathMatch: 'full'},
],
},
{path: '**', redirectTo: 'error/403', pathMatch: 'full'},
];
Auth module ->login component oninit and submit methods
ngOnInit(): void {
this.initLoginForm();
// redirect back to the returnUrl before login
this.route.queryParams.subscribe(params => {
this.returnUrl = params.returnUrl || '/';
});
}
submit() {
const controls = this.loginForm.controls;
/** check form */
if (this.loginForm.invalid) {
Object.keys(controls).forEach(controlName =>
controls[controlName].markAsTouched()
);
return;
}
this.loading = true;
const authData = {
email: controls.email.value,
password: controls.password.value,
};
this.auth
.login(authData.email, authData.password)
.pipe(
tap(user => {
if (user) {
debugger;
localStorage.setItem('currentUser', JSON.stringify(user));
if(this.rememberMeChecked){
localStorage.setItem('remember_me_email', user.email);
}else{
localStorage.removeItem('remember_me_email');
}
this.router.navigate([this.returnUrl]);; // Main page
} else {
this.authNoticeService.setNotice(this.translate.instant('AUTH.VALIDATION.INVALID_LOGIN'), 'danger');
}
}),
takeUntil(this.unsubscribe),
finalize(() => {
this.loading = false;
this.cdr.markForCheck();
})
)
.subscribe();
}
And auth guard:
#Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
constructor(
private router: Router,
private auth: AuthService
) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const currentUser = this.auth.currentUserValue;
if (currentUser) {
// logged in then return true
return true;
}
this.router.navigate(['/auth/login'], { queryParams: { returnUrl: state.url } });
return false;
}
}
When user click from profile auth service call logout method:
logout() {
// remove user from local storage to log user out
localStorage.removeItem('currentUser');
this.currentUserSubject.next(null);
}
So what should be case for this?
I appreciate any helps.
Thanks.
I found the case.
Its about the auth guard currentUser value. I forgot to set after auth service login impletemtation.I set the next value of currentUserSubjectValue after get the user:
this.currentUserSubject.next(user);
So I did like this then it has been solved:
login(email: string, password: string): Observable<User> {
debugger;
if (!email || !password) {
return of(null);
}
return this.getAllUsers().pipe(
map((result: User[]) => {
if (result.length <= 0) {
return null;
}
const user = find(result, (item: User) => {
return (item.email.toLowerCase() === email.toLowerCase() && item.password === password);
});
if (!user) {
return null;
}
user.password = undefined;
localStorage.setItem('currentUser', JSON.stringify(user));
this.currentUserSubject.next(user);
return user;
})
);

Vue.js, Send a value from component to router/index.js

I am trying to send a JWT token value from the Login.vue component and to validate it in the router/index.js before I redirect the user for the next page.
Login.vue:
<script>
import axios from "axios";
export default {
name: "Login",
methods: {
login() {
let userDetails = {
'Username' : this.email,
'Password' : this.password
}
axios
.post('https://localhost:44349/api/Token/CreateToken',userDetails)
.then((response) => {
this.token = response.data.token
this.$router.push({
name:'Books',
params:{token:this.token}}) // <--- send Token
})
.catch((error) => {
console.log(error)
});
}
}
};
</script>
router/index.js
const router = new Router({
routes: [
{
path: '/books',
name: 'books',
component: Books,
props: true,
meta: {
requiresAuth: true
}
},
{
path: '/login',
name: 'Login',
component: Login
}
]
});
router.beforeEach((to, form, next) => {
if (to.matched.some(rec => rec.meta.requiresAuth)) {
let isTokenValid= ??; // how to get the token?
if (isTokenValid) {
next();
}
else {
next({ name: 'Login' });
}
}
});
export default router
I used localstorage to store token. Alternatively you can use vuex for storage, do follow this Authentication using Vuex
.then((response) => {
this.token = response.data.token
localStorage.setItem('user-token', this.token);
this.$router.push({
name:'Books',
}) // <--- send Token
}).catch(){
localStorage.removeItem('user-token');
}

How to stop component-loading and redirect in Vue?

I have a component which is not to be accessed by non-logged-in user. I implemented this logic into the beforeCreate hook. Problem is that this doesn't stop the component from continuing in loading, which I want it to.
This is my code:
<script>
export default {
beforeCreate: function () {
if (this.$root.auth.user === null) {
this.$router.push({ name: 'auth.login' })
}
},
mounted: function () {
// some code that SHOULD NOT be processed
// if the user isn't authenticated
}
}
</script>
What am I doing wrong?
You should move your beforeCreate function to the router itself.
Here's my Auth catch
router.beforeEach(
(to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
// if route requires auth and user isn't authenticated
if (!store.state.Authentication.authenticated) {
let query = to.fullPath.match(/^\/$/) ? {} : { redirect: to.fullPath }
next(
{
path: '/login',
query: query
}
)
return
}
}
next()
}
)
It allows me to use my routes definition to handle auth and guest placements.
{
path: '/',
component: load('Template'),
children: [
{ path: '', component: load('Dashboard'), name: 'Dashboard' }
],
meta: { requiresAuth: true }
},
{
path: '/login',
component: load('Authentication/Login'),
name: 'login'
},
By having it in the router it's called before the components are initialized by Vue, this will stop processing of any component level events.

Categories