Firebase Authentication not working with React - reactjs

I have a simple app using React and Firebase (Cloud Firestore & Authentication).
I have used Firebase Authentication Sign in/ sign up with email and password but the promises have no response, although it worked well last night, and firebase.auth().onStateAuthChanged is not working neither but firebase.auth().sendPasswordResetEmail() works well. I do not know what is wrong?
You can check my code at: https://lfddm.csb.app/ (I've used codesandbox)
The firebase cloud firestore rules:
match /users/{userId} {
allow create: if request.auth.uid != null
allow read, write: if request.auth.uid == userId
}
The SignIn component:
import React, { useState } from "react";
import { Link } from "react-router-dom";
import {
Button,
Modal,
ModalHeader,
ModalBody,
Form,
FormGroup,
Label,
Input
} from "reactstrap";
import firebase from "../config/firebase";
export default function SignInModal(props) {
const [modal, setModal] = useState(false);
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const resetModal = () => {
setModal(false);
setEmail("");
setPassword("");
};
const signIn = (e, p) => {
// console.log(e, p);
firebase
.auth()
.signInWithEmailAndPassword(e, p)
.then(() => alert("Sign in successfully!"))
.then(() => resetModal())
.catch(err => alert(err.message));
};
const styles = {
signModal: {
display: "inline",
margin: "0 3px"
}
};
return (
<div className="SignModal" style={styles.signModal}>
<Button color="primary" onClick={() => setModal(!modal)}>
Sign In
</Button>
<Modal isOpen={modal} toggle={() => resetModal()}>
<ModalHeader toggle={() => resetModal()}>Sign In</ModalHeader>
<ModalBody>
<Form
onSubmit={e => {
e.preventDefault();
signIn(email, password);
}}
>
<FormGroup>
<Label for="email">Email</Label>
<Input
type="email"
id="email"
value={email}
onChange={e => setEmail(e.target.value)}
required
/>
</FormGroup>
<FormGroup>
<Label for="password">Password</Label>
<Input
type="password"
id="password"
value={password}
onChange={e => setPassword(e.target.value)}
required
/>
</FormGroup>
<Button
type="submit"
color="primary"
style={{ marginRight: "5px" }}
>
Sign In
</Button>
<Link to="/forget-password">Forget password</Link>
</Form>
</ModalBody>
</Modal>
</div>
);
}
signIn function does not return anything, it means that the firebase.auth() promise is pending.

Related

API Calls with Axios fail on React

