Login page (with firebase auth) is not rendering if dataProvider (Woocommerce) is failing - react-admin

The app is not rendering the login page in case the dataprovider is failing.
I have made a customized login page, using firebase as authProvider.
I'm trying to make it work so that I log in and then use the user.id provided to use a correct API -key to fetch & update data from WooCommerce site.
Everything works fine if I have predefined the user id in the dataprovider, like this
const user = async () => {await authProvider.checkAuth()};
const vendor = user.lengt > 0 ? user.uid: 'reader';
let WooCommerce = new WooCommerceAPI(wcConfig[host][vendor]);
If I plainly use the user.uid it is undefined before I log in and the login age will not be shown at all. Now what I do wonder is that why would we try to use the dataprovider before auth and how to get pass that?
After log in the vendor (used for woocommerce api) is not updated either.
WooCommerce dataProvider is self made, it does return a 401, but I'm wondering if there's something missing in the error handling.
Here's the actual code:
export default {
getList: (resource, params) => {
const search = params.filter.q;
const { page, per_page } = params.pagination;
const query = {
page: JSON.stringify(page),
per_page: JSON.stringify(per_page),
search: JSON.stringify(search)
};
let url = `${resource}/?${stringify(query)}`
if (resource === 'system_status'){
url = resource;
}
return WooCommerce.get(url, params.data)
.then((response) => {
return {
data: response.data,
total: parseInt(response.headers['x-wp-total']),
pages: parseInt(response.headers['x-wp-totalpages'])
};
})
.catch((error) => {
// Invalid request, for 4xx and 5xx statuses
console.log("Response Status:", error.response.status);
console.log("Response Headers:", error.response.headers);
console.log("Response Data:", error.response.data);
return { data: error.response.data };
})
.finally(() => {
// Always executed.
});
},
getOne: (resource, params) => {
return WooCommerce.get(resource + '/' + params.id)
.then((response) => {
// Successful request
return {
data: response.data
};
})
.catch((error) => {
// Invalid request, for 4xx and 5xx statuses
console.log("Response Status:", error.response.status);
console.log("Response Headers:", error.response.headers);
console.log("Response Data:", error.response.data);
return { data: error.response.data };
})
.finally(() => {
// Always executed.
});
},
update: (resource, params) => {
return WooCommerce.put(resource + '/' + params.id, params.data)
.then((response) => {
// Successful request
return {
data: response.data
};
})
.catch((error) => {
// Invalid request, for 4xx and 5xx statuses
console.log("Response Status:", error.response.status);
console.log("Response Headers:", error.response.headers);
console.log("Response Data:", error.response.data);
return { data: error.response.data };
})
.finally(() => {
// Always executed.
});
},
create: (resource, params) => {
return WooCommerce.post(resource, params.data)
.then((response) => {
// Successful request
return {
data: response.data
};
})
.catch((error) => {
// Invalid request, for 4xx and 5xx statuses
console.log("Response Status:", error.response.status);
console.log("Response Headers:", error.response.headers);
console.log("Response Data:", error.response.data);
return { data: error.response.data };
})
.finally(() => {
// Always executed.
});
}
}

Apparently one has to handle the configuration error.
let WooCommerce = () => {
try {
return new WooCommerceAPI(config[host][vendor]);
} catch (error) {
console.error(error)
}
}
Could've been better, but at least it works now.

Related

Cant catch axios error in promise, response works fine

I am trying to catch an error whilst the user tries to access a page without an authentication token.
axios.js?v=012beb2f:840 POST http://localhost:3001/api/get-user 422 (Unprocessable Entity)
Uncaught (in promise) AxiosError {message: 'Request failed with status code 422', name: 'AxiosError', code: 'ERR_BAD_REQUEST', config: {…}, request: XMLHttpRequest, …}
router.beforeEach((to, from, next) => {
const store = useUserStore()
if(to.meta.requiresAuth)
{
try
{
const response = axios.post('/api/get-user', {}, {
headers: {
Authorization: `Bearer ${store.user.token}`
}
})
.then(response => {
console.log(response)
next()
})
}
catch(error)
{
console.log(error)
next('/login')
}
}
else
{
next()
}
})
Thats the code that makes the request to the server. If the token is correct it works fine. However incorrect token throws the error mentioned above. I would like it to redirect to /login page if token is incorrect.
This is the code on server side.
router.post('/get-user', signupValidation, (req, res, next) => {
if(
!req.headers.authorization ||
!req.headers.authorization.startsWith('Bearer') ||
!req.headers.authorization.split(' ')[1]
){
return res.status(422).json({
message: "Please provide the token",
});
}
const theToken = req.headers.authorization.split(' ')[1];
const decoded = jwt.verify(theToken, 'the-super-strong-secrect');
db.query('SELECT * FROM users where id=?', decoded.id, function (error, results, fields) {
if (error) throw error;
return res.send({ error: false, data: results[0], message: 'Fetch Successfully.' });
});
});
Change the synchronous try/catch...
try
{
somePromise.then(...)
}
catch(error)
{
console.log(error)
next('/login')
}
...to instead use the catch() provided by the promise:
const headers = { Authorization: `Bearer ${store.user.token}` };
axios.post('/api/get-user', {}, { headers })
.then(response => {
console.log(response)
next()
})
.catch(error => {
console.log(error)
next('/login')
}}
Note, also, that the OP code incorrectly assigned the axios.post promise to an unused variable called "response".
Alternatively, use the synchronous try/catch style with async/await:
router.beforeEach(async (to, from, next) => {
const store = useUserStore()
if(to.meta.requiresAuth)
{
try
{
const headers = { Authorization: `Bearer ${store.user.token}` };
const response = await axios.post('/api/get-user', {}, { headers });
console.log(response);
next();
}
catch(error)
{
console.log(error)
next('/login')
}
}
else
{
next()
}
})

