Vue3 + Firebase Authentication (onAuthStateChanged) Problem - firebase

I'm working on vue3 and firebase. I'm having a problem with the onAuthStateChanged.
If I make all the router without the children it works but, if I add /admin and then put the children routes there I cannot stay logged in anymore.
This is my main.js file:
// main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import { auth } from './plugins/firebase'
import { onAuthStateChanged } from 'firebase/auth'
import App from './App.vue'
import router from './router'
import './assets/styles/index.css'
import { useAuthenticationStore } from './stores/authentication'
let newApp
onAuthStateChanged(auth, (user) => {
if (!newApp) {
newApp = createApp(App)
newApp.use(createPinia())
newApp.use(router)
const authStore = useAuthenticationStore()
authStore.user = user
newApp.mount('#app')
}
})
This is my router. You can see that I need to validate if the user is logged in inside /admin
//router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import { useAuthenticationStore } from '../stores/authentication'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: () => import('../views/PublicView.vue'),
children: []
},
{
path: '/admin',
name: 'admin',
children: [
{
path: '',
name: 'dashboard',
component: () => import('../views/admin/HomeView.vue'),
meta: {
requiresAuth: true,
layout: 'PrivateLayout'
}
},
{
path: 'login',
name: 'login',
component: () =>
import('../views/admin/authentication/LoginView.vue'),
meta: {
requiresAuth: false,
layout: 'AuthenticationLayout'
}
},
{
path: 'password',
children: [
{
path: 'recovery',
name: 'password-recovery',
component: () =>
import(
'../views/admin/authentication/PasswordRecoveryView.vue'
),
meta: {
requiresAuth: false,
layout: 'AuthenticationLayout'
}
},
{
path: 'reset',
name: 'password-reset',
component: () =>
import('../views/admin/authentication/PasswordResetView.vue'),
meta: {
requiresAuth: false,
layout: 'AuthenticationLayout'
}
}
]
}
]
},
{
path: '/404',
component: () => import('../views/NotFound.vue')
},
{ path: '/:catchAll(.*)', redirect: '/404' }
]
})
router.beforeEach((to, from, next) => {
const authenticationStore = useAuthenticationStore()
const requiresAuth = to.matched.some((record) => record.meta.requiresAuth)
const isAuthenticated = authenticationStore.user !== null
if (to.path === '/admin/login' && isAuthenticated) {
next('/admin/dashboard')
}
if (requiresAuth && !isAuthenticated) {
next('/admin/login')
}
next()
})
export default router
//plugins/firebase.js
import { initializeApp } from 'firebase/app'
import { getAuth } from 'firebase/auth'
// Firebase configuration
const firebaseConfig = {
apiKey: import.meta.env.VITE_API_KEY,
authDomain: import.meta.env.VITE_AUTH_DOMAIN,
projectId: import.meta.env.VITE_PROJECT_ID,
storageBucket: import.meta.env.VITE_STORAGE_BUCKET,
messagingSenderId: import.meta.env.VITE_MESSAGING_SENDER_ID,
appId: import.meta.env.VITE_APP_ID,
measurementId: import.meta.env.VITE_MEASUREMENT_ID
}
// Initalize Firebase
const firebaseApp = initializeApp(firebaseConfig)
const auth = getAuth(firebaseApp)
export { auth }

Related

Laravel API route doesnt work on vue3 vue-router

Hey everyone laravel 9 vue 3 and vue-router the problem is that localhost:8000/api/admin/login is not available. Transition opens non-existent vue components page Can you suggest how to exclude api prefix from vue router.
Backend: Laravel 9
PHP: 8.0
Fronted: Vue3,axios, vue-routera
web.php
Route::get('{any?}', function () {
return view('welcome');
})->where('any', '.*');
api.php
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\API\Admin\UserController;
Route::any('login', [UserController::class, 'login']);
Route::any('register', [UserController::class, 'register']);
Route::group(['middleware' => 'auth:api'], function () {
Route::get('logout', [UserController::class, 'logout']);
Route::get('user', [UserController::class, 'user']);
});
app.js
import {createApp} from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import axios from 'axios'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(ElementPlus)
app.use(router, axios)
app.config.globalProperties.$axios = axios
app.mount("#app")
router.js
import { createRouter, createWebHistory } from 'vue-router';
import Home from './components/Main/HomeComponent.vue';
import Login from './components/Main/LoginComponent.vue';
import Dashboard from './components/Main/DashboardComponent.vue';
const routes = [
{
path: '/',
name: 'home',
component: Home,
meta: {
requiresAuth: false,
title: 'Home',
}
},
{
path: '/login',
name: 'login',
component: Login,
meta: {
requiresAuth: false,
title: 'Login',
}
},
{
path: '/dashboard',
name: 'dashboard',
component: Dashboard,
meta: {
requiresAuth: true,
title: 'Dashboard',
}
}
]
const router = createRouter({
history: createWebHistory(),
mode: 'history',
routes,
linkExactActiveClass: 'active'
});
// eslint-disable-next-line no-unused-vars
router.beforeEach((to, from) => {
document.title = to.meta.title;
if (to.meta.requiresAuth === true && !this.$store.authenticated) {
return this.$router.push('/login');
}
})
export default router
In the end, calling api route.php worked correctly