On handleSubmit i am trying to log in using an API hosted on Heroku and made based on ExpressJS. It is working fine when i am using Postman to reach the API on the login endpoint. But when using axios on react it fails.
Heroku login endpoint : https://nodejs-mongodb-authapp.herokuapp.com/login
Code :
import React, { useState } from "react";
import './Login.css' ;
import {FaMountain} from 'react-icons/fa';
import { Form, Button } from "react-bootstrap";
import axios from "axios";
import Cookies from "universal-cookie";
const cookies = new Cookies();
export const Login = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [login, setLogin] = useState(false);
const [state , setState] = useState(false);
const axios = require("axios");
const handleSubmit = (event) =>{
const data = JSON.stringify({
email: "au#outlook.com",
password: "zzz",
});
const config = {
method: "post",
url: "https://nodejs-mongodb-authapp.herokuapp.com/login",
headers: {
"Content-Type": "application/json",
},
data: data,
};
axios(config)
.then((result) => {
alert("in thenn");
// setLogin(true);
// cookies.set("TOKEN", result.data.token, {
// path: "/",
// });
// navigate('/auth');
})
.catch((error) => {
error = new Error();
});
}
return(
<div className="p1">
<div className="log">
<form className="formFields" >
<div className="lfa">
<FaMountain />
</div>
<p>LOG IN</p>
<Form onSubmit={(e)=>handleSubmit(e)} className="form_">
{/* email */}
<Form.Group controlId="formBasicEmail">
<Form.Control
type="email"
name="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Enter email"
/>
</Form.Group>
{/* password */}
<Form.Group controlId="formBasicPassword">
<Form.Control
type="password"
name="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
/>
</Form.Group>
{/* submit button */}
<Button
variant="primary"
type="submit"
onClick={(e) => handleSubmit(e)}
>
Login
</Button>
</Form>
</form>
</div>
{/* {login ? (
<p className="text-success">You Are Logged in Successfully</p>
) : (
<p className="text-danger">You Are Not Logged in</p>
)} */}
</div>
)
}
The API is public for the moment you can try login with those credentials on PostMan :
Well, I managed to test it and it does respond. The request should be done like this:
import React, { useState } from "react";
import { FaMountain } from "react-icons/fa";
import { Form, Button } from "react-bootstrap";
import Cookies from "universal-cookie";
const cookies = new Cookies();
export const Login = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [login, setLogin] = useState(false);
const axios = require("axios");
const handleSubmit = (event) => {
event.preventDefault();
const data = JSON.stringify({
email: "au#outlook.com",
password: "zzz"
});
const config = {
method: "post",
url: "https://nodejs-mongodb-authapp.herokuapp.com/login",
headers: {
"Content-Type": "application/json"
},
data: data
};
axios(config)
.then((result) => {
alert("in thenn");
console.log(result);
setLogin(true);
cookies.set("TOKEN", result.data.token, {
path: "/"
});
})
.catch((error) => {
error = new Error();
});
};
return (
<div className="p1">
<div className="log">
<div className="lfa">
<FaMountain />
</div>
<p>LOG IN (Login State {login.toString()})</p>
<p>Token: {cookies.get('TOKEN')}</p>
<Form onSubmit={(e) => handleSubmit(e)} className="form_">
{/* email */}
<Form.Group controlId="formBasicEmail">
<Form.Control
type="email"
name="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Enter email"
/>
</Form.Group>
{/* password */}
<Form.Group controlId="formBasicPassword">
<Form.Control
type="password"
name="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
/>
</Form.Group>
{/* submit button */}
<Button
variant="primary"
type="submit"
onClick={(e) => handleSubmit(e)}
>
Login
</Button>
</Form>
</div>
{/* {login ? (
<p className="text-success">You Are Logged in Successfully</p>
) : (
<p className="text-danger">You Are Not Logged in</p>
)} */}
</div>
);
};
Since you are sending a JSON with the access information through the body, you have to take the JSON structure and then chain it to send it through post, and you have to add the ContentType: application/json header.
UP: When forms are used and you use an input type submit, every time the login button is clicked, the component is updated, to avoid this add the following inside the function event.preventDefault();
Test it here

Why might I be getting a: TypeError: updatePassword is not a function

I have a simple react app with an update profile page, using firebase for auth, and when I try to change the password, I'm getting this error:TypeError: updatePassword is not a function.
Here is my UpdateProfile.js
import React, { useRef, useState } from "react";
import { Form, Button, Card, Alert } from "react-bootstrap";
import { getAuth } from "firebase/auth";
import { Link, useNavigate } from "react-router-dom";
export default function UpdateProfile() {
const emailRef = useRef();
const passwordRef = useRef();
const passwordConfirmRef = useRef();
const { currentUser, updateEmail, updatePassword } = getAuth();
const [error, setError] = useState("");
const [loading, setLoading] = useState(false);
const navigate = useNavigate();
function handleSubmit(e) {
e.preventDefault();
if (passwordRef.current.value !== passwordConfirmRef.current.value) {
return setError("Passwords do not match");
}
const promises = [];
setLoading(true);
setError("");
if (emailRef.current.value !== currentUser.email) {
promises.push(updateEmail(emailRef.current.value));
}
if (passwordConfirmRef.current.value) {
promises.push(updatePassword(passwordRef.current.value));
}
Promise.all(promises)
.then(() => {
navigate("/");
})
.catch(() => {
setError("Failed to update account");
})
.finally(() => {
setLoading(false);
});
}
return (
<>
<Card>
<Card.Body>
<h2 className="text-center mb-4">Update Profile</h2>
{error && <Alert variant="danger">{error}</Alert>}
<Form onSubmit={handleSubmit}>
<Form.Group id="email">
<Form.Label>Email</Form.Label>
<Form.Control
type="email"
ref={emailRef}
required
defaultValue={currentUser.email}
/>
</Form.Group>
<Form.Group id="password" data-testid="pword">
<Form.Label>Password</Form.Label>
<Form.Control
type="password"
ref={passwordRef}
placeholder="Leave blank to keep the same"
/>
</Form.Group>
<Form.Group id="password-confirm">
<Form.Label>Password Confirmation</Form.Label>
<Form.Control
type="password"
ref={passwordConfirmRef}
placeholder="Leave blank to keep the same"
/>
</Form.Group>
<div className="w-100 mt-2">
<Button disabled={loading} className="w-100" type="submit">
Update
</Button>
</div>
</Form>
</Card.Body>
</Card>
<div className="w-100 text-center mt-2">
<Link to="/">Cancel</Link>
</div>
</>
);
}
the Error points to line 30, which is here in stars:
if (emailRef.current.value !== currentUser.email) {
promises.push(updateEmail(emailRef.current.value));
}
if (passwordConfirmRef.current.value) {
**promises.push(updatePassword(passwordRef.current.value));**
}
Can anyone see what I have wrong? Thanks!
The updateEmail and updatePassword functions can be imported from Firebase Auth SDK and not the Auth instance. Try updating your import statements to:
import { updateEmail, updatePassword } from "firebase/auth"

