Getting a 401 error when trying to create a new post - react-native

I am trying to create a post using an app built in react native but everytime I try creating it gives me a 401 error after I have already logged in. I assume it isn't getting a token from AsyncStorage. I need helping.
This is the ItemContext where the functionality for creating a post-
import createDataContext from "./createDataContext";
import sellerApi from "../api/seller";
import { navigate } from "../navigationRef";
const itemReducer = (state, action) => {
switch (action.type) {
case "fetch_items":
return action.payload;
case "create_item":
return { errorMessage: "", item: action.payload };
default:
return state;
}
};
const fetchItems = dispatch => async () => {
const response = await sellerApi.get("/api/items");
console.log(response.data);
dispatch({ type: "fetch_items", payload: response.data });
};
const createItem = dispatch => async (
title,
category,
detail,
condition,
price
) => {
try {
const response = await sellerApi.post("/api/items", {
title,
category,
detail,
condition,
price
});
//this is the other place the error might be happening i need this to save in the phone local storage
console.log(response.data);
dispatch({ type: "create_item", payload: response.data });
navigate("Home");
} catch (err) {
console.log(err);
}
};
export const { Provider, Context } = createDataContext(
itemReducer,
{ createItem, fetchItems },
[]
);
this is the AuthContext where the signin and signup functionality is located and the AsyncStorage is used. Let me know if you guys need to see the node function for Auth.
import createDataContext from "./createDataContext";
import sellerApi from "../api/seller";
import { navigate } from "../navigationRef";
const authReducer = (state, action) => {
switch (action.type) {
case "add_error":
return { ...state, errorMessage: action.payload };
case "signup":
return { errorMessage: "", token: action.payload };
case "signin":
return { errorMessage: "", token: action.payload };
case "fetch_user":
return action.payload;
case "clear_error_message":
return { ...state, errorMessage: "" };
case "signout":
return { token: null, errorMessage: "" };
default:
return state;
}
};
const tryLocalSignin = dispatch => async () => {
const token = await AsyncStorage.getItem("token");
if (token) {
dispatch({ type: "signin", payload: token });
navigate("Home");
} else {
navigate("loginFlow");
}
};
const clearErrorMessage = dispatch => {
dispatch({ type: "clear_error_message" });
};
const signup = dispatch => async ({ name, phone, email, password }) => {
try {
const response = await sellerApi.post("/api/users", {
name,
phone,
email,
password
});
//this is the other place the error might be happening i need this to save in the phone local storage
await AsyncStorage.setItem("token", response.data);
console.log(response.data);
dispatch({ type: "signup", payload: response.data.token });
navigate("Home");
} catch (err) {
dispatch({ type: "add_error", payload: "FAIL" });
}
};
const signin = dispatch => async ({ email, password }) => {
try {
const response = await sellerApi.post("/api/auth", {
email,
password
});
await AsyncStorage.setItem("token", response.data);
console.log(response.data);
dispatch({ type: "signin", payload: response.data.token });
navigate("Home");
} catch (err) {
dispatch({ type: "add_error", payload: "FAIL" });
}
};
// const fetchUser = dispatch => async () => {
// const response = await sellerApi.get("/auth");
// dispatch({ type: "fetch_user", payload: response.data });
// };
//need to get the users info to display it in the accountScreen
const signout = dispatch => async () => {
await AsyncStorage.removeItem("token");
dispatch({ type: "signout" });
navigate("loginFlow");
};
export const { Provider, Context } = createDataContext(
authReducer,
{ signup, signin, signout, tryLocalSignin },
{ token: null, errorMessage: "" }
);
This is the backend for the Auth function that makes sure the user is logged in before begin able to send a post request----
const jwt = require("jsonwebtoken");
const config = require("config");
module.exports = function (req, res, next) {
const token = req.header("x-auth-token");
if (!token) return res.status(401).send("Access denied");
try {
const decoded = jwt.verify(token, config.get("jwtPrivateKey"));
req.user = decoded;
next();
} catch (ex) {
res.status(400).send("Invalid token.");
}
}
this is where the post request for when you signup and login is pretty much similar-
router.post("/", async (req, res) => {
const { error } = validate(req.body);
if (error) return res.status(400).send(error.details[0].message);
let user = await User.findOne({ email: req.body.email });
if (user) return res.status(400).send("User already registered.");
user = new User(_.pick(req.body, "name", "phone", "email", "password"));
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(user.password, salt);
await user.save();
const token = user.generateAuthToken();
res.header("x-auth-token", token).send(token);
});
PLEASE HELP

Importing Async storage like this import {AsyncStorage} from 'react-native'; has been deprecated. You can check here async storage .
Thats why i suppose the AsyncStorage is not working, try downloading this rn-community-async-storage . package first and then import AsyncStorage like
import AsyncStorage from '#react-native-community/async-storage';
hope it helps. feel free for doubts

