Firebase createUserWithEmailAndPassword not doing anything in Next.js - javascript

I am trying to use Firebase Authentication on my Next.js website. For some reason, when I press the sign up button, nothing happens and no errors are logged. It just refreshes the page. It doesn't even set any cookies or create a user.
Here are my two files related to authentication:
utils/authProvider.js:
import firebase from 'firebase/app';
import 'firebase/auth';
if(!firebase.apps.length) {
firebase.initializeApp({
// config
});
}
const auth = firebase.auth();
module.exports = { auth };
pages/signup.js:
import { useState } from 'react'
import { LockClosedIcon } from '#heroicons/react/solid'
import { auth } from '../utils/authProvider'
export default function CreateAccount() {
const [emailField, setEmailField] = useState('');
const [passwordField, setPasswordField] = useState('');
// emailField and passwordField are set correctly, I used console.log to test it
const createAccount = () => {
// this event does get triggered, I used console.log to test it
auth.createUserWithEmailAndPassword(emailField, passwordField)
.then((userCredential) => {
window.location.replace('/');
console.log('logged in!');
}).catch((error) => {
console.error(error);
});
}
Edit: After experimenting some more, I saw that the request to https://www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser never gets completed. It just shows as red in the network traffic tab of Chrome dev tools. Any reason why this might happen?
Thanks in advance.

Related

React package / solution to "stealed" token

I'm currently working on authentication of an application in the frontend. I don't have access to the backend.
User state is managed via redux and the authentication requires a token.
While testing I notice that when I'm logged in and via Postman (for example) I authenticate the user the app return an unidentified state. I am still able to navigate the private routes but I can not see the data I fetch from the backend.
Here is how I am managing the logged / !logged state for the private route.
I wonder if I am getting something wrong and if not, if its possible to overcome this issue of potentially "stealed" token... Imagine 2 person working using the same username/password... (wrong, but technically possible)
import { useState, useEffect } from 'react'
import store from '../store'
export const useAuthStatus = () => {
const [loggedIn, setLoggedIn] = useState(false)
const [checkingStatus, setCheckingStatus] = useState(true)
const user = store.getState().userLogin
const status = user.userDetails.status
useEffect(() => {
if (status === "ok") {
setLoggedIn(true)
} else {
setLoggedIn(false)
}
setCheckingStatus(false)
}, [status, user])
return { loggedIn, checkingStatus }
}

How to handle firebase's onAuthStateChanged function in quasar

Currently I'm using tailwind css and headlessui for a few components and firebase.
Now I would like to use quasar but the boot files are very mysterious to me.
Currently I manage firebase with config.js, main.js and pinia store.
I replaced my old config.js file with a firebase.js boot file as recommended by Quasar and it seems to work. (but I don't really know if it's good practice)
import { boot } from 'quasar/wrappers'
import { initializeApp } from 'firebase/app'
import { getFirestore } from 'firebase/firestore'
import { getAuth } from 'firebase/auth'
const firebaseConfig = {
apiKey: 'xxxxxxxxxxxxxx',
authDomain: 'xxxxxxxxxxxxxx',
projectId: 'xxxxxxxxxxxxxx',
storageBucket: 'xxxxxxxxxxxxxx',
messagingSenderId: 'xxxxxxxxxxxxxx',
appId: '1:xxxxxxxxxxxxxx'
}
// Init firebase
initializeApp(firebaseConfig)
// Init services
const db = getFirestore()
const auth = getAuth()
export { db, auth }
// "async" is optional;
// more info on params: https://v2.quasar.dev/quasar-cli/boot-files
export default boot(async (/* { app, router, ... } */) => {
// something to do
})
But I don't know what to do with the old mains.js file which is no longer available in Quasar. In main.js there is the following code:
import { createApp, markRaw } from 'vue'
import router from './router/router'
import { createPinia } from 'pinia'
import App from './App.vue'
// firebase
import { auth } from './firebase/config'
import { onAuthStateChanged } from 'firebase/auth'
import './input.pcss'
let app
onAuthStateChanged(auth, () => {
if (!app) {
app = createApp(App)
.use(
createPinia().use(({ store }) => {
store.$router = markRaw(router)
})
)
.use(router)
.mount('#app')
}
})
What should I do with the code above in particular with the onAuthStateChanged function?
Thanks for your help
I've found a solution for this that is suitable for my purposes. For me the requirements were:
Make sure auth is initialized on a refresh, before rendering.
Make sure any data required for the app is also initialized, before rendering.
Detect log-in, log-out, and time outs and act accordingly.
I haven't tested time outs yet but basically I solved this with the following flow.
In your router/index.js file, add a before each function, that checks to see if a listener is active, and calls a store function to create it if not.
Router.beforeEach(async (to, from, next) => {
// Access a store where you check if the auth changes are being handled
const storeAuth = useAuth()
if (!storeAuth.handlingAuth) {
await storeAuth.handleAuth()
}
// Redirects as necessary using to.path and next
next()
})
In the auth store, make a function that returns a promise to await in the beforeEach. Something like:
async handleAuth() {
const auth = getAuth()
return new Promise((resolve) => {
let initialLoad = true
auth.onAuthStateChanged(async (user) => {
if (user) {
await this.initializeUserData()
} else {
await this.clearUserData()
}
// If it is not initial load, use the router to push
// depending on whether the user exists.
// if (user && !initialLoad) this.router.push('/members')
// This would detect a login and go to the members section.
// If it is the initial load, resolve the promise
// so the router proceeds
if (initialLoad) {
initialLoad = false
this.handlingAuth = true
resolve()
}
})
})
}
Don't make the mistake of using useRouter() in the store. useRouter() is only for use in the setup function, or <script setup>. What you need to do is add the router as a plugin to Pinia. So in your stores/index.js import your router, then add this:
pinia.use(({ store }) => { store.router = markRaw(router) })
That way you can use this.router.push() in your store modules.
This might seem a bit messy because of redirects in both the navigation guard and the store action but this seems like the easiest way to get it to load the required data from both refresh and login while only using onAuthStateChanged in one place.
In summary it works like this:
When refreshed or entering a url manually, the app awaits the first state change, and any necessary loading.
Then in the nav guard, you can check whatever store variables you need to check your user login state, and redirect. Like back to the login page, if the user happened to enter a url for a members-only section.
Subsequent navigations (non-refresh) see that the auth handling is already set up, so it is ignored.
When a separate function triggers login or logout, we can see that it is not the initialLoad, and do any other redirects at that point.

React doesn't call useEffect on firebase (v9) auth update

I'm uploading a user profile image into firebase storage, then updating the photoURL value of the auth user with updateProfile(). After that I want the user to be updated without manually refreshing the page. I've been trying this for days now and the issue gets more weird every time I try to debug it.
The interesting thing is the user object seems to be already updated when I log it with console.log(currentUser) after the then promise of updateProfile() is fulfilled. So the new photoURL is already present in the currentUser object. But it seems to not call a state update or console.log("!!!!currentAuthUserUpdate", user);. So the user image wouldn't refresh in my page.
I even tried it with doing a useEffect with the currentUser as a dependency but it wasn't fired. Still, the currentUser object changed when logging it after updateProfile()
Updating the profile, UpdateUserImage.tsx:
import { useAuth } from "../../contexts/AuthContext";
const { currentUser } = useAuth();
// updating the user profile
updateProfile(currentUser, { photoURL })
AuthContext.tsx:
import { auth } from "./../firebase/firebase";
const [currentUser, setCurrentUser] = useState(null);
const auth = getAuth(app);
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged((user) => {
console.log("!!!!currentAuthUserUpdate", user);
// I tried setting the user as a custom new object: const userData = { ...user };
setCurrentUser(user);
});
return unsubscribe;
}, []);
firebase.js
import { getAuth } from "firebase/auth";
// Initialize Firebase
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
export {
auth
};
What I tried additionally: (But this wouldn't work as the user is already updated without a state refresh from react, so it's trying to replace the same object)
const reloadUser = async () => {
try {
const res = await currentUser.reload();
const user = auth.currentUser;
console.log("currentUser:", user);
setCurrentUser(auth.currentUser);
console.log("res", res);
} catch (err) {
console.log(err);
}
};
it's not auth.onAuthStateChanged. You need to import onAuthStateChanged from 'firebase/auth'
import { getAuth, onAuthStateChanged } from "firebase/auth"
const auth = getAuth(); // leave getAuth empty if you only have one app
Then in your useEffect it should be
onAuthStateChanged(auth, async (currentUser) => { ... }
The setCurrentUser function returned from useState isn't always the same function in my experience.
You can try passing it as a dependency into the useEffect - but I don't think that's what you want.
React lets you use an old useState setter if you give it an updater function, rather than a value: setCurrentUser(()=>auth.currentUser)
The React docs dispute this though.
Using useStates are good for re-rendering components. However going into utilizing useRefs are best for updating the actual variable and will not cause a re-render of the component.
Declare it like:const currentUser = useRef(null)
Update it like: currentUser.current = updatedUserData
Use in code like: currentUser.current.photoURL

expo auth response is always null

I've searched on here already but it seems all the answers are very outdated or they are questions that haven't been answered.
I've got an expo app SDK 43 and I'm using their auth library to authorize a reddit login. I've followed the example here https://docs.expo.dev/guides/authentication/#reddit to produce this code
import React from 'react';
import * as WebBrowser from 'expo-web-browser';
import { makeRedirectUri, ResponseType, useAuthRequest } from 'expo-auth-session';
import { Button } from 'react-native-paper';
import { useAppDispatch } from '../../common/hooks/redux';
import { setAuthCode } from '../../common/state/authSlice';
WebBrowser.maybeCompleteAuthSession();
// Endpoint
const discovery = {
authorizationEndpoint: 'https://www.reddit.com/api/v1/authorize.compact',
tokenEndpoint: 'https://www.reddit.com/api/v1/access_token',
};
const LoginScreen = () => {
const dispatch = useAppDispatch();
const [request, response, promptAsync] = useAuthRequest(
{
responseType: ResponseType.Token,
clientId: 'MY_CLIENT_ID',
scopes: ['identity'],
redirectUri: makeRedirectUri({
scheme: undefined,
}),
},
discovery
);
React.useEffect(() => {
console.log(`response is ${response}`);
if (response?.type === 'success') {
const { access_token } = response.params;
dispatch(setAuthCode(access_token));
console.log(access_token);
} else {
console.log(response);
}
}, [response]);
return (
<Button
disabled={!request}
onPress={() => {
promptAsync();
}}
>
Login
</Button>
);
};
export default LoginScreen;
But despite the fact that the login button correctly takes me to the login screen, I successfully log in and allow the app (and if I go onto the web separately I can see in my account that the app is there under the authorized apps.)
At this point on my device one of two things happens: 1. something causes the app to disconnect from metro and it hangs on a loading wheel belonging to the greater expo stuff, or 2. It successfully gets back to the app but it redownloads the bundle and the response is null.
What is screwing up here?

How to properly update user information in firebase?

I have a code that is a form, basically is suppose to update the user information.
According to Firebase documentation manage user but for some reason I'm getting this errors
This is what I have I don't think I'm missing anything is pretty much the same
The only difference is that on my firebase.js I made new cons to make codes shorter but that shouldn't affect at all.
firebase.js
const db = firebase.firestore();
const auth = firebase.auth();
const storage = firebase.storage();
export {db, auth, storage};
Code
import {auth} from "./firebase"
const authUser = auth.currentUser
const update = (e) => {
e.preventDefault();
authUser.updateProfile({
displayName: {fullName}
})
}
I just wanted to test it on the user name first and then do all the others but I don't understand why is not working is super simple.
Update
authUser is undefined. This means that auth.currentUser was undefined at the time when it was assigned to authUser. This probably means that the user is not logged in. You will need to handle the case when the user is not logged in, like
import {auth} from "./firebase"
const authUser = auth.currentUser
const update = (e) => {
e.preventDefault();
if (authUser) {
authUser.updateProfile({
displayName: fullName
})
} else {
//User is not logged in, handle that case here
}
}
import {auth} from "./firebase";
const authUser = auth.currentUser;
const update = (e) => {
e.preventDefault();
authUser?.updateProfile({
displayName: fullName
});
}

Categories