React native axios call throws 403 but postman correctly outputs the data - react-native

I'm working on a RN app, which has redux in it. Now I can login with the help of jwt but when Im trying the to get the data from my other component its giving me 403 error. Please find below the relevant code.
Here is my reducer:
const initState = {
isLoadingCollegeDashList : false,
collegeDashList:{},
collegeDashListFail:false
}
const collegeReducer = ( state = initState, action) => {
switch(action.type){
case 'IS_LOADING_COLLEGE_DASH_LIST' :
return{
...state,
isLoadingCollegeDashList: true,
collegeDashList : false
}
case 'COLLEGE_DASH_LIST' :
return {
...state,
isLoadingCollegeDashList : false,
collegeDashList : true,
userData : action.userData
}
case 'COLLEGE_DASH_LIST_FAIL' :
return{
...state,
isLoadingCollegeDashList:false,
collegeDashList: false,
collegeDashListFail: action.error
}
default :
return state
}
}
and here's my action that's making get request
export const populateCollege = (token) => {
const headers = {
'api-secret' : ...secret...,
'authorization':...authToken...,
'Content-Type': 'application/json',
}
return dispatch => {
dispatch(isLoadingCollegeDashList(true));
return axios.get( '...api/api/...', {
},{
headers:headers,
})
.then((response) => {
if(response.status < 300){
dispatch(isLoadingCollegeDashList(false))
dispatch(collegeDashList(response))
console.log(response);
}
else{
response.json().then((responseJSON) => {
console.log("responseJSON",responseJSON);
dispatch(isLoadingCollegeDashList(false))
dispatch(collegeDashListFail(responseJSON.message))
})
}
})
.catch((error) => {
console.log("error",error);
dispatch(isLoadingCollegeDashList(false))
dispatch(collegeDashListFail(error))
})
}
}
export const isLoadingCollegeDashList = (bool) => {
return{
type:'IS_LOADING_COLLEGE_DASH_LIST',
isLoadingCollegeDashList:bool
}
}
export const collegeDashList = (userData) => {
return{
type:'COLLEGE_DASH_LIST',
userData
}
}
export const collegeDashListFail = (error) => {
return{
type:'COLLEGE_DASH_LIST_FAIL',
error
}
}
here's action that im calling if you want to check it
const mapDispatchToProps = dispatch => ({
populateCollege : (token) => dispatch(actions.populateCollege({token}))
});
PS I've for now stored token in the state of one hence passing the token from this dispatch itself.
Let me know if you need any clarification / more information then do let me know. Thanks in advance

Make sure you have the authorisation schema before your token. The schema can be like Basic, Bearer or any other value based on your authorisation details. (eg. Authorization: Bearer TOKEN).
Also, try to reuse your auth headers while creating the axios instance so you won't need to inject them on every call.

Related

Urql config with guest token for SSG on next js