Related

Vue3 Errorhandling in Vuex , how to catch error, when you indirectly dispatch a naction?

auth(){} is to send request to the server. For, authentication ,login and signUp url is different. So I use two actions to assign the url by mode "login/signUp". then "login" and "signUp" will dispatch auth(){}
In the past, I directly put send request function fetch () in login() / signUp(). when error throw out ,I can catch it by use try {} catch{}.
Now, I want to reduct the duplicated code . The problem is , the error will be throw out by auth(), in Vue3 component, what I dispatch is not auth(), it is login() / signUp().
How can I get the error? will it be possible to pass the error through login()/signUp(),then I can get it ?
updated:
auth.vue
async setUser() {
if (this.mode === "sign up") {
if (this.emailIsValid === true && this.passwordsIsValid === true) {
const payload = {
email: this.email,
password: this.password,
};
await this.$store.dispatch("homepage/signUp", payload);
}
} else if (this.mode === "login") {
if (this.emailIsValid === true && this.passwordsIsValid === true) {
this.isLoading = true;
const payload = {
email: this.email,
password: this.password,
};
try {
await this.$store.dispatch("homepage/login", payload);
} catch (err) {
this.isLoading = false;
// console.log(err);
this.error = err.message || "something went wrong";
}
}
}
},
Vuex actions.js
async signUp (context, payload) {
context.dispatch('auth', {
...payload,
mode: 'sign up'
})
},
async login (context, payload) {
await context.dispatch('auth', {
...payload,
mode: 'login'
})
},
async auth (context, payload) {
let url = ''
if (payload.mode === 'sign up') {
url =
'https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=[api key]'
} else if (payload.mode === 'login') {
url =
'https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=[api key]'
}
const res = await fetch(url, {
method: 'POST',
body: JSON.stringify({
email: payload.email,
password: payload.password,
returnSecureToken: true
})
})
const resData = await res.json()
if (!res.ok) {
// console.log(resData)
const error = new Error(resData.error.message || 'Try again later ')
throw error
}
}
}
just pass error the bridge action .
add another try{} catch {}
vuex actions.js
async login (context, payload) {
try {
await context.dispatch('auth', {
...payload,
mode: 'login'
})
} catch (err) {
throw err
}
}

React Navigation taking me to home screen from login screen even if the credentials are right or wrong. Also, no error Alert is being displayed

Loginhandler is the function which evokes after clicking log in button. Below are the pieces of two files. LoginScreen is my login.js file where as Action is my action file. I have made reducer file too, but my focus is to get the username and the password from input field, send it to action file using loginhandler function and on success, opens up my Home Screen and on Error, the Alert pops up.
----------------Login Screen------------
useEffect(() => {
if (error) {
Alert.alert("An Error Occurred!", error, [{ text: "Okay" }]);
}
}, [error]);
const loginHandler = async () => {
let action = authActions.login(
formState.inputValues.username,
formState.inputValues.password
);
setError(null);
setIsLoading(true);
try {
await dispatch(action);
props.navigation.navigate("PostAuth");
} catch (err) {
setError(err);
setIsLoading(false);
}
};
-----------------ACTION FILE-------------------
const axios = require("axios");
export const LOGIN = "LOGIN";
export const login = (username, password) => {
const params = new URLSearchParams();
params.append("username", username);
params.append("password", password);
return async (dispatch) => {enter code here
axios
.post("xyz.com/testApp/api/login.php", params)
.then((response) => {
const res = response.data.response;
const resMsg = res.message;
let preDefinedErrMsg;
if (resMsg !== "success") {
preDefinedErrMsg = "Wrong Credentials";
throw new Error(preDefinedErrMsg);
}
dispatch({
type: LOGIN,
token: "resData.idToken",
userId: "resData.id",
errorMessage: "message",
});
console.log(response);
})
.catch((err) => {
//console.log(err);
});
};
};
Yes I got it solved, by handling error in my action file.
const axios = require("axios");
export const LOGIN = "LOGIN";
export const login = (username, password) => {
const params = new URLSearchParams();
params.append("username", username);
params.append("password", password);
return async (dispatch) => {
await axios
.post("xyz.com/api/login.php", params)
.then((response) => {
const res = response.data.response;
const resMsg = res.message;
let preDefinedMsg;
if (resMsg === "Error") {
preDefinedErrMsg = "Wrong Credentials";
throw new Error(preDefinedErrMsg);
} else if (resMsg === "success") {
preDefinedMsg = "success";
dispatch({
type: LOGIN,
token: "resData.idToken",
userId: "resData.id",
errorMessage: "message",
});
}
})
.catch((error) => {
if (error.message === preDefinedErrMsg) {
throw new Error(preDefinedErrMsg);
}
});
};
};