can not call action in Pinia option API

I am using Vue3 option API and Pinia .
I want to call an action in Pinia option Api from component
component
import { mapActions } from "pinia";
import { useTableStore } from "../../../stores/table";
export default {
name: "LoggingForm",
data() {
return {
login: {
username: "",
password: "",
serverhost: "",
},
};
},
methods: {
submit(){
this.getData(this.login)
}
},
computed: {
...mapActions(useTableStore, ["getData"]),
},
};
and this is store/table.js
import { defineStore } from 'pinia'
import authService from "#/api/auth.js";
export const useTableStore = defineStore({
id: 'table',
state: () => ({
table: []
}),
getters: {
headers: (state) => state.table[0],
body: (state) => state.table.slice(1)
},
actions: {
async getData1(data) {
// do something
}
},
}
})
But I get this error
I can Use state and getters perfectly Just action don't work !
what's the problem ?
Here is what you need
https://pinia.vuejs.org/core-concepts/actions.html#without-setup
In short:
computed => mapGetters
methods => mapActions
You are using mapActions with computed so that will not work

How to write AuthGuard in Ionic/Angular with AngularFire7 that works on iOS?

I'm running into an odd problem with my Ionic/angular app.
I'm using
"#angular/core": "~13.2.2"
"#capacitor/core": "3.4.0"
"#capacitor/ios": "3.4.0"
"#angular/fire": "^7.2.0"
"firebase": "^9.6.6",
It works well on the web-version and Android, but when I compile to iOS, I only get a blank screen. I've narrowed the problem down to the auth-guard
I have tried using
the #angular/fire/compat/authguard - blank screen
not using any guard - works fine
writing my own guard and always returning true - works fine
writing my own guard, code below - blank screen
Here are the app-routing.module.ts:
import { NgModule } from '#angular/core';
import { RouterModule, Routes } from '#angular/router';
import { AuthGuard } from '../guards/auth.guard';
import { AutoLoginGuard } from '../guards/auto-login.guard';
import { TabsPage } from './tabs.page';
const routes: Routes = [
{
path: '',
component: TabsPage,
children: [
{
path: '',
redirectTo: 'dashboard',
pathMatch: 'full',
},
{
path: 'dashboard',
loadChildren: () =>
import('../pages/dashboard/dashboard.module').then(
(m) => m.DashboardPageModule
),
canActivate: [AuthGuard],
},
{
path: 'welcome',
loadChildren: () =>
import('../pages/welcome/welcome.module').then(
(m) => m.WelcomePageModule
),
canActivate: [AutoLoginGuard],
},
{
path: 'login',
loadChildren: () =>
import('../pages/login/login.module').then((m) => m.LoginPageModule),
canActivate: [AutoLoginGuard],
},
],
},
];
#NgModule({
imports: [RouterModule.forChild(routes)],
})
export class TabsPageRoutingModule {}
and the auth.guard.ts
import { Injectable } from '#angular/core';
import { CanActivate, Router } from '#angular/router';
import { Auth, onAuthStateChanged } from '#angular/fire/auth';
#Injectable({
providedIn: 'root',
})
export class AuthGuard implements CanActivate {
constructor(private auth: Auth, private router: Router) {}
canActivate(): Promise<boolean> {
return new Promise(async (resolve, reject) => {
onAuthStateChanged(this.auth, async (user) => {
if (user) {
console.log('User authenticated');
resolve(true);
} else {
console.log('User redirected to login');
this.router.navigateByUrl('/', { replaceUrl: true });
reject(false);
}
});
});
}
}
Any idea what might be causing this issue?
Found the answer (or rather Simon Grimm did): it's a bug in the capacitor SDK.
When initializing the auth module you have to adjust for Capacitor:
app.module.ts:
import { Capacitor } from '#capacitor/core';
import { indexedDBLocalPersistence, initializeAuth } from 'firebase/auth';
import { getApp } from 'firebase/app';
...
imports: [ ...
provideAuth(() => {
if (Capacitor.isNativePlatform()) {
return initializeAuth(getApp(), {
persistence: indexedDBLocalPersistence,
});
} else {
return getAuth();
}
}),
...
]