Including AWS function call removes app layout

This is a picture of my react app before and after replacing a method handleSubmit from the following:
function handleSubmit(event) {
event.preventDefault();
}
with this:
async function handleSubmit(event) {
event.preventDefault();
try {
await Auth.signIn(email, password);
alert("Logged in");
} catch (e) {
alert(e.message);
}
}
As you can see the replacement of code removes the 'layout' of the app-page. I am new to AWS and React. Anyone has a clue what is wrong?
The entire Login.js container can be seen here:
import React, { useState } from "react";
import Form from "react-bootstrap/Form";
import Button from "react-bootstrap/Button";
import "./Login.css";
import { Auth } from "aws-amplify";
export default function Login() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
function validateForm() {
return email.length > 0 && password.length > 0;
}
async function handleSubmit(event) {
event.preventDefault();
try {
await Auth.signIn(email, password);
alert("Logged in");
} catch (e) {
alert(e.message);
}
}
return (
<div className="Login">
<Form onSubmit={handleSubmit}>
<Form.Group size="lg" controlId="email">
<Form.Label>Email</Form.Label>
<Form.Control
autoFocus
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</Form.Group>
<Form.Group size="lg" controlId="password">
<Form.Label>Password</Form.Label>
<Form.Control
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</Form.Group>
<Button block size="lg" type="submit" disabled={!validateForm()}>
Login
</Button>
</Form>
</div>
);
}

Testing a component that uses context

