axios get request not working inside useEffect - javascript

I am using axios get request to check if user logged in or not with jwt, However, when app launched it keeps showing the loading state is it set to true in the first time the app launch then it supposes to make get request and validate the user then set loading state to false and navigate to a specific route. what i am getting is loading state is true all time and request not send to backend server.
here is the function to check if user logged in or not:
useEffect(() => {
const checkLoggedIn = async () => {
const Token = await AsyncStorage.getItem('Token');
if (Token) {
axios.get('http://localhost:3000/isUserAuth', {
headers: {
'x-access-token': Token
}
}).then((res) => {
setUser(res.data.user)
AsyncStorage.setItem("Token", res.token);
setIsLoading(false);
}).catch((error) => {
console.log(error)
setIsLoading(false);
});
} else {
setUser(null);
setIsLoading(false)
}
}
checkLoggedIn();
}, []);
and this is the backend:
app.get('/isUserAuth', verifyJWT, (req, res) => {
const token = req.headers['x-access-token'];
let sqlCheck = `SELECT * FROM users where id =?`;
CON.query(sqlCheck, req.user, (err, user) => {
if (user) {
console.log(user)
return res.status(400).json({ auth: true, user: user, Token: token })
}
})
})
Hope someone help me identifying the problem. thanks

If your loading state is not changing to false that signals to me that it's not a problem with your database call because even if your call is failing the else should trigger and still set loading to false.
Might consider narrowing down the function complexity and building up from there. Maybe something like the following to make sure your loading state is correctly updating:
useEffect(() => {
const checkLoggedIn = async () => {
setUser(null);
setIsLoading(false)
}
checkLoggedIn();
}, []);

What about setIsLoading to true when the component mounts, then run the async/await based on its value?
useEffect(() => {
setIsLoading(false);
if (!setIsLoading) {
try {
// fetch code
} catch(e) {
//some error handling
} finally {
// something to do whatever the outcome
}
}
})

// I found the solution
the problem is from the backend server where user returns undefined and the mistake i made that only check if(user) and didn't set else which not give a response back to font-end which indicate that state keep true
so backend code should be like:
CON.query(sqlCheck, req.user, (err, user) => {
if (user) {
return res.status(200).json({ auth: true, user: user, Token: token })
}else{ return res.status(400).json({ auth: false, user: null })}
})

Related

userSession is null after Auth.signUp with "autoSignIn" enabled (AWS Cognito)

I need to get the jwtToken from the Auth.signUp. Is this possible if i enable autoSignIn:{enabled:true}?
const signUp = async () => {
await Auth.signUp({
username: email,
password,
attributes: {
email, // optional
name,
},
autoSignIn:{
enabled: true
}
})
.then((data) => {
console.log(data.user); //user.signInUserSession is null
})
.catch((err) => {
if (err.message) {
setInvalidMessage(err.message);
}
console.log(err);
});
await Auth.currentAuthenticatedUser()
.then(user =>{
console.log(user)
})
.catch(error => {
console.log(error) //"User is not authenticated"
})
};
I call I want the jwttoken from the userSession data for conditional rendering and I store the token in my router.js. The response object from Auth.signUp contains a CognitoUser which has a signInUserSession value but its's null.
EDIT: Tried to call Auth.currentAuthenticatedUser() after but yields an error that user is not authenticated. But when i restart my app, the user will be authenticated. I still cant authenticate user on the same app "instance"
import { Auth, Hub } from 'aws-amplify';
const listener = (data) => {
switch (data.payload.event) {
case 'autoSignIn':
console.log('auto sign in successful');
console.log(data.payload) //returns user data including session and tokens.
//other logic with user data
break;
}
};
Above is the code to initalize the Hub listener provided by amplify api. Ater user presses sign up, I called to get user session data when user is automatically signed in.
Hub.listen('auth', listener)

vue-router beforeEach function does not return item which is updated

