Nuxt 3 can not get the URL hash inside middleware - nuxtjs3

I am trying to get the hash part from the URL but for some reason at the first load of the page, the middleware will not see the hash.
This is not the case with a reload or when using the navigateTo() method
export default defineNuxtRouteMiddleware(async (to) => {
console.log("Auth -- hash:", to.hash)
})

Got some answers from GitHub, check the conversation:
https://github.com/nuxt/framework/discussions/7505#discussioncomment-3644705
Solution
For getting the hash I just removed the middleware, since is impossible to get the browser hash values on the server. Instead, I changed to using a composable function and calling it from the page that received the URL inside an onMounted() hook.
<script setup lang="ts">
const route = useRoute();
onMounted(() => {
oauthCallback(route.hash) // composable function
})
</script>

Related

Nuxt middleware not triggered upon initial render

The following code is meant to check the role of the user.
The middleware runs everytime the site is reloaded are a new route is taken.
// Some nuxt middleware
import * as firebase from 'firebase/app'
import 'firebase/auth'
export default function ({ app, store, route, redirect }) {
app.router.beforeEach((to, from, next) => {
// For some reason, this does not load every time.
firebase.auth().onAuthStateChanged((userAuth) => {
if (userAuth) {
console.log(userAuth)
firebase
.auth()
.currentUser.getIdTokenResult()
.then(function ({ claims }) {
// some auth stuff
})
})
}
For some reason, if the site is reloaded this user auth function always returns null. This leads to that the rest of the functions fail due to the unknown user data / user roles.
firebase.auth().onAuthStateChanged((userAuth) => {...})
So my question is, why does the upper function return null when the site is reloaded?
ps. Everything works normal if a new route is taken, it only fails when site is reloaded.
beforeEach is a guard triggered when you navigate from a page to another page thanks to vue router, aka using <nuxt-link> or $router.push.
On the initial page load, there is no navigation because you're rendering the content generated by the server, not the client directly.
Definition of a middleware from Nuxt's documentation
Middleware lets you define custom functions that can be run before rendering either a page or a group of pages (layout).
Notice, before rendering. This means that a middleware will be run as your beforeEach and on initial render.
Hence, you can totally strip the router guard part and simply let the middleware as this
export default function ({ app, store, route, redirect }) {
firebase.auth().onAuthStateChanged((userAuth) => {
...

How to verify the request is coming from my application clientside?

I have an app where users can create posts. There is no login or user account needed! They submit content with a form as post request. The post request refers to my api endpoint. I also have some other api points which are fetching data.
My goal is to protect the api endpoints completely except some specific sites who are allowed to request the api ( I want to accomplish this by having domain name and a secure string in my database which will be asked for if its valid or not if you call the api). This seems good for me. But I also need to make sure that my own application is still able to call the api endpoints. And there is my big problem. I have no idea how to implement this and I didn't find anything good.
So the api endpoints should only be accessible for:
Next.js Application itself if somebody does the posting for example
some other selected domains which are getting credentials which are saved in my database.
Hopefully somebody has an idea.
I thought to maybe accomplish it by using env vars, read them in getinitalprops and reuse it in my post request (on the client side it can't be read) and on my api endpoint its readable again. Sadly it doesn't work as expected so I hope you have a smart idea/code example how to get this working without using any account/login strategy because in my case its not needed.
index.js
import Head from 'next/head'
import Image from 'next/image'
import styles from '../styles/Home.module.css'
export default function Home(props) {
async function post() {
console.log(process.env.MYSECRET)
const response = await fetch('/api/hello', {
method: 'POST',
body: JSON.stringify(process.env.MYSECRET),
})
if (!response.ok) {
console.log(response.statusText)
}
console.log(JSON.stringify(response))
return await response.json().then(s => {
console.log(s)
})
}
return (
<div className={styles.container}>
<button onClick={post}>Press me</button>
</div>
)
}
export async function getStaticProps(context) {
const myvar = process.env.MYSECRET
return {
props: { myvar },
}
}
api
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
export default function handler(req, res) {
const mysecret = req.body
res.status(200).json({ name: mysecret })
}
From what I understand, you want to create an API without user authentication and protect it from requests that are not coming from your client application.
First of all, I prefer to warn you, unless you only authorize requests coming from certain IPs (be careful with IP Spoofing methods which could bypass this protection), this will not be possible. If you set up an API key that is shared by all clients, reverse engineering or sniffing HTTP requests will retrieve that key and impersonate your application.
To my knowledge, there is no way to counter this apart from setting up a user authentication system.

Post request redux thunk

I have GET requests and normally when those succeeded I save data in store, but for POST requests I need to know if it succeeded or not, in order to execute some code (show a message and redirect), the docu says you can use an isLoading variable, but it just says if the service is working but not if it succeeded, if I try to create a new success variable in the store, it will be turned on forever after the request and I don't need that either. I tried returning a promise from the action creator and handle response directly inside the component but it looks like the same to call axios there instead of using redux.
My action creator looks like this:
export function createProject(userId, projectName) {
return function (dispatch) {
dispatch({ type: projectsActions.START_CREATE_PROJECT });
return ProjectsService.createProject(userId, projectName).then(() => {
dispatch({ type: projectsActions.SUCCESS_CREATE_PROJECT });
}).catch((error) => {
dispatch({ type: projectsActions.ERROR_CREATE_PROJECT });
throw error;
});
}
}
I understand where your doubts are coming from, it doesn't seem appropriate to have a field on your Redux store only to know the success of a one-time request.
If you only need to make a post request and only care about it's result once, the simplest way to do it is to use state in the component making the request. Component-level state is easily manageable and gets removed from memory when the component is unmounted, but on the other hand you may want to have a single source of truth for your app. You have to make a choice, but your Redux implementation is correct.

firebase serve: From a locally served app, call locally served functions

How can I properly simulate a cloud function locally so that it has all data as when being invoked on firebase servers? (e.g. the context.auth)
I am serving my project with firebase serve, it runs ok on http://localhost:5000/, however, my cloud functions are being called from https://us-central1-<my-app>.cloudfunctions.net/getUser. (The function is not even deployed.)
To avoid XY problem, I am trying to debug my function, but calling it from firebase shell results in context.auth being undefined, same when calling via postman from http://localhost:5000/<my-app>/us-central1/getUser.
This is my ./functions/src/index.ts file
import * as functions from 'firebase-functions'
import admin from 'firebase-admin'
import { inspect } from 'util'
admin.initializeApp()
export const getUser = functions.https.onCall((data, context) => {
console.debug('== getUser called =========================================')
console.log('getUser', inspect(data), inspect(context.auth))
return admin.database().ref('userRights/admin').child(context.auth.uid).once('value', snapshot => {
console.log(snapshot.val())
if (snapshot.val() === true) {
return 'OK'
// return {status: 'OK'}
} else {
return 'NOK'
// return {status: 'error', code: 401, message: 'Unauthorized'}
}
})
})
file ./firebase.functions.ts
import { functions } from '~/firebase'
export const getUser = functions.httpsCallable('getUser')
Consumer ./src/pages/AdminPanel/index.tsx
import { getUser } from '~/firebase.functions'
//...
getUser({myDataX: 'asd'}).then(response => console.debug('response', response))
UPDATE - April/2021
As of April/2021, method useFunctionsEmulator has been deprecated. It is suggested to use method useEmulator(host, port) instead.
Original post:
By default, firebase serve sends queries to CLOUD function instead of localhost, but it is possible to change it to to point to localhost.
#gregbkr found a workaround for that at this github thread.
You basically add this after firebase initialization script (firebase/init.js) in html head.
<script>
firebase.functions().useFunctionsEmulator("http://localhost:5001");
</script>
Make sure to REMOVE it when deploying to SERVER
There is currently no support for local testing of callable functions like this. The team is working on a way for you to specify the URL endpoint of a callable function so that you can redirect it to a different location for testing.
Just found a workaround.
using fiddlers AutoResponder to redirect the function call to the local served function.
step 1
copy the target url of the function from the client
step 2
copy the local served function url
step 3
active the auto respoder and use the following rules
(the second rule is also importent to allow all outher requests
That worked for me, thank you #GorvGoyl!
script src="/__/firebase/init.js?useEmulator=true"></script

How to access custom response values from a page script?

This might sound like a silly question but I have really tried everything I could to figure it out. I am creating a variable and adding it to my response object in custom Express server file like so:
server.get('*', (req, res) => {
res.locals.user = req.user || null;
handle(req, res);
});
Now I want to access this res.locals.user object from all of my pages, i.e. index.js, about.js, etc., in order to keep a tab on the active session's user credentials. It's got to be possible some way, right?
P.S.: Reading some thread on the NextJS Github page, I tried accessing it from my props object as this.props.user but it keeps returning null even when a server-side console.log shows non-null values.
The res object is available on the server as a parameter to getInitialProps. So, with the server code you have above, you can do
static async getInitialProps({res}) {
return { user: res.locals.user }
}
to make it available as this.props.user.

Resources