react native make accessToken global

im using rn-secure-storage to save authState when using oauth2, then i have class AppHelper to control all network function like below:
import RNSecureStorage, { ACCESSIBLE } from 'rn-secure-storage'
export const accessToken = async() => {
await RNSecureStorage.get("authState").then((value) => {
console.log("authState", value);
return JSON.parse(value).accessToken
}).catch((err) => {
console.log("can not get authState", err);
});
};
export const getData = (url, tag = 'getData') => {
return fetch(url, {
method : 'GET',
headers: {
'Content-Type': 'application/json;charset=utf-8',
'Authorization' : 'Bearer ' + accessToken
}
})
.then((response) => {
console.log(tag, 'Bearer ' + accessToken);
return response.text();
})
.then((json) => {
console.log(tag, json)
return json;
})
.catch((error) => {
handleError(err)
console.log(tag, error, url)
});
}
export const handleError = (error) => {
if (error.response.code == 401){
alert('Unauthorized')
}else{
console.log('network call failed', error)
}
}
im coding native before, so im not familiar with React native syntax. I just want to get access token to apply in network call, but my code show error:
_getMasterInfoApi Bearer function _callee() {
return _regenerator.default.async(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.next = 2;
return _regenerator.default.awrap(_rnSecureStorage.default.get("authState").then(function (value) {
console.log("authState", value);
return JSON.parse(value).accessToken;
}).catch(function (err) {
handleError(err);
}));
case 2:
case "end":
return _context.stop();
}
}
}, null, null, null, Promise);
}
Can anyone help? thanks in advance

React-native-fetch-blob GET request error

I am replacing axios to rn-fetch-blob in my react-native project. In the request I ping my server with credentials and I expect a response.
The old request with axios is as follows and works perfectly:
export const postWorkspace =
(newWorkspace: Workspace): AppThunk =>
async (dispatch) => {
console.log('addWorkspace Start');
dispatch(setIsLoading(true));
let configOption = {
headers: {
'Access-Control-Allow-Origin': '*',
'X-AUTH-USER': newWorkspace.credentials.email,
'X-AUTH-TOKEN': newWorkspace.credentials.password,
},
};
await axios
.get(`${newWorkspace.url}/api/ping`, configOption)
.then(async (resp) => {
console.log('addWorkspace resp', resp);
try {
await storeWorkspaceToStorage(newWorkspace);
} catch (e) {
console.error(e);
}
})
.catch((err) => {
console.log('addWorkspace err', JSON.stringify(err));
return Promise.reject(err);
})
.finally(() => dispatch(setIsLoading(false)));
};
This is how I transformed the code with rn-fetch-blob:
export const postWorkspace=
(newWorkspace: Workspace): AppThunk =>
async (dispatch) => {
console.log('addWorkspace Start');
dispatch(setIsLoading(true));
let configOption = {
'Access-Control-Allow-Origin': '*',
'X-AUTH-USER': newWorkspace.credentials.email,
'X-AUTH-TOKEN': newWorkspace.credentials.password,
};
await RNFetchBlob
.fetch('GET', '${newWorkspace.url}/api/ping', configOption)
.then( async(resp) => {
console.log('addWorkspace resp', resp);
try {
await storeWorkspaceToStorage(newWorkspace);
} catch (e) {
console.error(e);
}
})
.catch((err) => {
//console.log(err.info().status);
console.log('addWorkspace err', JSON.stringify(err));
return Promise.reject(err);
})
.finally(() => dispatch(setIsLoading(false)));
};
The new request with rn-fetch-blob returns this error:
response error "line":126349,"column":34,"sourceURL":"http://localhost:8081/index.bundle?platform=android&dev=true&minify=false"
When I opend the file "http://localhost:8081/index.bundle?platform=android&dev=true&minify=false" around line 1262349 the code looks like this, I can't understand what went wrong:
var req = RNFetchBlob[nativeMethodName];
req(options, taskId, method, url, headers || {}, body, function (err, rawType, data) {
subscription.remove();
subscriptionUpload.remove();
stateEvent.remove();
partEvent.remove();
delete promise['progress'];
delete promise['uploadProgress'];
delete promise['stateChange'];
delete promise['part'];
delete promise['cancel'];
promise.cancel = function () {};
//line 126349
if (err) reject(new Error(err, respInfo));else {
if (options.path || options.fileCache || options.addAndroidDownloads || options.key || options.auto && respInfo.respType === 'blob') {
if (options.session) session(options.session).add(data);
}
respInfo.rnfbEncode = rawType;
resolve(new FetchBlobResponse(taskId, respInfo, data));
}
});
});
I am doing this since rn-fetch-blob is basically one of the few libraries that allows react-native to ping a server with no SSL certification.
Thank you