So I have a project using the latest Next js 13, React 18, Urql 3, and using typescript
Currently, I have issues when trying to query the urql from the getstaticprops function. My urql request needs a guest token, and I'm storing the token on session storage(other suggestions ?).
It has no issue when the query is running on the client, but I have it when querying inside the function.
My concern is related to the token reading, so the server cannot read the session storage value.
I'm asking what is the better and simplest way to make this work.
Does use cookies to store guest tokens will make this work?
Or the configuration that doesn't work?
This is my current config for urql.ts
import {
createClient,
ssrExchange,
dedupExchange,
cacheExchange,
fetchExchange,
} from "urql";
import { GRAPH_URL } from "#lib/constant/env";
import type { TypedDocumentNode } from "#urql/core";
const isServerSide = typeof window === "undefined";
const ssrCache = ssrExchange({
isClient: !isServerSide,
});
const client = createClient({
url: GRAPH_URL,
exchanges: [dedupExchange, cacheExchange, ssrCache, fetchExchange],
fetchOptions: () => {
const token = sessionStorage.getItem("accessToken");
return {
headers: {
authorization: token ? `Bearer ${token}` : "",
},
};
},
});
const query = async (
query: TypedDocumentNode<any, object>,
variables?: Record<string, string | string[] | unknown>
) => {
try {
const response = await client.query(query, variables as any).toPromise();
return response;
} catch (error) {
if (error instanceof Error) console.error(error.message);
}
};
const mutation = async (
mutation: TypedDocumentNode<any, object>,
variables?: Record<string, string | string[] | unknown>
) => {
try {
const response = await client
.mutation(mutation, variables as any)
.toPromise();
return response;
} catch (error) {
if (error instanceof Error) console.error(error.message);
}
};
export { client, query, mutation, ssrCache };
And this some of the code for the blog index page
export const getStaticProps = async () => {
await fetchArticlesSummary();
return {
props: {
urqlState: ssrCache.extractData(),
},
revalidate: 600,
};
};
export default withUrqlClient(() => ({
url: GRAPH_URL,
}))(BlogPage);
This is for the fetchArticlesSummary
export const fetchArticlesSummary = async () => {
try {
const {
data: { listArticles },
}: any = await query(getListArticle);
return listArticles.items;
} catch (error) {
return {
notFound: true,
};
}
};
I also doing a setup on _app.tsx
export default function App({ Component, pageProps }: AppProps) {
if (pageProps.urqlState) {
ssrCache.restoreData(pageProps.urqlState);
}
return (
<Provider value={client}>
<Component {...pageProps} />
</Provider>
);
}
Thank you
I have followed urql documentation about server-side configuration and many others but still don't have any solutions.

React redux - passing parameters to url - error - Actions must be plain objects

I want to attach params to react redux fetch action and I searched for many days the redux docs, but even after trying out a few things i am getting this error:
[Unhandled promise rejection: Error: Actions must be plain objects. Use custom middleware for async actions.]
https://codesandbox.io/s/fast-framework-ct2fc?fontsize=14&hidenavigation=1&theme=dark
The original action looks like this:
export function fetchArticleDetails() {
return apiAction({
url: "http://myurl/appApi/2.0.0/getData/1", //1 should be an optional value
onSuccess: setArticleDetails,
onFailure: () => console.log("Error occured loading articles"),
label: FETCH_ARTICLE_DETAILS
});
}
function setArticleDetails(data) {
console.log(data);
return dispatch({
type: SET_ARTICLE_DETAILS,
payload: data
});
}
i tried to set the param directly
export function fetchArticleDetails(id)
...
url: `http://myurl/appApi/2.0.0/getData/${id}`,
or some variations to put the params in the payload directly
function setArticleDetails(data) {
console.log(data);
return dispatch({
type: SET_ARTICLE_DETAILS,
payload: data,
userid: id
});
}
All this results in the same error. Anyone have an idea where to place the dynamic data to solve it?
Another idea could be to set the params in my reducer maybe?
Update store/index.js
import { createStore, applyMiddleware } from "redux";
import rootReducer from "../reducers";
import apiMiddleware from "../middleware/api";
const store = createStore(rootReducer, applyMiddleware(apiMiddleware));
window.store = store;
export default store;
update: middleware/api.js
import axios from "axios";
import { API } from "../actions/types";
import { accessDenied, apiError, apiStart, apiEnd } from "../actions/api";
const apiMiddleware = ({ dispatch }) => next => action => {
next(action);
if (action.type !== API) return;
const {
url,
method,
data,
accessToken,
onSuccess,
onFailure,
label,
headers
} = action.payload;
const dataOrParams = ["GET", "DELETE"].includes(method) ? "params" : "data";
// axios default configs
axios.defaults.baseURL = process.env.REACT_APP_BASE_URL || "";
axios.defaults.headers.common["Content-Type"] = "application/json";
axios.defaults.headers.common["Authorization"] = `Bearer ${accessToken}`;
if (label) {
dispatch(apiStart(label));
}
axios
.request({
url,
method,
headers,
[dataOrParams]: data
})
.then(({ data }) => {
dispatch(onSuccess(data));
})
.catch(error => {
dispatch(apiError(error));
dispatch(onFailure(error));
if (error.response && error.response.status === 403) {
dispatch(accessDenied(window.location.pathname));
}
})
.finally(() => {
if (label) {
dispatch(apiEnd(label));
}
});
};
export default apiMiddleware;
function apiAction()
function apiAction({
url = "",
method = "GET",
data = null,
accessToken = null,
onSuccess = () => {},
onFailure = () => {},
label = "",
headersOverride = null
}) {
return {
type: API,
payload: {
url,
method,
data,
accessToken,
onSuccess,
onFailure,
label,
headersOverride
}
};
}
There are a couple of issues with the code. apiMiddleware should only pass the action to the next middleware in the chain if it's not of type API.
const apiMiddleware = ({ dispatch }) => (next) => (action) => {
if (action.type !== API) {
return next(action)
}
// do stuff
}
Since the apiMiddleware dispatches what onFailure returns, the function has to return an object. In fetchArticleDetails, you're passing () => console.log("Error occured loading articles") causing apiMiddleware to dispatch undefined.
export function fetchArticleDetails(id) {
return apiAction({
url: `https://jsonplaceholder.typicode.com/todos/${id}`,
onSuccess: setArticleDetails,
onFailure: (error) => ({
type: FETCH_ARTICLE_ERROR,
payload: error
}),
label: FETCH_ARTICLE_DETAILS
})
}
CodeSandbox
I would strongly recommend using React Query to simplify data fetching, managing, and syncing server state.

