I have built a feature that allows a logged in User to update their email. When I change the email and redirect to a different page the email that is displayed is the old email, and only when I refresh the page does the new email appear. I believe the the best way to address this is for the system to re-authenticate the User (I may be wrong and am open to suggestions).
// UpdateEmail.js
handleSubmit(e) {
e.preventDefault()
this.props.updateEmail(this.state.newEmail)
this.props.history.push('/settings')
}
const mapDispatchToProps = (dispatch) => {
return {
updateEmail: (newEmail) => dispatch(updateEmail(newEmail))
}
}
// authActions.js *NEW*
export const updateEmail = (newEmail, oldEmail, password) => {
return (dispatch, getState, {getFirebase, getFirestore}) => {
const firebase = getFirebase();
let user = firebase.auth().currentUser
let credential = firebase.auth.EmailAuthProvider.credential(oldEmail, password);
user.reauthenticateAndRetrieveDataWithCredential(credential)
.then(() => {
user.updateEmail(
newEmail
).then(() => {
dispatch({ type: 'UPDATE_LOGIN_DETAILS_SUCCESS'})
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
dispatch({
type: 'CHECK_REAUTH_SUCCESS',
user: user
})
} else {
dispatch({ type: 'CHECK_REAUTH_ERROR'})
}
});
})
.catch(err => {
dispatch({ type: 'UPDATE_LOGIN_DETAILS_ERROR'}, err)
})
})
.catch(err => {
dispatch({ type: 'REAUTH_ERROR'}, err)
})
}
}
// Settings.js this is where the user is redirected after CHECK_REAUTH_SUCCESS
const mapStateToProps = (state) => {
return {
licenses: state.firestore.ordered.licenses,
aircraft: state.firestore.ordered.aircraft,
profile: state.firebase.profile,
auth: state.firebase.auth
}
}
// authActions.js *OLD*
export const updateEmail = (newEmail) => {
return (dispatch, getState, {getFirebase, getFirestore}) => {
const firebase = getFirebase();
const user = firebase.auth().currentUser
user.updateEmail(
newEmail
).then(() => {
dispatch({ type: 'UPDATE_LOGIN_EMAIL_SUCCESS'})
}).catch(err => {
dispatch({ type: 'UPDATE_LOGIN_EMAIL_ERROR', err })
})
}
}
Related
I am working on my test for a new project and have them set up but am running into an async issue with jest.
I start with jest.mock('axios');
And the working test is
it('Dispatches SNACK_BAR after submitting ForgotPassword', async () => {
let store = configureStore({ reducer: {
auth: authReducer,
snackBar: snackBarReducer
}})
axios.post.mockResolvedValueOnce({headers: {
authorization: 'asdasdasdas'
},
status: 200});
await store.dispatch(forgotPasswordActions.forgotPasswordPost('test#test.com', (path) => {}))
expect(store.getState().snackBar).toEqual({"message": "Check your email for a reset link", "severity": "success", "timestamp": store.getState().snackBar.timestamp});
});
But when I try the fail case
it('Dispatches SNACK_BAR after submitting ForgotPassword with an error', async () => {
let store = configureStore({ reducer: {
auth: authReducer,
snackBar: snackBarReducer
}})
axios.post.mockRejectedValueOnce({response: {headers: {
authorization: 'asdasdasdas'
},
status: 500,
data: {
error: 'Error'
}}});
await store.dispatch(forgotPasswordActions.forgotPasswordPost('test#test.com', (path) => {}))
expect(store.getState().snackBar).toEqual({"message": "Error", "severity": "error"});
})
The expect doesn't wait for the dispatch to resolve. If I change the mockRejectedValueOnce to mockResolvedValueOnce then I get a similar result to the first test. It seems the only difference is mockRejectedValueOnce but I am not sure why
export const forgotPasswordPost = ({ email }, navigate) => async dispatch => {
axios.post(`${ROOT_URL}/auth/password`,
{ "user": { email: email }})
.then(response => {
return dispatch({ type: SNACK_BAR, payload: ["Check your email for a reset link", "success"]})
})
.catch(response => {
return dispatch({ type: SNACK_BAR, payload: ["Error", "error"]});
})
}
The function that is being tested
In forgotPasswordPost you return async function async dispatch => {}. This function has to return a promise. But currently it is void
/// original function
export const forgotPasswordPost = ({ email }, navigate) => async dispatch => {
axios.post(`${ROOT_URL}/auth/password`,
{ "user": { email: email }})
.then(response => {
return dispatch({ type: SNACK_BAR, payload: ["Check your email for a reset link", "success"]})
})
.catch(response => {
return dispatch({ type: SNACK_BAR, payload: ["Error", "error"]});
})
}
/// Try to to return THE Promise, not you return the axios.post promise chain
export const forgotPasswordPost = ({ email }, navigate) => async dispatch => {
return axios.post(`${ROOT_URL}/auth/password`,
{ "user": { email: email }})
.then(response => {
return dispatch({ type: SNACK_BAR, payload: ["Check your email for a reset link", "success"]})
})
.catch(response => {
return dispatch({ type: SNACK_BAR, payload: ["Error", "error"]});
})
}
/// ANother proposal using async await
export const forgotPasswordPost = ({ email }, navigate) => async dispatch => {
try {
const response = await axios.post(`${ROOT_URL}/auth/password`, { "user": { email: email }});
dispatch({ type: SNACK_BAR, payload: ["Check your email for a reset link", "success"]})
} catch (e) {
dispatch({ type: SNACK_BAR, payload: ["Error", "error"]});
}
}
I want to add a Clickable “Remember Me” checkbox in my login page that tells the browser to save a cookie so that if you close out the window for the site without signing out, the next time you go back, you will be signed back in automatically.that can save username and password
export const getUser = () => {
const userStr = sessionStorage.getItem("user");
if (userStr) return JSON.parse(userStr);
else return null;
};
export const getToken = () => {
return sessionStorage.getItem("token") || null;
};
export const setUserSession = (token, user) => {
sessionStorage.setItem("token", token);
sessionStorage.setItem("user", JSON.stringify(user));
};
export const removeUserSession = () => {
sessionStorage.removeItem("token");
sessionStorage.removeItem("user");
};
export const handleSuccessfulLogin = async (token, rememberMe) => {
localStorage.setItem("token", token);
localStorage.setItem("rememberme", rememberMe);
};
export const handleLogout = () => {
localStorage.clear();
};
This is my login that work with api
const handelLogin = () => {
setError(null);
setLoading(true);
axios
.post("https://www.mecallapi.com/api/login", {
username: username,
password: password,
})
.then((response) => {
setLoading(false);
setUserSession(response.data.token, response.data.user);
navigate("/Dashboard");
})
.catch((error) => {
setLoading(false);
if (error.response.status === 401 || error.response.status === 400) {
setError(error.response.data.message);
} else {
setError("somthing went wrong ,please try again");
}
});
};
This is my remember me checkbox
<div className="login-bottom">
<Checkbox {...label} />
</div>
I am working on authentication for my react native app.
The problem I am having is that the signInUser function seems to be executing in the wrong order. I want the signIn function to fully execute before moving on.
However, that doesn't seem to be happening as I am getting this response in my console, with the "undefined" coming from console.log(response) in the SignInScreen.
Here is the console:
undefined
16d0707a3508a9b43b8c36c8574ca73d8b4b26af
I have this function in the SignInScreen.js
import { signIn } from "../services/authService";
import { useAuthDispatch } from "../contexts/authContext";
const SignInScreen = ({ navigation }) => {
const dispatch = useAuthDispatch();
const [signInLoading, setSignInLoading] = useState(false);
const signInUser = async (values) => {
const { email, password } = values;
setSignInLoading(true);
signIn(email, password)
.then((response) => {
console.log(response);
dispatch({
type: "SIGN_IN",
token: response,
});
})
.catch((e) => {
console.log(e);
})
.finally(() => setSignInLoading(false));
};
And this is my authService.js:
import axios from "axios";
const signIn = async (email, password) => {
axios
.post("http://127.0.0.1:8000/rest-auth/login/", {
username: email,
email: email,
password: password,
})
.then((response) => {
console.log(response.data.key);
return response.data.key;
})
.catch((error) => {
return error;
});
};
How can I fix this?
I want to set user from auth to database. I do it by .set and it is working good but if I have some fields for one user, then all data are reset when user will sign in again. For this example collection -> users -> docs: (name, color). The name will be static after login but color could be changing and remember to the next logging.
On the login I set:
const signIn = () => {
auth
.signInWithPopup(provider)
.then(result => {
dispatch({
type: actionTypes.SET_USER,
user: result.user
});
db.collection('users').doc(result.user.uid)
.set({
name: result.user.displayName,
color: 'blue'
})
// add user list
db.collection('users').onSnapshot(snapshot => (
dispatch({
type: actionTypes.SET_USER_LIST,
payload: (
snapshot.docs.map(doc => ({
data: doc.data()
}))
)
})
))
})
.catch(error => alert(error.message));
}
And in another file when the user clicks on the setting and changes his color is also changing in the database, but when he login again the function signIn reset this color.
const changeColor = e => {
let colorVal = e.target.value;
db.collection('users').doc(user.uid).update({
color: colorVal
})
setOpenColors(!openColors)
};
So question is how to check if(userExist)/ filter or sth like this between .doc(result.user.uid)
and set(). I tried to do this by .where in firebase, setting data to reducer but always was the same result, so I decide to ask you.
If I correctly understand your question, you should query for the user doc in the signIn() function, in order to check if it already exists or not, as follows:
const signIn = () => {
auth
.signInWithPopup(provider)
.then(result => {
dispatch({
type: actionTypes.SET_USER,
user: result.user
});
db.collection('users').doc(result.user.uid)get()
.then((doc) => {
if (!doc.exists) {
return db.collection('users').doc(result.user.uid)
.set({
name: result.user.displayName,
color: 'blue'
});
} else {
return null;
}
})
.then(() => {
db.collection('users').onSnapshot(snapshot => (
dispatch({
type: actionTypes.SET_USER_LIST,
payload: ( snapshot.docs.map(doc => ({data: doc.data()})))
})
})
})
.catch(error => alert(error.message));
}
I have read articles that saving the token in localstorage is dangerous to XSS attack. So I have decided to store my tokens in cookies. And I am using react-cookie. I saw the examples and I am trying to do it but my auth.js consists of const and is not a class, so I do not know how to use the withCookies() with it, this is my auth.js where I want to store the token to the cookies:
import {
LOGIN,
LOGIN_SUCCESS,
LOGIN_FAILED,
GET_USER_DATA,
GET_USER_DATA_SUCCESS,
GET_USER_DATA_FAILED,
LOGOUT,
LOGOUT_SUCCESS,
LOGOUT_FAILED,
} from './types'
import axios from 'axios'
var api = require ('./../api.js');
export const login = (email, pass) => {
return (dispatch) => {
dispatch({
type: LOGIN
})
var url = api.logInApi
axios.post(url, {
email: email,
password: pass
})
.then(res => {
dispatch({
type: LOGIN_SUCCESS,
payload: res.data
})
localStorage.setItem('token', res.data.token)
dispatch(getUserData())
})
.catch(err => dispatch({
type: LOGIN_FAILED,
payload: err
}))
}
}
export const getUserData = () => {
return (dispatch) => {
dispatch({
type: GET_USER_DATA
})
var url = api.getUserDataApi
axios.post(url, {}, {headers: {
"Authorization": `Bearer ${localStorage.getItem("token")}`
}})
.then(res => {
dispatch({
type: GET_USER_DATA_SUCCESS,
payload: res.data
})
})
.catch(err => dispatch({
type: GET_USER_DATA_FAILED,
payload: err
}))
}
}
export const logout = () => {
return (dispatch) => {
dispatch({
type: LOGOUT
})
var url = api.logoutApi
axios.post(url, {}, {headers: {
"Authorization": `Bearer ${localStorage.getItem("token")}`
}})
.then(res => {
window.location.replace("")
dispatch({
type: LOGOUT_SUCCESS,
payload: res.data
})
})
.catch(err => dispatch({
type: LOGOUT_FAILED,
payload: err
}))
}
}
Now, I tried doing this and of course it doesn't work:
import {
LOGIN,
LOGIN_SUCCESS,
LOGIN_FAILED,
GET_USER_DATA,
GET_USER_DATA_SUCCESS,
GET_USER_DATA_FAILED,
LOGOUT,
LOGOUT_SUCCESS,
LOGOUT_FAILED,
} from './types'
import axios from 'axios'
import { withCookies, Cookies } from 'react-cookie'; <<added this
var api = require ('./../api.js');
const login = (email, pass) => {
return (dispatch) => {
dispatch({
type: LOGIN
})
const { cookies } = props; <<added this
var url = api.logInApi
axios.post(url, {
email: email,
password: pass
})
.then(res => {
dispatch({
type: LOGIN_SUCCESS,
payload: res.data
})
cookies.set('token', res.data.token, { path: '/' }); <<added this
dispatch(getUserData())
})
.catch(err => dispatch({
type: LOGIN_FAILED,
payload: err
}))
}
}
export default withCookies(login) <<added this(wrong)
const getUserData = () => {
return (dispatch) => {
dispatch({
type: GET_USER_DATA
})
const { cookies } = props; <<added this
var token = cookies.get('token'); <<added this
var url = api.getUserDataApi
axios.post(url, {}, {headers: {
"Authorization": `Bearer ${token}` <<added this(this is where I wanna get the cookie)
}})
.then(res => {
dispatch({
type: GET_USER_DATA_SUCCESS,
payload: res.data
})
})
.catch(err => dispatch({
type: GET_USER_DATA_FAILED,
payload: err
}))
}
}
export default withCookies(getUserData) <<added this(wrong)
const logout = () => {
return (dispatch) => {
dispatch({
type: LOGOUT
})
const { cookies } = props;
var token = cookies.get('token');
var url = api.logoutApi
axios.post(url, {}, {headers: {
"Authorization": `Bearer ${token}` <<added this
}})
.then(res => {
window.location.replace("")
dispatch({
type: LOGOUT_SUCCESS,
payload: res.data
})
})
.catch(err => dispatch({
type: LOGOUT_FAILED,
payload: err
}))
}
}
export default withCookies(logout) <<added this(wrong)
this one is wrong because there should only be one export default. But I don't know how to implement withCookies to const and there are also these ones that are included in the example and I don't know if I need them or where do I put them:
static propTypes = {
cookies: instanceOf(Cookies).isRequired
};
constructor(props) {
super(props);
const { cookies } = props;
this.state = {
name: cookies.get('name') || 'Ben'
};
}
and also, another question is that, I can access my cookies anywhere in my project right? just like how localstorage is accessible to my project?
I hope someone can help me and I am a newbie to this. I have never used cookies before so thank you for your consideration.
I personally would rather using js-cookie to write/read cookies.
It has a very basic API:
Cookie.set('cookie_name', 'value') // will set "cookie_name" to "value"
Cookie.get('cookie_name') // will return "value"
Which means:
const login = (email, pass, cookie) => {
return (dispatch) => {
dispatch({
type: LOGIN
})
var url = api.logInApi
axios.post(url, {
email: email,
password: pass
})
.then(res => {
dispatch({
type: LOGIN_SUCCESS,
payload: res.data
})
cookies.set('token', res.data.token);
dispatch(getUserData())
})
.catch(err => dispatch({
type: LOGIN_FAILED,
payload: err
}))
}
}
Passing to the login funuction js-cookie's Cookie in the 3rd argument.
Now, you can still use the same react-cookie package to read the cookie values (I believe there shouldn't be any conflicts). Or you can replace it with js-cookie. To do that, however, you will have to pass the Cookie object to props. I probably would do that using mapStateToProps if you're using Redux or just by simply passing it through JSX