I'm trying to write tests for the Login component of my app, and want to test that the user can enter email and password fields, then submit them. There are two complications: it has a to another component, and it uses context to import a function called loginUser. The first problem I solve by making a custom renderWithRouter function, but I can't solve the second problem because the component isn't able to consume the loginUser function outside of context. I get this error: Error: Uncaught [TypeError: Cannot read property 'then' of undefined] because loginUser is undefined. Any idea how I can solve this? Here is Login.tsx:
import React, { useState } from 'react';
import { useAuth } from 'context/authContext'
import { useHistory, Link } from 'react-router-dom'
import Form from 'react-bootstrap/Form';
import Button from 'react-bootstrap/Button'
export const Login = (props: any) => {
const { setUser, loginUser } = useAuth()
const [email, setEmail] = useState<string>();
const [password, setPassword] = useState<string>();
const [errors, setErrors] = useState<boolean>()
const history = useHistory()
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
loginUser({
email,
password
}).then((result: any) => {
console.log(result)
if (result.message === 'login error') {
setErrors(true)
} else {
history.push('/dashboard/home')
}
})
}
return(
<div className="login-wrapper">
<h1>Please Log In</h1>
<div className='form-wrapper'>
<Form onSubmit={handleSubmit}>
<Form.Group controlId="formBasicEmail">
<Form.Label>Email address</Form.Label>
<Form.Control
isInvalid={errors ? true : false}
type="email"
placeholder="Enter email"
onChange={e => setEmail(e.target.value)}/>
<Form.Control.Feedback type="invalid">
{errors ? 'either you don\'t have an account or the email and password do not match' : null}
</Form.Control.Feedback>
<Form.Text className="text-muted">
umm..
</Form.Text>
</Form.Group>
<Form.Group controlId="formBasicPassword">
<Form.Label>Password</Form.Label>
<Form.Control
type="password"
placeholder="Password"
onChange={e => setPassword(e.target.value)}
/>
</Form.Group>
<Button variant="primary" type="submit">
Submit
</Button>
<div>
<Link to='/register'>new user? click here to register</Link>
</div>
</Form>
</div>
</div>
)
}
And Login.test.tsx:
test("allows user to input their email", () => {
const onSubmit = jest.fn();
renderWithRouter(<Login />)
const input = screen.getByLabelText("Email address")
const pwd = screen.getByLabelText("Password")
const button = screen.getByText("Submit")
fireEvent.change(input, { target: { value: "t#t.com"}})
fireEvent.change(pwd, { target: { value: "123456"}})
fireEvent.click(button)
expect(onSubmit).toBeCalled()
})
})
Haven't run it but refactor to something like this and then test the LoginForm component.
import React, { useState } from 'react';
import { useAuth } from 'context/authContext'
import { useHistory, Link } from 'react-router-dom'
import Form from 'react-bootstrap/Form';
import Button from 'react-bootstrap/Button'
interface LoginFormProps {
onSubmit: (email: string, password: string) => void;
errors: boolean;
}
export const LoginForm: React.FC<LoginFormProps> = ({onSubmit, errors}) => {
const [email, setEmail] = useState<string>();
const [password, setPassword] = useState<string>();
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
onSubmit(email, password)
}
return(
<div className="login-wrapper">
<h1>Please Log In</h1>
<div className='form-wrapper'>
<Form onSubmit={handleSubmit}>
<Form.Group controlId="formBasicEmail">
<Form.Label>Email address</Form.Label>
<Form.Control
isInvalid={errors ? true : false}
type="email"
placeholder="Enter email"
onChange={e => setEmail(e.target.value)}/>
<Form.Control.Feedback type="invalid">
{errors ? 'either you don\'t have an account or the email and password do not match' : null}
</Form.Control.Feedback>
<Form.Text className="text-muted">
umm..
</Form.Text>
</Form.Group>
<Form.Group controlId="formBasicPassword">
<Form.Label>Password</Form.Label>
<Form.Control
type="password"
placeholder="Password"
onChange={e => setPassword(e.target.value)}
/>
</Form.Group>
<Button variant="primary" type="submit">
Submit
</Button>
<div>
<Link to='/register'>new user? click here to register</Link>
</div>
</Form>
</div>
</div>
)
}
export const Login = (props: any) => {
const { setUser, loginUser } = useAuth()
const [errors, setErrors] = useState<boolean>(false)
const history = useHistory()
const handleSubmit = (email, password) => {
loginUser({
email,
password
}).then((result: any) => {
console.log(result)
if (result.message === 'login error') {
setErrors(true)
} else {
history.push('/dashboard/home')
}
})
}
return(
<LoginForm onSubmit={handleSubmit} errors={errors} />
)
}

How do I make the invalid hook call go away?