NextJs/ Apollo Client/ NextAuth issue setting authorization Bearer Token to headers correctly

I cannot correctly set my jwt token from my cookie to my Headers for an authenticaed gql request using apollo client.
I believe the problem is on my withApollo.js file, the one that wraps the App component on _app.js. The format of this file is based off of the wes bos advanced react nextjs graphql course. What happens is that nextauth saves the JWT as a cookie, and I can then grab the JWT from that cookie using a custom regex function. Then I try to set this token value to the authorization bearer header. The problem is that on the first load of a page with a gql query needing a jwt token, I get the error "Cannot read property 'cookie' of undefined". But, if I hit browser refresh, then suddenly it works and the token was successfully set to the header.
Some research led me to adding a setcontext link and so that's where I try to perform this operation. I tried to async await setting the token value but that doesn't seem to have helped. It just seems like the headers don't want to get set until on the refresh.
lib/withData.js
import { ApolloClient, ApolloLink, InMemoryCache } from '#apollo/client';
import { onError } from '#apollo/link-error';
import { getDataFromTree } from '#apollo/react-ssr';
import { createUploadLink } from 'apollo-upload-client';
import withApollo from 'next-with-apollo';
import { setContext } from 'apollo-link-context';
import { endpoint, prodEndpoint } from '../config';
import paginationField from './paginationField';
const getCookieValue = (name, cookie) =>
cookie.match(`(^|;)\\s*${name}\\s*=\\s*([^;]+)`)?.pop() || '';
let token;
function createClient(props) {
const { initialState, headers, ctx } = props;
console.log({ headers });
// console.log({ ctx });
return new ApolloClient({
link: ApolloLink.from([
onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors)
graphQLErrors.forEach(({ message, locations, path }) =>
console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
)
);
if (networkError)
console.log(
`[Network error]: ${networkError}. Backend is unreachable. Is it running?`
);
}),
setContext(async (request, previousContext) => {
token = await getCookieValue('token', headers.cookie);
return {
headers: {
authorization: token ? `Bearer ${token}` : '',
},
};
}),
createUploadLink({
uri: process.env.NODE_ENV === 'development' ? endpoint : prodEndpoint,
fetchOptions: {
credentials: 'include',
},
headers,
}),
]),
cache: new InMemoryCache({
typePolicies: {
Query: {
fields: {
// TODO: We will add this together!
// allProducts: paginationField(),
},
},
},
}).restore(initialState || {}),
});
}
export default withApollo(createClient, { getDataFromTree });
page/_app.js
import { ApolloProvider } from '#apollo/client';
import NProgress from 'nprogress';
import Router from 'next/router';
import { Provider, getSession } from 'next-auth/client';
import { CookiesProvider } from 'react-cookie';
import nookies, { parseCookies } from 'nookies';
import Page from '../components/Page';
import '../components/styles/nprogress.css';
import withData from '../lib/withData';
Router.events.on('routeChangeStart', () => NProgress.start());
Router.events.on('routeChangeComplete', () => NProgress.done());
Router.events.on('routeChangeError', () => NProgress.done());
function MyApp({ Component, pageProps, apollo, user }) {
return (
<Provider session={pageProps.session}>
<ApolloProvider client={apollo}>
<Page>
<Component {...pageProps} {...user} />
</Page>
</ApolloProvider>
</Provider>
);
}
MyApp.getInitialProps = async function ({ Component, ctx }) {
let pageProps = {};
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx);
}
pageProps.query = ctx.query;
const user = {};
const { req } = ctx;
const session = await getSession({ req });
if (session) {
user.email = session.user.email;
user.id = session.user.id;
user.isUser = !!session;
// Set
nookies.set(ctx, 'token', session.accessToken, {
maxAge: 30 * 24 * 60 * 60,
path: '/',
});
}
return {
pageProps,
user: user || null,
};
};
export default withData(MyApp);
api/auth/[...nextAuth.js]
import NextAuth from 'next-auth';
import Providers from 'next-auth/providers';
import axios from 'axios';
const providers = [
Providers.Google({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
}),
Providers.Credentials({
name: 'Credentials',
credentials: {
username: { label: 'Username', type: 'text', placeholder: 'jsmith' },
password: { label: 'Password', type: 'password' },
},
authorize: async (credentials) => {
const user = await axios
.post('http://localhost:1337/auth/local', {
identifier: credentials.username,
password: credentials.password,
})
.then((res) => {
res.data.user.token = res.data.jwt;
return res.data.user;
}) // define user as res.data.user (will be referenced in callbacks)
.catch((error) => {
console.log('An error occurred:', error);
});
if (user) {
return user;
}
return null;
},
}),
];
const callbacks = {
// Getting the JWT token from API response
async jwt(token, user, account, profile, isNewUser) {
// WRITE TO TOKEN (from above sources)
if (user) {
const provider = account.provider || user.provider || null;
let response;
let data;
switch (provider) {
case 'google':
response = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}/auth/google/callback?access_token=${account?.accessToken}`
);
data = await response.json();
if (data) {
token.accessToken = data.jwt;
token.id = data.user._id;
} else {
console.log('ERROR No data');
}
break;
case 'local':
response = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}/auth/local/callback?access_token=${account?.accessToken}`
);
data = await response.json();
token.accessToken = user.token;
token.id = user.id;
break;
default:
console.log(`ERROR: Provider value is ${provider}`);
break;
}
}
return token;
},
async session(session, token) {
// WRITE TO SESSION (from token)
// console.log(token);
session.accessToken = token.accessToken;
session.user.id = token.id;
return session;
},
redirect: async (url, baseUrl) => baseUrl,
};
const sessionPreferences = {
session: {
jwt: true,
},
};
const options = {
providers,
callbacks,
sessionPreferences,
};
export default (req, res) => NextAuth(req, res, options);

