I should make user auth with React JS and Firebase. I have written the codes bellow and getting
POST https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=XXXXXXX%3B 400 I have done everything following the instruction but don't know what the problem is. When the user signs up, it is not appearing in firebase identifier.
base.js
import * as firebase from "firebase/app";
import {getAuth} from "firebase/auth";
const app = firebase.initializeApp({
...
});
export const auth = getAuth(app);
export default app;
AuthContext.js
import React, { useState, useContext, useEffect } from "react";
import { auth } from "../base";
import {
createUserWithEmailAndPassword,
onAuthStateChanged,
} from "#firebase/auth";
export const AuthContext = React.createContext();
export function useAuth() {
return useContext(AuthContext);
}
export function AuthProvider({ children }) {
const [currentUser, setCurrentUser] = useState();
const [loading, setLoading] = useState(true);
function signup(email, password) {
return createUserWithEmailAndPassword(auth, email, password);
}
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (user) => {
setCurrentUser(user);
setLoading(false);
});
return unsubscribe;
}, []);
const value = {
currentUser,
signup,
};
return (
<AuthContext.Provider value={value}>
{!loading && children}
</AuthContext.Provider>
);
}
Code in signUp page
const { signup, currentUser } = useAuth();
const handleSubmit = async (e) => {
e.preventDefault();
const v1 = USER_REGEX.test(user);
const v2 = PWD_REGEX.test(pwd);
if (!v1 || !v2) {
setErrMsg("Invalid Entry");
return;
}
try {
setErrMsg('')
setLoading(true)
await signup(emailRef.current.value, pwdRef.current.value);
} catch {
setErrMsg("Failed to create an account");
}
setLoading(false)
console.log(user, emailRef, pwdRef);
setSuccess(true);
};
Related
I have been trying to setup authentication on my project for a while using firebase authentication. I am doing a Next.js project.
I am having issues where If I login and refresh the page, I have to login again, I found a partial solution which involves using contexts, but whenever I use it to login, I am getting type error login is not a function. I have attached 3 files I am using
here is my context file
import { createContext, useContext } from "react";
import { useFirebaseAuth } from "../firebase";
const authUserContext = createContext({
authUser: null,
loading: true,
login: async () => {},
});
export function AuthUserProvider({ children }) {
const Auth = useFirebaseAuth;
return (
<authUserContext.Provider value={Auth}>{children}</authUserContext.Provider>
);
}
export const useAuth = () => useContext(authUserContext);
Here is my login page. If i just make the login as a regular export in the firebase Auth file, I dont get the error, i am able to login but the context doesnt get updated.
import React from "react";
import { useState } from "react";
// import { emailRegex } from "../components/Utilities/validations";
import { useAuth } from "../components/contexts/userContext";
const Login = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const { login, authUser } = useAuth();
const [loading2, setLoading] = useState(false);
const handleSubmit = async (e) => {
// validate(e.values);
e.preventDefault();
setLoading(true);
try {
const user = await login(email, password);
console.log(user.user);
console.log(authUser);
} catch (err) {
console.log(err);
}
setLoading(false);
};
};
export default Login;
and here is the firebase code I have. If I remove the login function from the scope of the useFirebaseAuth, I can just import the login normally and get a login token. However, it doesnt update the authUser. If I refresh the page, I have to login again.
function useFirebaseAuth() {
const [authUser, setAuthUser] = useState(null);
const [loading, setLoading] = useState(true);
const formatAuthUser = (user) => ({
uid: user.uid,
email: user.email,
});
const authStateChanged = async (authState) => {
if (!authState) {
setAuthUser(null);
setLoading(false);
return;
}
setLoading(true);
const formattedUser = formatAuthUser(authState);
setAuthUser(formattedUser);
console.log("useAuth " + authUser?.email);
};
const login = (email, password) => {
const promise = auth.signInWithEmailAndPassword(auth, email, password);
console.log(promise);
return promise;
};
useEffect(() => {
const unsub = auth.onAuthStateChanged(authStateChanged);
return unsub;
}, []);
return {
authUser,
loading,
login,
};
}
I am building an app using next13 (to make use of server side components), however, for some reason my existing AuthContext is not working. I am getting the following error:
TypeError: React.createContext is not a function
From what I can see, the AuthContext needs to be set to 'use client', as there is use of useState and useEffect within it, but for some reason the application no longer recognises that createContext is actually a function.
This is my AuthContext:
'use client';
import { createContext, useContext, useEffect, useState } from 'react';
import { onAuthStateChanged, signOut, signInWithEmailAndPassword, createUserWithEmailAndPassword } from 'firebase/auth';
import { auth } from '../config';
const AuthContext = createContext({});
export const useAuth = () => useContext(AuthContext);
export const AuthContextProvider = ({ children }) => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (user) => {
setLoading(true);
setUser(user ?? null);
setLoading(false);
});
return () => unsubscribe();
}, []);
const login = async (email, password) => {
await signInWithEmailAndPassword(auth, email, password);
};
const logout = async () => {
setUser(null);
await signOut(auth)
};
const register = async (email, password) => {
try {
const userCred = await createUserWithEmailAndPassword(auth, email, password);
await userCred.user.sendEmailVerification({
url: process.env.NEXT_PUBLIC_HOST
});
} catch (err) {
return {
errorCode,
errorMessage
}
}
};
return (
<AuthContext.Provider value={{ user, loading, login, logout, register }}>
{children}
</AuthContext.Provider>
);
};
The AuthContext is then used in my main layout page within the app directory:
'use client';
import { CssBaseline, Container } from '#mui/material';
import { NavBar, Footer } from '../components';
import { AuthContextProvider } from '../context';
import '#fontsource/roboto/300.css';
import '#fontsource/roboto/400.css';
import '#fontsource/roboto/500.css';
import '#fontsource/roboto/700.css';
const RootLayout = ({ children }) => {
return (
<html lang='en'>
<head>
<link rel="icon" href="/favicon.ico" />
</head>
<body>
<AuthContextProvider>
<CssBaseline />
<NavBar />
<Container component='main' sx={{ padding: 3 }}>
{children}
</Container>
<Footer />
</AuthContextProvider>
</body>
</html>
);
}
export default RootLayout;
I am unsure if I need to take a different approach to authentication, perhaps using the next-auth package, but I am not sure what the best way would be.
Cheers for any help!
Here's an example of useContext I am using on my application.
'use client'
import { createContext, useContext, useEffect, useState } from 'react'
import { getAuth, User } from 'firebase/auth'
import { initializeApp, getApps, getApp } from 'firebase/app'
import nookies from 'nookies'
const firebaseConfig = {
...
}
getApps().length ? getApp() : initializeApp(firebaseConfig)
const auth = getAuth()
const AuthContext = createContext<User | null>(null)
export function AuthProvider({ children }: any) {
//
const [user, setUser] = useState<User | null>(null)
useEffect(() => {
return auth.onIdTokenChanged(async (user) => {
if (!user) {
setUser(null)
nookies.set(undefined, 'token', '', { path: '/' })
} else {
const token = await user.getIdToken()
setUser(user)
nookies.set(undefined, 'token', token, { path: '/' })
}
})
}, [])
useEffect(() => {
const handle = setInterval(async () => {
const user = auth.currentUser
if (user) await user.getIdToken(true)
}, 15 * 60 * 1000)
return () => clearInterval(handle)
}, [])
return <AuthContext.Provider value={user}>{children}</AuthContext.Provider>
}
export const useAuth = () => {
return useContext(AuthContext)
}
Note that we're also forcing token refresh every 15 minutes, and saving that to cookies. You can access cookies in server pages using the new next13 cookies package.
You can also get the user by importing the useAuth hook we just created.
For example
'use client'
import useAuth from '../context/AuthProvider'
const Page = () => {
const {user} = useAuth()
// Rest of your application
}
Hope it helps
I got following error at onAuthStateChanged method at store.dispatch(userActions.setUser(null));
Error: [Immer] produce can only be called on things that are draftable: plain objects, arrays, Map, Set or classes that are marked with '[immerable]: true'. Got '[object Object]'
I am trying to change to firebase authentication from jwt. So custom jwt authentication is using redux. Now when I call firebase's signOut(auth) method, onAuthStateChanged method give me this error. May I know how to write immerable object for user.
user_reducer.js
import produce from "immer";
import * as Action_Constants from "../actions/user_actions";
const initialState = null;
export const UserReducer = produce((state = initialState, action) => {
switch (action.type) {
case Action_Constants.SET_USER: {
return action.user;
}
case Action_Constants.FETCH_USER_COMPLETED: {
return action.user;
}
default:
return state;
}
});
user_actions.js
export const FETCH_USER = "FETCH_USER";
export const FETCH_USER_COMPLETED = "FETCH_USER_COMPLETED";
export const SET_USER = "SET_USER";
export const actionCreators = {
fetchUser: (id) => ({
type: FETCH_USER,
id,
}),
fetchUserCompleted: (user) => ({
type: FETCH_USER_COMPLETED,
user,
}),
setUser: (user) => ({
type: SET_USER,
user,
}),
};
I have deleted other firebase functions to simply the file.
auth_provider.jsx
import React, { useState, useEffect, useContext, createContext } from "react";
import { useLocation, Navigate } from "react-router-dom";
import { signIn, signUp } from "../helpers/gql_auth_helpers";
import paths from "../routes/paths";
import { store } from "../store/configure_store";
import { actionCreators as userActions } from "../store/actions/user_actions";
import { auth } from "../helpers/init-firebase";
import {
onAuthStateChanged,
signOut,
} from "firebase/auth";
const AuthContext = createContext(null);
let accessToken = "";
export const getAccessToken = () => accessToken;
export const setAccessToken = (token) => {
accessToken = token;
};
export const AuthProvider = ({ user: usr, children }) => {
const [user, setUser] = useState(usr);
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (user) => {
if (user) {
setUser(user);
setAccessToken(user.getIdToken(true));
store.dispatch(userActions.setUser(user));
} else {
setUser(null);
setAccessToken(null);
store.dispatch(userActions.setUser(null));
}
});
return () => {
unsubscribe();
};
}, []);
async function logout() {
return signOut(auth);
}
const value = {
user,
accessToken,
logout,
};
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
};
export const useAuth = () => {
return useContext(AuthContext);
};
I have a AuthProvider and useAuth method that returns useContext(AuthContext) inside my App.js root file, however, if I console.log the return value of useAuth() in App.js, it is undefined, why??
In any other child component like Loginpage, there is an authContext displayed..
my App.js:
import { AuthProvider, useAuth } from './providers/AuthProvider'
function App() {
const auth = useAuth()
console.log({ auth }) //undefined??
AuthProvider.js:
import React, { useContext, useState, useEffect } from 'react'
import { app, auth, firestore, storage } from '../firebase'
const AuthContext = React.createContext()
export function useAuth() {
return useContext(AuthContext)
}
export function AuthProvider({ children }) {
const [currentUser, setCurrentUser] = useState(undefined)
const [loginError, setLoginError] = useState(null)
const login = async (email, password) => {
try {
return auth.signInWithEmailAndPassword(email, password)
} catch (error) {
setLoginError(error)
}
}
const logout = () => auth.signOut()
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(user => {
setCurrentUser(user)
})
return unsubscribe
}, [])
const value = {
loginError,
login,
signup,
logout,
currentUser,
app,
firestore,
storage,
auth,
}
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
}
Since you import AuthProvider in same file of App it gives a clue that you didn't use the Context API correctly.
Context values are available for Context-Consumers.
Context-Consumers are children of Context-Provider, therefore your App component have to be a child of <AuthContext.Provider>:
<AuthProvider>
<App />
</AuthProvider>
I am facing a issue where I can login/register but I cannot sign out in the app.
This is the error I am getting from my terminal:
In my HomeScreen.js, below contains the code I did for signout:
import { auth } from '../firebase'
import { useNavigation } from '#react-navigation/core'
import { signOut } from 'firebase/auth'
const navigation = useNavigation()
const handleSignOut = async () =>{
try{
const { user } = signOut()
console.log("Signed out successfully")
navigation.replace("Login")
}catch (error) {
console.log({error});
}
}
Updated HomeScreen.js
import { auth } from '../firebase'
import { useNavigation } from '#react-navigation/core'
import { signOut } from 'firebase/auth'
const navigation = useNavigation()
const handleSignOut = async () =>{
try{
await signOut()
console.log("Signed out successfully")
navigation.replace("Login")
}catch (error) {
console.log({error});
}
}
As this is my first time doing this, I am trying to replicate what I did for login which is
import { auth } from '../firebase'
import { createUserWithEmailAndPassword } from "firebase/auth"
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const handleSignUp = async () => {
try {
if (email && password) {
const { user } = await createUserWithEmailAndPassword(auth, email, password)
console.log(user);
}
} catch (error) {
console.log({error});
}
}
I am guessing this is wrong so can anyone help me with this? Thank you very much!
You need to import auth from firebase like this:
import {auth} from '../firebase'
import { useNavigation } from '#react-navigation/core'
import { signOut } from 'firebase/auth'
const navigation = useNavigation()
const handleSignOut = async () =>{
try{
await signOut(auth);
console.log("Signed out successfully")
navigation.replace("Login")
}catch (error) {
console.log({error});
}
}