How to set authorization header coorectly?

Problem:
In my react native app in order to remove repeated calls I have developed a general POST GET methods in httpClient file. It code is look likes this.
import axios from 'axios';
import AsyncStorage from '#react-native-community/async-storage';
axios.defaults.headers.post['Content-Type'] = 'application/json';
var instance = null;
const setAuthorisationHeder = async () => {
const token = JSON.parse(await AsyncStorage.getItem('auth_data'));
if (token) {
console.log('>>>>>> instance', instance);
Object.assign(instance.headers, {
Authorization: 'Bearer' + token.accessToken,
});
} else {
console.log('>>>>>> instance', instance);
Object.assign(instance.headers, {
Authorization: '',
});
}
};
export const setHeader = () => {
console.log('>>>>>>>> HIIII');
instance = axios.create({
baseURL: '',
timeout: 150000,
headers: {
'Content-Type': 'application/json',
},
});
instance.interceptors.response.use(
function (response) {
return response;
},
async function (error) {
if (error.response.status) {
if (error.response.status === 401) {
AsyncStorage.removeItem('auth_data');
} else {
throw error;
}
} else {
console.log(error);
}
},
);
};
export const Get = (route, data) => {
function getData() {
return instance.get(
route,
data == null ? {data: {}} : {data: JSON.stringify(data)},
);
}
if (instance) {
console.log('>>>>>> HIIIIii');
// setAuthorisationHeder();
return getData();
}
return setHeader().then(getData);
};
export const Post = (route, data) => {
console.log('>>>>>> route', route);
function postData() {
return instance.post(route, JSON.stringify(data));
}
if (instance) {
console.log('>>>>>> HIIIIii');
// setAuthorisationHeder();
// setAuthorisationHeder();
return postData();
}
return setHeader().then(postData);
};
Can some tell me a way to add an authorization header to this instance? My token is storing the Asyncstorage in the middle of some actions so at the beginning called I don't have the token. As my code setHeader is running only one time so I created a method call setAuthorisationHeder() function. But it is giving me can not find property .then error when I am putting a request. Can someone help me to solve this issue? Thank you?
you can define global headers once and use it in every network call.
https://github.com/axios/axios#global-axios-defaults
Create a global auth variable where you'll store the auth data from storage. Before making a request get the auth data and use interceptor to set the bearer token.
let authToken = '';
const getAuthToken = async () => {
// asumming auth token was saved as string
authToken = await AsyncStorage.getItem('auth_data');
};
Interceptor
// request interceptor
axiosInstance.interceptors.request.use(
function (config) {
// Do something before request is sent
config.headers.Authorization = `Bearer ${authToken}`;
return config;
},
function (error) {
// Do something with request error
return Promise.reject(error);
}
);
complete code
import axios from 'axios';
import AsyncStorage from '#react-native-community/async-storage';
let authToken = '';
const axiosInstance = axios.create({
baseURL: '',
timeout: 150000,
headers: {
'Content-Type': 'application/json',
},
});
// request interceptor
axiosInstance.interceptors.request.use(
function (config) {
// Do something before request is sent
config.headers.Authorization = `Bearer ${authToken}`;
return config;
},
function (error) {
// Do something with request error
return Promise.reject(error);
}
);
const getAuthToken = async () => {
// asumming auth token was saved as string
authToken = await AsyncStorage.getItem('auth_data');
};
export const Get = async (route, data = {}) => {
// get and set auth token
await getAuthToken();
// route = /user?id=787878 or /user/787878
return await axiosInstance.get(route);
};
export const Post = async (route, data = {}) => {
await getAuthToken();
return await axiosInstance.post(route, data);
};

