I am trying to set up frontend authentication for a react application running on a Django REST-API.
The registration API endpoint works well if I send the request on Postman. However, the program is behaving very odd when I type information into my frontend registration form and click on submit.
Please look at the code below - I use a useInputState hook to keep track of the state of a given input. This works fine.
The registerUser function takes in username, password and email and sends a Post-Request to the dedicated API Endpoint.
The handleSubmit function checks if the passwords match and throws an error if they don't this works fine as well. If the passwords do match, it calls registerUser and provides the users input.
Now somewhere along the way, the password that has been passed into registerUser disappears (see comments). I have no idea where this issue could come from and any help is greatly appreciated!
function Register() {
const { auth, dispatchAuth } = useContext(AuthContext);
const { dispatchErrors } = useContext(ErrorContext);
const { dispatchMessages } = useContext(MessageContext);
const [username, setUsername] = useInputState("");
const [email, setEmail] = useInputState("");
const [password1, setPassword1] = useInputState("");
const [password2, setPassword2] = useInputState("");
const registerUser = ({ username, password, email }) => {
// headers
const config = {
headers: {
"Content-Type": "application/json",
},
};
// Request body
const body = JSON.stringify({
username,
password,
email,
});
console.log("body:");
console.log(body);
// Output looks like: {"username":"testuser","email":"email#test.com"}
// Where did the password go?!
axios
.post("/api/auth/register/", body, config)
.then((res) => {
dispatchAuth({ type: REGISTER_SUCCESS, payload: res.data });
})
.catch((err) => {
dispatchErrors(returnErrors(err.response.data, err.response.status));
dispatchAuth({ type: REGISTER_FAIL });
});
};
const handleSubmit = (e) => {
e.preventDefault();
if (password1 !== password2) {
dispatchMessages(
createMessage({ passwordsNotMatch: "Passwords do not match" })
);
} else {
console.log(password1); // Console output still contains the password
const newUser = { username, password1, email };
registerUser(newUser);
}
};
EDIT: Okay I'm an idiot and didn't notice that I need to change const newUser = { username, password1, email }; to be const newUser = { username, password, email };
Okay I'm an idiot and didn't notice that I need to change const newUser = { username, password1, email }; to be const newUser = { username, password, email };
Related
I'm working on a fullstack app with Express and React. For the calls to the backend, I use axios (version 1.1.2). Before this version, I was using a function to avoid writing the same calls to the database every time. Now, I get this error:
POST http://localhost:5005/api/auth/signup 400 (Bad Request)
Where does it come from?
This is my non working code:
const API_URL = process.env.REACT_APP_API_URL
export default axios.create({
baseURL: `${API_URL}/api`,
timeout: 1000,
headers: {
"Content-type": "application/json",
},
})
// Here, http refers to the axios.create function
class AuthService {
signup(data: any) {
return http.post("/auth/signup", data)
}
}
const handleSubmit = (e: React.ChangeEvent<HTMLFormElement>) => {
authService
.signup(inputs)
...rest
}
But this is working:
const handleSubmit = (e: React.ChangeEvent<HTMLFormElement>) => {
axios
.post(`${API_URL}/api/auth/signup`, inputs)
...rest
}
Thanks for your help!
EDIT:
When I submit the form, on the front end I get this error, which on the back end would be returned if an input is empty:
Please provide your full name.
Back end code:
if (!fullName) {
return res
.status(400)
.json({ message: "Please provide your full name." })
}
EDIT 2:
I tried to add a console.log on the back end with the req.body and this is what I get:
{}
This is the full backend code:
router.post("/signup", (req, res, next) => {
const { email, fullName, password } = req.body
console.log(req.body)
if (!fullName) {
return res
.status(400)
.json({ message: "Please provide your full name." })
}
User.findOne({ email })
.then(foundUser => {
...rest
return User.create({
email,
fullName,
password,
}).then(createdUser => {
const payload = { user: createdUser }
const authToken = jwt.sign(
payload,
process.env.TOKEN_SECRET,
jwtConfig
)
res.status(201).json({
user: createdUser,
authToken: authToken,
})
})
})
.catch(err => console.log(err))
})
And my terminal returns this:
POST /api/auth/login 401 16.919 ms - 39
EDIT 3:
This is my Express app.js:
require("dotenv/config")
require("./db")
const express = require("express")
const app = express()
require("./config")(app)
const allRoutes = require("./routes/index")
app.use("/api", allRoutes)
require("./error-handling")(app)
module.exports = app
You can find the full repo here: https://github.com/JulSeb42/tsx-express-jwt
I am able to sign up a new user, but how can I store that user in a collection in firestore? I am not sure with firebase v9 if the addDoc function is in the right place, but I don't know where else or how to code it.
export const useSignup = () => {
const [error, setError] = useState("");
const { dispatch } = useAuthContext();
const signup = (email: string, password: string, username: string) => {
setError("");
createUserWithEmailAndPassword(auth, email, password)
.then((res) => {
dispatch({ type: "LOGIN", payload: res.user });
const uid = res.user.uid;
const data = {
id: uid,
email,
username,
};
const ref = collection(db, "users");
addDoc(ref, {
data,
});
})
.catch((err) => {
setError(err.message);
});
};
return { error, signup };
};
To create a user-document for a newly signed-up user, you can do the following:
Access their bid from the user object (like you already did).
Create a document reference whose path ends in the above mid. Be aware that this document does not exist yet.
Use the setDoc method with above document reference, and user data to be stored as inputs.
It looks like the following in the form of code:
const uid = res.user.uid;
const data = {
id: uid,
email,
username,
};
const ref = collection(db, `users/${uid}`);
setDoc(ref, data)
.then(() => console.log("Created New User Document Successfully"))
.catch(() => console.log("Error"))
You must write the above code right after your dispatch call. Hope this helps!
I am using firebasev9 authentication for my react project. I have used email authentication for logging in/signing up a user. I want to add phone number too in the user but I am doing all the right steps but when I call updatePhoneNumber with user and phone crediential it throws an error and doesnt add phone number. I am updating the displayName too within the same function which works fine.
I have enabled phone signin in firebase dashboard
The error I am getting is this:
(https://i.postimg.cc/yY55Qzg2/Screenshot-2022-06-25-200735.jpg)
This is my signup function:
const signup = async (email, password, displayName, phoneNumber,phoneCrediential, userType) => {
setError(null);
setIsPending(true);
try {
const res = await createUserWithEmailAndPassword(
auth,
email,
password
);
console.log(res.user);
if (!res) {
throw new Error("Could not complete signUp");
}
debugger;
await updateProfile(res.user, { displayName });
**This updatePhonenumber function throws this error flow moves to catch block**
const resPhone = await updatePhoneNumber(auth.currentUser, phoneCrediential );
console.log(resPhone)
dispatch({ type: "LOGIN", payload: res.user });
console.log(res.user)
addDocument({
name: res.user.displayName,
email: res.user.email,
uid: res.user.uid,
type: userType,
});
if (!isCancelled) {
setError(null);
setIsPending(false);
}
} catch (err) {
if (!isCancelled) {
console.log(err.message);
setError(err.message);
setIsPending(false);
}
}
};
In my component, I take phone number, generate Otp, and take otp and pass phoneCredential to the signup function:
const [verificationIdState, setVerificationIdState] = useState(null);
const [phoneCredientialState, setPhoneCredientialState] = useState(null);
const handleRecaptcha = () => {
window.recaptchaVerifier = new RecaptchaVerifier(
"sign-in-button",
{
size: "invisible",
callback: (response) => {
// reCAPTCHA solved
},
},
auth
);
};
const handleGetOTP = () => {
handleRecaptcha();
const phoneNumber = "+91" + userPhoneNumber;
const applicationVerifier = window.recaptchaVerifier;
const provider = new PhoneAuthProvider(auth);
const verificationId = provider.verifyPhoneNumber(
phoneNumber,
applicationVerifier
);
if (verificationId) {
setVerificationIdState(verificationId);
}
};
const handleOTPSubmit = () => {
const phoneCredential = PhoneAuthProvider.credential(
verificationIdState,
userOTP
);
if (phoneCredential) {
setPhoneCredientialState(phoneCredential);
console.log(phoneCredential);
}
};
//Base Register
const handleRegisterSubmit = (e) => {
e.preventDefault();
signup(
userEmail,
userPassword,
userName,
userPhoneNumber,
phoneCredientialState,
userType
);
};
In my reactJS app, I am using firebase authentication. I can login successfully. I tried to update user profile, like photoUrl and displayName as follows. Data is updated but I can't get the latest update data unless refresh the page.
Please ignore updateCurrentUser function. it is an API call to update display name and photo URL to database.
after updateCurrentUser function callback, I call again
const auth = getAuth();
const currentUser = getAuth().currentUser;
But updated display name and photo is still old data in currentUser. I want to know how can I update profile. I am showing the user info in the header section as well.
updateUser function
async function updateUser(updateUserId, displayName, photoUrl) {
const auth = getAuth();
const currentUser = getAuth().currentUser;
return await updateProfile(auth.currentUser, {
displayName: displayName,
photoUrl: photoUrl,
})
.then(async function () {
const auth = getAuth();
if (auth.currentUser != null) {
updateCurrentUser(updateUserId, displayName, photoUrl)
.then((updatedUser) => {
if (updatedUser) {
const auth = getAuth();
const currentUser = getAuth().currentUser;
setUser(currentUser);
setAccessToken(currentUser.getIdToken(true));
store.dispatch(saveUser(currentUser));
} else {
setUser(null);
setAccessToken(null);
store.dispatch(saveUser(null));
}
})
.catch((error) => {
throw error;
});
}
})
.catch((error) => {
throw error;
});
}
Firebase show updates of user profile only after refresh and automatically after login/logout operation. The only way you can do this will be by using the same values you submitted
async function updateUser(updateUserId, displayName, photoUrl) {
const auth = getAuth();
const currentUser = getAuth().currentUser;
return await updateProfile(auth.currentUser, {
displayName: displayName,
photoUrl: photoUrl,
})
.then(async function () {
const auth = getAuth();
if (auth.currentUser != null) {
updateCurrentUser(updateUserId, displayName, photoUrl)
.then((updatedUser) => {
if (updatedUser) {
const auth = getAuth();
let currentUser = getAuth().currentUser;
currentUser.displayName = displayName;
currentUser.photoUrl = photoUrl
setUser(currentUser);
setAccessToken(currentUser.getIdToken(true));
store.dispatch(saveUser(currentUser));
} else {
setUser(null);
setAccessToken(null);
store.dispatch(saveUser(null));
}
})
.catch((error) => {
throw error;
});
}
})
.catch((error) => {
throw error;
});
}
I make a silly mistake. It is wrong spelling. It should be photoURL instead of photoUrl. It is working when refresh because, at api server side, need to update user info again. At server side, variable name is correct.
I have a login form in with onSubmit property pointing to the following function:
function handleSubmit(event) {
event.preventDefault();
console.log(email);
login(email, password);
}
The login function used above takes email and password from the form and is is defined in another file like this:
export const login = async (email, password) => {
const config = {
headers: {
"Content-Type": "application/json"
}
};
const body = JSON.stringify({ email, password });
const res = await axios.post(
"http://localhost:5000/userauth/login",
body,
config
);
console.log("token: ", res.data);
return res.data;
};
Now I want to redirect the user to a dashboard component after a successful login. How do I approach this? I read that private route is needed for this. But I cannot understand how to use it in my case.
Suppose you want to navigate to /dashboad page after successful login
put this.props.history.push('/dashboard') inside your function of login submit
function handleSubmit(event) {
event.preventDefault();
console.log(email);
login(email, password);
this.props.history.push('/dashboard')
}
I would go with a more robust approach.
You will probably want the user to be redirected to the URL he was intended to and not necessary your default one.
So I would add this code on your login function:
const { history, redirectTarget } = redirect;
history.push(redirectTarget);
and will pass the following data from your Login Component/Container:
let redirectTarget = 'defaultURL';
try {
redirectTarget = this.props.location.state.from/pathname;
}
catch (err) {
redirectTarget = 'defaultURL';
}
this.props.loginRequestAction(
{ username, password },
{ history: this.props.history, redirectTarget }
);