I get this error when trying to npm start my project:
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
import React, { useState, useEffect } from 'react';
import './index.css';
import conspireLogo from './conspireLogo.png';
import Post from './Post'
import { auth, db } from './firebase';
import { makeStyles } from '#material-ui/core/styles';
import Modal from '#material-ui/core/Modal';
import { Button, Input } from '#material-ui/core'
import ImageUpload from './imageUpload';
//import { BrowserRouter, Route, Switch } from 'react-router-dom';
function getModalStyle() {
const top = 50;
const left = 50;
return {
top: `${top}%`,
left: `${left}%`,
transform: `translate(-${top}%, -${left}%)`,
};
}
const useStyles = makeStyles((theme) => ({
paper: {
position: 'absolute',
width: 400,
backgroundColor: theme.palette.background.paper,
border: '2px solid #000',
boxShadow: theme.shadows[5],
padding: theme.spacing(2, 4, 3),
},
}));
function MainPage() {
const classes = useStyles();
const [modalStyle] = useState(getModalStyle);
const [posts, setPosts] = useState([]);
const [open, setOpen] = useState(false);
const [openSignIn, setOpenSignIn] = useState(false);
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [email, setEmail] = useState('');
const [user, setUser] = useState(null);
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged((authUser) => {
if (authUser) {
console.log(authUser);
setUser(authUser);
} else {
setUser(null);
}
})
return () => {
unsubscribe();
}
}, [user,username]);
useEffect(() => {
db.collection('posts').orderBy('timestamp', 'desc').onSnapshot(snapshot => {
setPosts(snapshot.docs.map(doc => ({
id: doc.id,
post: doc.data()
})));
})
}, []);
const signUp = (event) => {
event.preventDefault();
auth
.createUserWithEmailAndPassword(email, password)
.then((authUser) => {
authUser.user.updateProfile({
displayName: username
})
})
.catch((error) => alert(error.message));
}
const signIn = (event) => {
event.preventDefault();
auth
.signInWithEmailAndPassword(email, password)
.catch((error) => alert(error.message));
setOpenSignIn(false);
}
return (
<div className="app">
<Modal
open={open}
onClose={() => setOpen(false)}
>
<div style={modalStyle} className={classes.paper}>
<form className="app__signup">
<center>
<img
className="app__headerImage"
src={conspireLogo}
alt="Conspire Logo"
/>
</center>
<Input
placeholder="username"
type="text"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<Input
placeholder="email"
type="text"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<Input
placeholder="password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<Button type="submit" onClick={signUp}>Sign Up</Button>
</form>
</div>
</Modal>
<Modal
open={openSignIn}
onClose={() => setOpenSignIn(false)}
>
<div style={modalStyle} className={classes.paper}>
<form className="app__signup">
<center>
<img
className="app__headerImage"
src={conspireLogo}
alt="Conspire Logo"
/>
</center>
<Input
placeholder="email"
type="text"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<Input
placeholder="password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<Button type="submit" onClick={signIn}>Sign In</Button>
</form>
</div>
</Modal>
<div className="app__header">
<img
className="app__headerImage"
src={conspireLogo}
alt="Conspire Logo"
/>
{user ? (
<Button onClick={() => auth.signOut()}>Logout</Button>
): (
<div className="app__loginContainer">
<Button onClick={() => setOpenSignIn(true)}>Sign In</Button>
<Button onClick={() => setOpen(true)}>Sign Up</Button>
</div>
)}
</div>
<div className="app__footer">
<Button onClick={() => setOpenSignIn(true)}><img
className="app__footerImage"
src="http://www.simpleimageresizer.com/_uploads/photos/bdfbb0d1/346a1f4363e1b59f6860fdce6abc1082_2_15.jpg"
alt="Create"
/>
</Button>
</div>
<div className="app__posts">
<div className="app__postsLeft">
{
posts.map(({id, post}) => (
<Post key={id} postId={id} user={user} username={post.username} caption={post.caption} imageUrl={post.imageUrl}></Post>
))
}
</div>
</div>
{user?.displayName ? (
<ImageUpload username={user.displayName} />
): (
<h3 className="center">Sorry you need to login to upload</h3>
)}
</div>
);
}
export default MainPage;
const [modalStyle] = useState(getModalStyle);
You're storing the function reference, not the function's return. Change it to
const [modalStyle] = useState(getModalStyle());
Moreover as you don't need to change this modalStyle, you don't need to have it in state
<div style={getModalStyle()} className={classes.paper}>
I don't think so there should be any error in this file, please check the other files
ex - Post, ImageUpload

Resources