Redirect to a Screen after async action on React-Native Redux Application using redux-thunk

I'm integrating redux(redux version - 4.0.x, react-redux version - 7.1.x) to an existing react-native(version - 0.60.5) application with react-navigation(version - 4.0.x) and revamping the existing backend Http requests from Axios to fetch API in redux action creators using redux-thunk middleware(version - 2.3.x).
I was unable to figure out how to setup react-navigation to redirect to another screen when in the success function of the fetch call.
Here I'm not going to handover the navigation to redux using react-navigation/redux-helpers but need to keep the react-navigation to manage the navigation of the application
Example Fetch Call in Redux Action Creator(Login):
const fetchLogin = (username, password) => {
const url = hostUrl + '/oauth/token';
return(dispatch) => {
dispatch(getLoginAttempt(true))
return(fetch(url, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
},
body:"client_secret&username="+username+"&password="+password
}))
.then((response) => {
console.log("Response", response);
if(response.status >= 200 && response.status < 300) {
dispatch(getLoginAttempt(false))
response.json()
.then((responseJson) => {
console.log("responseJSON", responseJson);
dispatch(getLoginAttemptSuccess(responseJson))
try{
setAccessToken(responseJson.access_token);
setAccessToken5(responseJson.access_token);
} catch (e) {
console.log("Error Saving the Access Token");
}
})
} else {
response.json()
.then((responseJson) => {
console.log("responseJSON", responseJson);
dispatch(getLoginAttempt(false))
dispatch(getLoginAttemptFailure(responseJson.message))
})
}
})
.catch((error) => {
console.log("error", error);
dispatch(getLoginAttempt(false))
dispatch(getLoginAttemptFailure(error))
})
}}
const getLoginAttempt = (isLoading) => {
return {
type: FETCH_LOGIN,
isLoading: isLoading
};
}
const getLoginAttemptSuccess = (responseJson) => {
return {
type: FETCH_LOGIN_SUCCESSFUL,
responseJson: responseJson
};
}
const getLoginAttemptFailure = (error) => {
return {
type: FETCH_LOGIN_FAILURE,
error: error
};
}
Example Reducer(Login Reducer)
const initialState = {
loginResponseData: {},
isLoggedIn: false,
isLoginLoading: false,
loginError: null
}
const loginReducer = (state = initialState, action) => {
switch (action.type) {
case FETCH_LOGIN:
return Object.assign({}, state,
{
isLoginLoading: true,
isLoggedIn: false
})
case FETCH_LOGIN_SUCCESSFUL:
return Object.assign({}, state,
{
isLoginLoading: false,
isLoggedIn: true,
loginResponseData: action.responseJson
})
case FETCH_LOGIN_FAILURE:
return Object.assign({}, state,
{
isLoginLoading: false,
isLoggedIn: false,
loginError: action.error
})
default:
return state
}
}
then connected the reducer with a root reducer in redux store configuration and connected to a screen(Login Screen) as below
export default connect(mapStateToProps, mapDispatchToProps)(LoginScreen);
I have created a library for handling navigation problems like this one.
React Navigation Helpers
Its setup and usage are very basic. You simply need to set your navigation reference into the top global level (which the library handles it itself). You simply put this code segment into your navigation logic and set global level navigator with your main navigator.
import NavigationService from "react-navigation-helpers";
<MainNavigator
ref={navigatorRef =>
NavigationService.setGlobalLevelNavigator(navigatorRef)
}
/>
After you're done with the setup. You can navigate to anywhere to anywhere with this library.
USAGE
// Goes to spesific screen from prop
NavigationService.navigate("home")
// Goes to preview screen
NavigationService.back()
I believe that this solution will solve your confusion and fix your problem for the whole project.
Simply you need to put this line of code into your successful fetch result:
NavigationService.navigate("home")
If you have trouble using react-navigation-helpers, ask me anything 😇