React Native AsyncStorage.getItem is not working. ({"_40": 0, "_55": null, "_65": 0, "_72": null})

Good day! I have this function of AsyncStorage that gets an item of a token. I used with ApolloClient to process the token but when I test it first, it seems to have an error with what will I get by AsyncStorage function.
export function jwtLogin(data) {
return async dispatch => {
const userData = {
email: data.email,
password: data.password,
};
console.log(userData);
const client = new ApolloClient({
link: new HttpLink({
uri: API_URL,
}),
cache: new InMemoryCache(),
});
client
.mutate({
mutation: loginUser,
variables: {
email: userData.email,
password: userData.password,
},
})
.then(resp => {
console.log(resp.data.tokenCreate);
console.log('Token', resp.data.tokenCreate.token);
if (resp.data.tokenCreate.token !== null) {
saveJWTTokenData(resp.data.tokenCreate.token); //from AsyncStorage save function
async function main() { //function of AsyncStorage
await AsyncStorage.getItem('jwt_token').then(item => {
return item;
});
}
console.log(main()); // returns error
Actions.push('main_loading');
} else {
const errors = resp.data.tokenCreate.errors;
{
errors.map(err => {
Alert.alert('Error.', err.message);
});
}
}
})
.catch(err => {
Alert.alert('Error.', err.message);
});
};
}
For the save storage function:
export const saveJWTTokenData = async jwt_token => AsyncStorage.setItem('jwt_token', jwt_token);
My Error Log Picture
I think your Promise is not handled correctly..
Try to add a catch after your then call like this:
.catch(err => console.log(err))
Or try to use your function like this maybe:
await getData("jwt_token")
.then(data => data)
.then(value => this.setState({ token: value })) // here it is setState but I guess you can also return
.catch(err => console.log("AsyncStorageErr: " + err));

Redux async actioncreator not recognizing then

I need to use .then() on a redux action, what is wrong in the following action?
export const userLogin = (username, password) => {
return dispatch => {
axios.post(`${TT_API_BASE}/Access/Login`, { username: username, password: password, applicationId: 2 }, {
headers: {
'Content-Type': 'application/json;charset=utf-8',
'Authorization': 'Basic ' + auth,
}
})
.then(response => {
dispatch({
type: LOGIN,
payload: response.data
})
})
.catch(err => {
console.log(err)
dispatch({
type: LOGIN_FAILED
})
})
}
}
It is then called in a component like this
handlePress() {
this.props.userLogin(this.state.username, this.state.password)
.then(() => {
this.props.navigation.navigate('SelectInstance')
})
}
Which displays the errormessage that then is not defined. What am I doing wrong?
When you do dispatch(someThunkActionCreator()), the return value of dispatch is whatever your thunk function returns. So, you can only do dispatch().then() if the thunk function returns a promise.
Your thunk is making an AJAX call, but not actually returning a promise, so it actually returns undefined. Putting a return statement in front of axios.post() will return that promise and fix the problem.
Solved by doing this:
export const userLogin = (username, password) => {
return async dispatch => {
const onSuccess = data => {
dispatch({
type: LOGIN,
payload: data
})
}
const onError = err => {
dispatch({
type: LOGIN_FAILED
})
}
try {
const req = await axios.post(`${TT_API_BASE}/Access/Login`, { username: username, password: password, applicationId: 2 }, {
headers: {
'Content-Type': 'application/json;charset=utf-8',
'Authorization': 'Basic ' + auth,
}
})
return onSuccess(req.data)
}
catch (err) {
return onError(err)
}
}
}