Return to request after refreshing tokens

I am trying to get refresh tokens working.
It works for the most part, but the problem is I'm not sure how to return to where the original call was before the access token needed refreshing.
It doesn't work the first time it refreshes, after that the token is refreshed and then it works ok again until it expires.
So the problem is I cant get it returning to where it started on the refresh
Here is an example from the component
created(){
axios.get("http://localhost:63861/api/sampledata/WeatherForecasts").then(response => {
console.log(response.data);
//**** DO STUFF WITH THE DATA. I WANT TO GET BACK HERE AFTER REFRESH
})
.catch(error => {
console.log(error);
});
I need to get back to the point where it can do stuff with the data once it has refreshed and reset the access tokens.
my interceptor:
import axios from "axios";
import store from "../store";
import Storage from "../services/storage";
import { REFRESH } from "../store/actions.type";
export default function execute() {
// Request
axios.interceptors.request.use(
config => {
var token = Storage.getAccessToken();
if (token) {
console.log("Bearer " + token);
config.headers["Authorization"] = "Bearer " + token;
}
return config;
},
error => {
return Promise.reject(error);
}
);
// Response
axios.interceptors.response.use(
response => {
return response;
},
error => {
console.log("Error need to refresh");
const originalRequest = error.config;
// token expired
if (error.response.status === 401) {
originalRequest._retry = true;
let tokenModel = {
accessToken: Storage.getAccessToken(),
client: "Web",
refreshToken: Storage.getRefreshToken()
};
var refreshPath = "auth/" + REFRESH;
store
.dispatch(refreshPath, { tokenModel })
.then(response => {
console.log(response);
return axios(originalRequest);
})
.catch(error => {
console.log(error);
// Logout
});
}
return Promise.reject(error);
}
);
}
You need to return your refresh promise.
return store
.dispatch(refreshPath, { tokenModel })
.then(response => {
console.log(response);
return axios(originalRequest);
})
.catch(error => {
console.log(error);
// Logout
});
What is happening now is you dispatch the action, then your return Promise.reject(error) is ran. By returning the refresh promise, you ensure axios waits for that chain to finish

Using promise with GraphRequestManager

Does anyone have an example on how to use promise with GraphRequestManager?
I get Cannot read property then of undefined error in my action creator.
function graphRequest(path, params, token=undefined, version=undefined, method='GET') {
return new Promise((resolve, reject) => {
new GraphRequestManager().addRequest(new GraphRequest(
path,
{
httpMethod: method,
version: version,
accessToken: token
},
(error, result) => {
if (error) {
console.log('Error fetching data: ' + error);
reject('error making request. ' + error);
} else {
console.log('Success fetching data: ');
console.log(result);
resolve(result);
}
},
)).start();
});
}
I call the above using my action creator
export function accounts() {
return dispatch => {
console.log("fetching accounts!!!!!!");
dispatch(accountsFetch());
fbAPI.accounts().then((accounts) => {
dispatch(accountsFetchSuccess(accounts));
}).catch((error) => {
dispatch(accountsFetchFailure(error));
})
}
}
I get 'Success fetching data:' in the console along with the result before the error. So the API call is made successfully. The error is after fetching the accounts in fbAPI.accounts().then((accounts) which I think is due to GraphRequestManager returning immediately instead of waiting.
I have a solution for you.
My provider look like this :
FBGraphRequest = async (fields) => {
const accessData = await AccessToken.getCurrentAccessToken();
// Create a graph request asking for user information
return new Promise((resolve, reject) => {
const infoRequest = new GraphRequest('/me', {
accessToken: accessData.accessToken,
parameters: {
fields: {
string: fields
}
}
},
(error, result) => {
if (error) {
console.log('Error fetching data: ' + error.toString());
reject(error);
} else {
resolve(result);
}
});
new GraphRequestManager().addRequest(infoRequest).start();
});
};
triggerGraphRequest = async () => {
let result = await this.FBGraphRequest('id, email');
return result;
}
That works great ! I let you adapt my solution to your system.