I'm stuck with a problem. So here is the scenario. I put an axios request which takes the access token from cookies on store. Then I committed a mutation to make true isLoggedIn variable. Then I access this variable from Navbar to change menu items. It works. But when I try to access isLoggedIn variable with getters in beforeEach function, it turns still false. But it is true on Navbar.
user/actions.js which I request to backend to for authentication.
import axios from 'axios'
const checkUser = ({ commit }) => {
axios({
method: 'get',
url: 'http://localhost:3000/api/auth/VpW02cG0W2vGeGXs8DdLIq3dQ62qMd0',
withCredentials: true,
headers: {
Accept: "application/json",
},
})
.then((res) => {
commit('defineUser', res.data)
return true
})
.catch((err) => {
console.log(err)
return false
})
}
export default {
checkUser,
}
user/mutations.js which I set user and isLoggedIn variables
const defineUser = (state, res) => {
state.user = res.user
state.isLoggedIn = true
}
export default {
defineUser,
}
Then I call that action func in beforeEach in router
router.beforeEach(async (to, from, next) => {
const accessToken = VueCookies.get('access_token')
if (accessToken) { // first I check if there is an access token. I do that because I check every request if it is logged in. So I can manage Navbar.
await store.dispatch('user/checkUser')
if (store.getters['user/isLoggedIn']) { // HERE IS THE PROBLEM IT TURNS FALSE HERE. if there is an access token, then I check it with if mutation made isLoggedIn true and all doors are open for that user
next()
} else { // if it is false then show a toast and remove access token and reload the page
router.app.$bvToast.toast('You need to log in to see this page!', { // another question, when I deleted async it cannot read toast with only getter. If I put something else it works perfectly.
title: 'Unathorized',
variant: 'danger',
solid: true
})
VueCookies.remove('access_token')
router.go(router.currentRoute)
}
} else if (to.meta.requiresAuth) { // so if there is no access token and this page requires auth so show an toast
router.app.$bvToast.toast('You need to log in to see this page!', {
title: 'Unathorized',
variant: 'danger',
solid: true
})
} else { // if no requires auth and no access token then just get in the page
next()
}
})
If you need any other information please say, so I can share with you. Any help will be appreciated.
You are awaiting checkUser but it doesn't return a promise. Change it to:
const checkUser = ({ commit }) => {
return axios({ // notice the `return` statement
...
}
Alternatively, you could use async/await:
const checkUser = async ({ commit }) => { // async
await axios({ // await
...
}

How can I handle a vuex dispatch response?

I'm raising the white flag and asking for suggestions even though I feel the answer is probably right in front of my face.
I have a login form that I am submitting to an api (AWS) and acting on the result. The issue I am having is once the handleSubmit method is called, I am immediately getting into the console.log statement... which to no surprise returns dispatch result: undefined
I realize this is likely not a direct function of vue.js, but how I have the javascript set executing.
Here is my login component:
// SignInForm.vue
handleSubmit() {
try {
const {username, password} = this.form;
this.$store.dispatch('user/authenticate', this.form).then(res => {
console.log('dispatch result: ', res);
});
} catch (error) {
console.log("Error: SignInForm.handleSubmit", error);
}
},
...
Here is what my store is doing. I'm sending it to a UserService I've created. Everything is working great. I am getting the correct response(s) and can log everything out I need. The UserService is making an axios request (AWS Amplify) and returning the response.
// user.js (vuex store)
authenticate({state, commit, dispatch}, credentials) {
dispatch('toggleLoadingStatus', true);
UserService.authenticate(credentials)
.then(response => {
dispatch('toggleLoadingStatus', false);
if (response.code) {
dispatch("setAuthErrors", response.message);
dispatch('toggleAuthenticated', false);
dispatch('setUser', undefined);
// send error message back to login component
} else {
dispatch('toggleAuthenticated', true);
dispatch('setUser', response);
AmplifyEventBus.$emit("authState", "authenticated");
// Need to move this back to the component somehow
// this.$router.push({
// name: 'dashboard',
// });
}
return response;
});
},
...
Where I'm getting stuck at is, if I have error(s) I can set the errors in the state, but I'm not sure how to access them in the other component. I've tried setting the data property to a computed method that looks at the store, but I get errors.
I'm also struggling to use vue-router if I'm successfully authenticated. From what I've read I really don't want to be doing that in the state anyway -- so that means I need to return the success response back to the SignInForm component so I can use vue-router to redirect the user to the dashboard.
Yep. Just took me ~6 hours, posting to SO and then re-evaluating everything (again) to figure it out. It was in fact, somewhat of a silly mistake. But to help anyone else here's what I was doing wrong...
// SignInForm.vue
async handleSubmit() {
try {
await this.$store.dispatch("user/authenticate", this.form)
.then(response => {
console.log('SignInForm.handleSubmit response: ', response); // works
if (response.code) {
this.errors.auth.username = this.$store.getters['user/errors'];
} else {
this.$router.push({
name: 'dashboard',
});
}
}).catch(error => {
console.log('big problems: ', error);
});
} catch (error) {
console.log("Error: SignInForm.handleSubmit", error);
}
},
...
Here's my first mistake: I was calling from an async method to another method - but not telling that method to be async so the call(er) method response was executing right away. Here's the updated vuex store:
// user.js (vuex store)
async authenticate({state, commit, dispatch}, credentials) { // now async
dispatch('toggleLoadingStatus', true);
return await UserService.authenticate(credentials)
.then(response => {
console.log('UserService.authenticate response: ', response); // CognitoUser or code
dispatch('toggleLoadingStatus', false);
if (response.code) {
dispatch("setAuthErrors", response.message);
dispatch('toggleAuthenticated', false);
dispatch('setUser', undefined);
} else {
dispatch('toggleAuthenticated', true);
dispatch('setUser', response);
AmplifyEventBus.$emit("authState", "authenticated");
}
return response;
});
},
...
My second error was that I wasn't returning the result of the method at all from the vuex store.
Old way:
UserService.authenticate(credentials)
Better way:
return await UserService.authenticate(credentials)
Hope this saves someone a few hours. ¯_(ツ)_/¯
This works for Vue3:
export default {
name: 'Login',
methods: {
loginUser: function () {
authenticationStore.dispatch("loginUser", {
email: 'peter#example.com',
})
.then(response => {
if (response.status === 200) {
console.log('Do something')
}
});
},
},
}
In the store you can simply pass back the http response which is a promise.
const authenticationStore = createStore({
actions: {
loginUser({commit}, {email}) {
const data = {
email: email
};
return axios.post(`/authentication/login/`, data)
.then(response => {
toastr.success('Success')
return response
})
},
}
})

Where to put API session auth token in SDK request methods?

I am using the ConnectyCube React Native SDK and have obtained an app auth token using their API. This token is required when making further requests - for example when logging in as a user. Their documentation says:
Upgrade session token (user login)
If you have an application session token, you can upgrade it to a user session by calling login method:
var userCredentials = {login: 'cubeuser', password: 'awesomepwd'};
ConnectyCube.login(userCredentials, function(error, user) {
});
The problem is it that when I use this method, I get an error in response saying 'Token is required'.
If I were interfacing with a REST API, I would put the token in the header of the request, but obviously in this instance I can't. So the question is, where do I put the token? I have it, the documentation just doesn't tell you how to use it! Any help appreciated.
Ok I came up with a fix. First of all I just tried passing the auth token in to the userCredntials object in the same way as in the documentation for social auth, that is absent from the description in my above code snippet taken from their docs.
Then I Promisified the API calls from within useEffect inside an async function to make sure everything was happening in the right order, and it works:
export default function App() {
const createAppSession = () => {
return new Promise((resolve, reject) => {
ConnectyCube.createSession((error, session) => {
!error
? resolve(session.token)
: reject(error, '=====1=====');
});
})
}
const loginUser = (credentials) => {
return new Promise((resolve, reject) => {
ConnectyCube.login(credentials, ((error, user) => {
!error
? resolve(user)
: reject(error, '=====2=====');
}));
})
}
useEffect(() => {
const ccFunc = async () => {
ConnectyCube.init(...config)
const appSessionToken = await createAppSession();
const userCredentials = { login: 'xxxxx', password: 'xxxxxxx', keys: { token: appSessionToken } };
const user = await loginUser(userCredentials);
console.log(user);
}
ccFunc()
}, []);
Hope it works....
please implement it by yourself...just take an understanding from code below.
code says: send the username and password to api...if all ok then authenticate else throw error ...if all ok..then store the returned token is asyncStorage...you can create the storage by any name you like...and use the token eveywhere in your app.
SignInUser = async () => {
this.setState({
username: this.state.username,
password:this.state.password,
})
if(this.state.username && this.state.password !== null){
try{
this.setState({
loading:true
})
const response = await fetch('YOUR API', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
username: this.state.username,
password: this.state.password
})
});
var promiseResponse = await response.json()
console.log(promiseResponse.token);
try {
await AsyncStorage.setItem('STORE_YOUR_LOGIN_TOKEN_HERE', JSON.stringify(promiseResponse.token));
console.log('Token Stored In Async Storage');
let tokenFromAsync = await AsyncStorage.getItem('STORE_YOUR_LOGIN_TOKEN_HERE');
console.log('Getting Token From Async...')
tokenFromAsync = JSON.parse(tokenFromAsync)
if(tokenFromAsync !== null){
console.log(tokenFromAsync);
this.setState({
loading:false
})
this.props.navigation.navigate('Tabnav');
}
} catch (error) {
// saving error
console.log(`ERROR OCCURED ${error}`)
}
//this.props.navigation.navigate('Tabnav')
} catch(error){
console.log(`COULDN'T SIGN IN ${error}`)
}
} else {
this.setState({
msg:'Invalid Credentials',
label:'red'
});
}
}
This is how i got the login to work in their sample react native app 1. i created a credentials object like this in my custom login function in src>components>AuthScreen>AuthForm.js
var credentials = {id:'',login: this.state.login,password: this.state.password}
2.I used their _signIn(credentials) function and set the 'id' attribute of my credentials object after their UserService.signin(credentials) resolved with a user object. (the resolved user object contained the logged-in user's id i.e user.id). Then it worked. This is how the code looked for the signin after the little tweak.
loginUser() { //my custom signin function
var credentials = {id:'',login: this.state.login,password: this.state.password} //my credentials object
this._signIn(credentials)
}
_signIn(userCredentials) { //their signin function
this.props.userIsLogging(true);
UserService.signin(userCredentials)
.then((user) => {
userCredentials.id = user.id //setting id of my credentials object after promise resolved
ChatService.connect(userCredentials) //using my credentials object with id value set
.then((contacts) => {
console.warn(contacts)
this.props.userLogin(user);
this.props.userIsLogging(false);
Actions.videochat(); //login worked
})
.catch(e => {
this.props.userIsLogging(false);
alert(`Error.\n\n${JSON.stringify(e)}`);
})
})
.catch(e => {
this.props.userIsLogging(false);
alert(`Error.\n\n${JSON.stringify(e)}`);
})
}

How to wait for async data before send response using promises and arrow functions?

I'm new to ES6, arrow functions and promises, and I can't figure out how to use them, even worse together.
I started a project with a REST generator (https://github.com/diegohaz/rest) and it works fine, but I need to modify part of the authentication.
I need to return data from a third-party server during authentication. I created the function that returns the data correctly with axios, however I can't return this information along with the other information (from this project), response is sent before.
Below is the generated code, almost untouchable, I added just extraData: user.getExtraData(user)
// function in auth controller file
export const login = ({ user }, res, next) => {
sign(user.id)
.then((token) => ({
token, user: user.view(true), extraData: user.getExtraData(user)
}))
.then(success(res, 201))
.catch(next)
}
// function in user model file
view (full) {
let view = {}
let fields = ['id', 'name', 'picture']
if (full) {
fields = [...fields, 'email', 'createdAt']
}
fields.forEach((field) => {
view[field] = this[field]
})
return view
}
Here is my function added into the user model
getExtraData (userView) {
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'
axios.post( userView.host, querystring.stringify( {
data1:userView.data1,
data2:userView.data2
}))
.then((response) => {
return response.data
})
.catch((error) => {
console.log('Error', error)
return null
})
}
How would the best way to make response wait until extraData is return from getExtraData function with the given code ? Thanks
You can use async/await. In that case, you need to await for getExtraData. For such reason, the anonymous function inside login and getExtraData both need to be declared as asynchronous functions:
// function in auth controller file
export const login = ({ user }, res, next) => {
sign(user.id)
.then(async (token) => ({
token,
user: user.view(true),
// Wait for getExtraData to finish using await
extraData: await user.getExtraData(user)
}))
.then(success(res, 201))
.catch(next)
}
async getExtraData (userView) {
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'
try {
const response = await axios.post( userView.host, querystring.stringify( {
data1:userView.data1,
data2:userView.data2
}))
return response.data
}
catch (err){
return null
}
}

Categories

Resources