I use Nuxt (universal mode) with Firestore, I set rules properly but I still get this error:
I get Missing or insufficient permissions
when I reload the page. When ccessing the page through the router, I don't get any error, the rules re working as it's supposed to.
Firestore is called in the asyncData (as it's a SSR app).
If I check the req.user in asynData when loading the page, everything looks fine. I have the roles and all information. It seems that serverMiddleware is activated after the asyncData, when reloading the browser. Any ideas why?
My Firestore rules:
service cloud.firestore {
match /databases/{database}/documents {
function isApplicant() {
return request.auth.token.roles.applicant == true
}
match /tests/{test} {
allow read: if isApplicant();
}
}
}
serverMiddleware:
const admin = require('../services/firebase-admin-init.js')
const cookieParser = require('cookie-parser')();
global.XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest
module.exports = async function (req, res, next) {
await getIdTokenFromRequest(req, res).then(idToken => {
if (idToken) {
addDecodedIdTokenToRequest(idToken, req).then(() => {
next();
});
} else {
next();
}
});
}
function getIdTokenFromRequest(req, res) {
if (req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) {
return Promise.resolve(req.headers.authorization.split('Bearer ')[1]);
}
return new Promise(function(resolve) {
cookieParser(req, res, () => {
if (req.cookies && req.cookies.__session) {
console.log("new")
console.log('Found "__session" cookie');
// Read the ID Token from cookie.
resolve(req.cookies.__session);
} else {
resolve();
}
});
});
}
/**
* Returns a Promise with the Decoded ID Token and adds it to req.user.
*/
function addDecodedIdTokenToRequest(idToken, req) {
// console.log('Start addDecodedIdTokenToRequest')
return admin.auth().verifyIdToken(idToken).then(decodedIdToken => {
console.log('ID Token correctly decoded', decodedIdToken);
req.user = decodedIdToken;
}).catch(error => {
console.error('Error while verifying Firebase ID token:', error);
});
}
Nuxt config files:
const path = require('path');
const environment = process.env.NODE_ENV || 'development';
const envSet = require(`./config/env.${environment}.js`);
module.exports = {
env: envSet,
mode: 'universal',
plugins: [
{ src: '~/plugins/fireinit.js', ssr: false, },
{ src: '~/plugins/auth-cookie.js', ssr: false },
{ src: '~/plugins/fireauth.js', ssr: false }
],
router: {
},
serverMiddleware: [
'~/serverMiddleware/validateFirebaseIdToken'
],
modules: [
'vue-scrollto/nuxt',
['nuxt-validate', {
lang: 'ja'
}],
'#nuxtjs/axios',
],
css: [
{src: '~/assets/scss/app.scss', lang: 'scss'}
],
/*
** Customize the progress bar color
*/
loading: { color: '#403a8f' },
resolve: {
extensions: ['.js', '.json', '.vue', '.ts'],
root: path.resolve(__dirname),
alias: {
'~': path.resolve(__dirname),
'#': path.resolve(__dirname)
},
},
/*
** Build configuration
*/
buildDir: '../cloud/front/',
build: {
publicPath: '/assets/',
extractCSS: true,
quiet: false,
terser: {
terserOptions: {
compress: {
drop_console: environment === 'production' ? true : false,
},
},
},
},
}
Related
The validate function for basic
await server.register(require('#hapi/basic'));
const validate = async (request, email, password, id_customer) => {
console.log(request)
if (!email || !password || !id_customer) {
return { credentials: null, isValid: false };
}
const results = await getHash(id_customer);
if (results.length == 0) {
return { credentials: null, isValid: false };
}
if (bcrypt.compareSync(password, results[0]['passwd'])) {
const credentials = { id: id_customer, email: email };
return { isValid: true, credentials };
}
return { credentials: null, isValid: false };
};
server.auth.strategy('simple', 'basic', { validate });
Route example :
{
method: 'POST',
path: '/home/getCategories',
config: {
auth: 'simple',
description: 'Get Home',
payload: {
multipart: true
},
handler: Home.getCategories
},
/* options: {
auth: 'simple'
},*/
//handler: Home.getCategories
},
Here is the axios call from the App :
axios.post('https://api.domain.com/home/getCategories', {
code: code
},
{
headers: {
'email': email,
'password': password,
'id_customer': id_customer
},
})
When I do the call I got a 401 unauthorized but I cant see the output of 'console.log(request)'
Any help ?
Have you tried the following? What version of Hapi.js are you using?
const categoryPostValidation = {
payload: Joi.object({
name: Joi.string().label("Name").min(1).max(30).error((errors) => new Error('Name is invalid, and must be 1 to 30 characters in length')).required(),
description: Joi.string().label("Description").min(1).max(255).error((errors) => new Error('Description is invalid, and must be 1 to 255 characters in length')).required()
}),
failAction: async (request, h, err) => {
throw err;
}
};
const categoryPostRouteOptions = {
description: "Posts one category.",
cors: true,
payload: {
output: 'data', // These are default options
parse: true // These are default options
},
auth: {
mode: 'required' // or 'try', etc
strategy: 'simple'
},
validate: categoryPostValidation,
handler: Home.getCategories
};
{
method: 'POST',
path: '/home/getCategories',
options: categoryPostRouteOptions
},
I have a Nuxt app in which everything works fine in middleware except when I use redirect.
When I comment the redirect('/admin') line it works fine even the state data is present when console logged. As soon as I uncomment the redirect line it makes the state null.
Please help if someone knows this issue. This exact code works in my other projects but not here.
This is my auth.js file in the middleware folder.
export default function ({ store, route, redirect }) {
const user = store.getters['user/user']
const blockRouteAdmin = /\/admin\/*/g
const blockRouteManager = /\/manager\/*/g
const path = ['/signup', '/login']
let value = path.includes(route.path)
if (user) {
if (user.isAdmin) {
if (!route.path.match(blockRouteAdmin)) {
redirect('/admin')
}
}
if (user.isManager) {
if (!route.path.match(blockRouteManager)) {
redirect('/manager')
}
}
if (user.isUser) {
if (
route.path.match(blockRouteAdmin) ||
route.path.match(blockRouteManager) ||
value
) {
console.log('isUser', user.isUser)
redirect('/')
}
}
}
if (!user) {
if (
route.path.match(blockRouteAdmin) ||
route.path.match(blockRouteManager)
) {
redirect('/')
} else {
redirect()
}
}
}
Here is my nuxt.config.js
export default {
// Target: https://go.nuxtjs.dev/config-target
target: 'static',
// Global page headers: https://go.nuxtjs.dev/config-head
head: {
title: 'aitl',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: '' },
{ name: 'format-detection', content: 'telephone=no' },
],
link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }],
},
// Global CSS: https://go.nuxtjs.dev/config-css
css: [],
// Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
plugins: ['~/plugins/firebaseConfig.js'],
// Auto import components: https://go.nuxtjs.dev/config-components
components: true,
// Modules for dev and build (recommended): https://go.nuxtjs.dev/config-modules
buildModules: [],
// Modules: https://go.nuxtjs.dev/config-modules
modules: [
// https://go.nuxtjs.dev/buefy
'nuxt-buefy',
// https://go.nuxtjs.dev/pwa
'#nuxtjs/pwa',
// https://go.nuxtjs.dev/content
'#nuxt/content',
],
// PWA module configuration: https://go.nuxtjs.dev/pwa
pwa: {
manifest: {
lang: 'en',
},
},
// Content module configuration: https://go.nuxtjs.dev/config-content
content: {},
// Build Configuration: https://go.nuxtjs.dev/config-build
build: {},
}
My index.js inside store.
import { vuexfireMutations } from 'vuexfire'
import { getUserFromCookie } from '../helper/index.js'
export const mutations = {
...vuexfireMutations,
}
export const actions = {
async nuxtServerInit({ dispatch, commit }, { req }) {
try {
const user = getUserFromCookie(req)
if (user) {
await dispatch('user/setUSER', {
email: user.email,
isAdmin: user.admin,
isManager: user.manager,
isUser: user.user,
uid: user.user_id,
name: user.name,
})
}
} catch (err) {
console.log(err)
}
},
}
User.js in store folder
import { auth } from '../plugins/firebaseConfig'
import Cookies from 'js-cookie'
export const state = () => ({
user: null,
})
export const getters = {
user(state) {
return state.user
},
}
export const actions = {
async userlogin({ dispatch }, user) {
try {
const token = await auth.currentUser.getIdToken(true)
const userInfo = {
email: user.email,
isAdmin: user.admin,
isManager: user.manager,
isUser: user.user,
uid: user.uid,
name: user.displayName,
}
Cookies.set('access_token', token)
await dispatch('setUSER', userInfo)
} catch (err) {
console.log(err)
}
},
setUSER({ commit }, user) {
commit('setUSER', user)
},
}
export const mutations = {
setUSER(state, user) {
state.user = user
},
}
The issue was solved by going from target: 'static' to target: 'server', aka mirroring the settings of another working project.
I'm following Microsoft's Tutorial:
Deploy static-rendered Next.js websites on Azure Static Web Apps
The problem is, I'm trying to add to my next.config.js file this code:
const data = require('./utils/projectsData');
module.exports = {
trailingSlash: true,
exportPathMap: async function () {
const { projects } = data;
const paths = {
'/': { page: '/' },
};
projects.forEach((project) => {
paths[`/project/${project.slug}`] = {
page: '/project/[path]',
query: { path: project.slug },
};
});
return paths;
},
};
but my next.config.js already has some existing content:
require('dotenv').config()
const withFonts = require('next-fonts')
module.exports = withFonts({
serverRuntimeConfig: {},
trailingSlash: true,
exportPathMap: function() {
return {
'/': { page: '/' }
};
},
publicRuntimeConfig: {
API_URL: process.env.API_URL,
PORT: process.env.PORT || 3000,
PUBLISHABLE_KEY: process.env.PUBLISHABLE_KEY,
},
})
how can I combine them?
Just combine them like that?
require('dotenv').config()
const data = require('./utils/projectsData'); // Add this line
const withFonts = require('next-fonts')
module.exports = withFonts({
serverRuntimeConfig: {},
trailingSlash: true,
// And override this key
exportPathMap: async function () {
const { projects } = data;
const paths = {
'/': { page: '/' },
};
projects.forEach((project) => {
paths[`/project/${project.slug}`] = {
page: '/project/[path]',
query: { path: project.slug },
};
});
return paths;
},
publicRuntimeConfig: {
API_URL: process.env.API_URL,
PORT: process.env.PORT || 3000,
PUBLISHABLE_KEY: process.env.PUBLISHABLE_KEY,
},
})
I'd like to build a Nuxt.js App, in this case, I'm using dynamic routes that to be generated by using config.
Well, I got an issue when I was trying to generate my web page using Nuxt & Firebase.
Here are my Nuxt Config JS code :
import * as firebase from 'firebase';
import 'firebase/auth';
import 'firebase/database';
var firebaseConfig = {
apiKey: "AIzaSyAOX6yNHPzWHWd30GnDagwlhgGv9iP8kLs",
authDomain: "musthofa-lapor.firebaseapp.com",
databaseURL: "https://musthofa-lapor.firebaseio.com",
projectId: "musthofa-lapor",
storageBucket: "musthofa-lapor.appspot.com",
messagingSenderId: "653288691711",
appId: "1:653288691711:web:e49daf72720bf99dc5f9ca",
measurementId: "G-0KW7CGZHL3"
};
var app = firebase.initializeApp(firebaseConfig);
var dbx = app.database();
export default {
mode: 'universal',
/*
** Headers of the page
*/
head: {
title: process.env.npm_package_name || '',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: process.env.npm_package_description || '' }
],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
],
script:[
{ src:'https://www.gstatic.com/firebasejs/7.2.3/firebase-app.js' },
{ src:'https://www.gstatic.com/firebasejs/7.2.3/firebase-auth.js' },
{ src:'https://www.gstatic.com/firebasejs/7.2.3/firebase-database.js' },
{ src:'https://www.gstatic.com/firebasejs/7.2.3/firebase-storage.js' },
]
},
/*
** Customize the progress-bar color
*/
loading: { color: '#fff' },
/*
** Global CSS
*/
css: [
],
/*
** Plugins to load before mounting the App
*/
plugins: [
],
/*
** Nuxt.js dev-modules
*/
buildModules: [
],
/*
** Nuxt.js modules
*/
modules: [
],
/*
** Build configuration
*/
build: {
/*
** You can extend webpack config here
*/
extend (config, { isDev, isClient }) {
if (isDev && isClient) {
config.module.rules.push({
enforce: 'pre',
test: /\.(js|vue)$/,
loader: 'eslint-loader',
exclude: /(node_modules)/
})
}
}
},
generate:{
routes(){
return dbx.ref('aspirasi').once("value",function(snap){
snap.forEach(function(snapshot){
var this_val = snapshot.val();
return {
route: '/admin/balas/' + this_val.id
}
})
})
}
}
}
It generated error as follows :
ERROR undefined 06:38:45
TypeError: Cannot read property '_normalized' of undefined
at normalizeLocation (/Volumes/DAKSA-HDD/PROJECTS/PRANANDA/MUSTHOFA LAPOR RAKYAT/PROJECTS/WEBSITE/MAIN/musthofa-web/node_modules/vue-router/dist/vue-router.common.js:1297:12)
at VueRouter.resolve (/Volumes/DAKSA-HDD/PROJECTS/PRANANDA/MUSTHOFA LAPOR RAKYAT/PROJECTS/WEBSITE/MAIN/musthofa-web/node_modules/vue-router/dist/vue-router.common.js:2627:18)
at st (server.js:1:31205)
at async e.default (server.js:1:32623)
Any helps will be appreciated. Thank you so much.
Best Regards
This is caused by returning null or undefined from the routes method, and indeed that is what's happening. There's 2 issues with your code:
You are not returning a value from your callback function.
Using return in a forEach does not return the value to the caller. forEach has a return type of undefined, so you should use map instead.
routes(){
return dbx.ref('aspirasi').once("value",function(snap){
return snap.map(function(snapshot){
var this_val = snapshot.val();
return {
route: '/admin/balas/' + this_val.id
}
})
})
}
Bonus: Your code could be cleaned up a lot by using ES6 syntax and async/await:
async routes() {
const snapshot = await dbx.ref("aspirasi").once("value")
return snapshot.map(snap => ({ route: "/admin/balas/" + snap.val().id }))
}
Currently when I reload dashboard first its redirect to /login then /dashboard if user already login. Its look quite wired. How Can I fix so that its land directly to /dashboard if user logged in.
Created function in main.js
created: function() {
try {
firebase.initializeApp(firebaseConfig);
}
catch(error){
return;
}
const store = this.$store;
firebase.auth().onAuthStateChanged(function(user){
if(typeof user !== 'undefined' && user !== null){
store.dispatch('loginUserOnLoad', user);
}
});
}
LoginUserOnlOad action
loginUserOnLoad: function({ commit }, user){
commit('authUser',{
email: user.email,
fullname: 'Guest'
})
},
Here is complete router configuration,
Vue.use(Router);
const router = new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'Welcome',
component: Welcome
},
{
path: '/tasks',
name: 'Tasks',
component: Layout,
meta: {
requireAuth: true
}
},
{
path: '/login',
name: 'Login',
component: Signin,
meta: {
guestAuth: true
}
},
{
path: '/register',
name: 'Signup',
component: Signup,
meta: {
guestAuth: true
}
},
{
path: '*',
name: 'NotFound',
component: NotFound
}
]
});
router.beforeEach((to, from, next) => {
const currentUser = firebase.auth.currentUser;
const requireAuth = to.matched.some(record => record.meta.requireAuth);
if(requireAuth && !currentUser){
next({ name: 'Login'});
}
else if(!requireAuth && currentUser){
next({ name: 'Tasks'});
}
else {
next();
}
});
export default router;
I don't know how you configure and export Firebase, but I think that you should modify your router code as follows (see comments in the code):
router.beforeEach((to, from, next) => {
//Instead of const currentUser = firebase.auth.currentUser; do
const currentUser = firebase.auth().currentUser;
const requireAuth = to.matched.some(record => record.meta.requireAuth);
if(requireAuth && !currentUser){
next({ name: 'Login'});
}
else if(!requireAuth && currentUser){
next({ name: 'Tasks'});
}
else {
next();
}
});
I did a mistake to initialize vue instance, I found solution from https://medium.com/#anas.mammeri/vue-2-firebase-how-to-build-a-vue-app-with-firebase-authentication-system-in-15-minutes-fdce6f289c3c