React Hook useEffect has a missing dependency: 'dispatch'. Either include it or remove the dependency array react-hooks/exhausCompiled with warnings

Error while dispatching an action!
React Hook useEffect has a missing dependency: 'dispatch'. Either include it or remove the dependency array react-hooks/exhausCompiled with warnings.
App.js :-
function App() {
const user = null;
const dispatch = useDispatch();
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(userAuth => {
if (userAuth) {
dispatch(login({
uid: userAuth.uid,
email: userAuth.email
}));
} else {
dispatch(logout);
}
})
return unsubscribe;
}, []);
userSlice.js :-
export const userSlice = createSlice({
name: 'user',
initialState: {
user: null,
},
reducers: {
login: (state, action) => {
state.user = action.payload;
}
},
logout: (state) => {
state.user = null;
}
});
export const { login, logout } = userSlice.actions;
store.js :-
import { configureStore } from '#reduxjs/toolkit';
import userReducer from '../features/userSlice';
export const store = configureStore({
reducer: {
user: userReducer,
},
});

Role-Based Authentication vue js firebase

I'm use VueJS with Firebase and I have doctors, admins, patients. Patient users cannot access the doctor's router. I followed the source code here
https://github.com/softauthor/vuejs-firebase-role-based-auth?files=1
I can't get an error message but the patient can access the router doctor. is there anyone who can give me a solution for this
I corrected it so it doesn't work either
//router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import firebase from 'firebase'
import Login from '#/views/Login'
import Register from '#/views/Register'
import Admin from '#/views/Admin'
import Driver from '#/views/Doctor'
import Customer from '#/views/Patient'
import Home from '#/views/Home'
Vue.use(Router)
let router = new Router({
routes: [
{
path: '/',
name: 'home',
component: Home,
meta: {
guest: true
}
},
{
path: '/register',
name: 'register',
component: Register,
meta: {
guest: true
}
},
{
path: '/login',
name: 'login',
component: Login,
meta: {
guest: true
}
},
{
path: '/admin',
name: 'admin',
component: Admin,
meta: {
auth: true
}
},
{
path: '/doctor',
name: 'doctor',
component: Doctor,
meta: {
auth: true
}
},
{
path: '/patient',
name: 'patient',
component: Patient,
meta: {
auth: true
}
},
],
})
router.beforeEach((to, from, next) => {
firebase.auth().onAuthStateChanged(userAuth => {
if (userAuth) {
firebase.auth().currentUser.getIdTokenResult()
.then(then((idTokenResult) =>
{
if (!!idTokenResult.claims.patient) {
if (to.path !== '/patient')
return next({
path: '/patient',
})
} else if (!!idTokenResult.claims.admin) {
if (to.path !== '/admin')
return next({
path: '/admin',
})
} else if (!!idTokenResult.claims.driver) {
if (to.path !== '/doctor')
return next({
path: '/doctor',
})
}
})
} else {
if (to.matched.some(record => record.meta.auth)) {
next({
path: '/login',
query: {
redirect: to.fullPath
}
})
} else {
next()
}
}
})
next()
})
export default router
//functions/index.js
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp()
exports.AddUserRole = functions.auth.user().onCreate(async (authUser) => {
if (authUser.email) {
const customClaims = {
customer: true,
};
try {
var _ = await admin.auth().setCustomUserClaims(authUser.uid, customClaims)
return admin.firestore().collection("roles").doc(authUser.uid).set({
email: authUser.email,
role: customClaims
})
} catch (error) {
console.log(error)
}
}
});
exports.setUserRole = functions.https.onCall(async (data, context) => {
if (!context.auth.token.admin) return
try {
var _ = await admin.auth().setCustomUserClaims(data.uid, data.role)
return admin.firestore().collection("roles").doc(data.uid).update({
role: data.role
})
} catch (error) {
console.log(error)
}
});
firebase.auth().onAuthStateChanged is asynchronous, so next() at the end of your router guard gets invoked without waiting for firebase.auth().onAuthStateChanged to resolve, meaning your router guard lets everyone through.

Resources