NuxtJs - Cannot read property 'headers' of undefined

I'm a newbie in NuxtJs. I'm trying to implement an external API Call with axios which I get token and store it on cookie. Everything works well in development. But when I try to run npm run generate it gives me errors that I don't know what to do.
When I delete nuxtSeverInit, npm run generate runs smoothly. And after some research, i think that nuxtServerInit that I'm using shouldn't be used. Can anyone please tell me how to make it work.
This is the first project in a new company, so I'm trying to prove myself. Please help me with it. Will you.
Click here for image that shows the error that appears after npm run generate
This is store/index.js file
import Vuex from 'vuex'
var cookieparser = require('cookieparser')
const createStore = () => {
return new Vuex.Store({
state: {
auth: null,
},
mutations: {
update (state, data) {
state.auth = data
}
},
actions: {
nuxtServerInit ({ commit }, { req }) {
let accessToken = null
if (req.headers.cookie) {
var parsed = cookieparser.parse(req.headers.cookie)
if(parsed){
accessToken = parsed.auth
}
}
commit('update', accessToken)
},
}
})
}
export default createStore
middleware/authenticated.js file
export default function ({ store, redirect }) {
// If the user is not authenticated
if (!store.state.auth) {
return redirect('/login')
}
}
middleware/notAuthenticated.js file
export default function ({ store, redirect }) {
// If the user is authenticated redirect to home page
if (store.state.auth) {
return redirect('/app/dashboard')
}
}
login.vue file
validateBeforeSubmit() {
this.$validator.validateAll().then((result) => {
if (result) {
this.button_title = 'One moment ...';
let submitted_user_data = {
'username': this.emailAddress,
'client_id': this.user_uuid,
'password': this.password,
}
MerchantServices.do_user_login(submitted_user_data)
.then(response => {
let access_token = response.data.access_token;
this.postLogin(access_token);
})
.catch(error => {
this.$refs.invalid_credentials.open();
this.button_title = 'Sign in'
});
return;
}
});
},
postLogin: function(access_token_val) {
if(access_token_val != ''){
setTimeout(() => {
const auth = {
accessToken: access_token_val
}
this.$store.commit('update', auth)
Cookie.set('auth', auth)
this.$refs.invalid_credentials.open();
this.button_title = 'Sign in'
this.$router.push('/app/dashboard')
}, 1000)
}else{
alert('hello')
}
},
and the last user login api call which also returns the token.
do_user_login(user){
var user_details = 'username='+user.username+'&client_id='+ user.client_id +'&grant_type=password&password='+user.password+''
return axios.post('myapiurl', user_details )
.then(response => {
return response;
});
},
Acording to Nuxt Docs req is not available on nuxt generate.
You should use nuxt build and than nuxt start after that.