I'm using fetch API for interacting with server in my react-native#0.28 app, but facing with quite aggressive caching.
Call which I proceed can be expressed like:
fetch(route + '&_t=' + Date.now(), {
headers: {
'Cache-Control': 'no-cache',
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json',
'Custom-Auth-Header': 'secret-token'
},
method: 'POST',
body: data,
cache: 'no-store'
})
In IOS simulator response get cached for 15-20 mins, can be cleared via Reset Content and Settings.
In result I just don't want to have any cache for any of my calls (including GET requests).
I tried all options which I know in order to avoid caching, but seems there is something else, any help would be very appreciated!
It turned out caching was caused by the server setting the session cookie. iOS/Android handles cookies automatically so it was used with every fetch call.
The solution was to delete all the cookies on logout using the https://github.com/joeferraro/react-native-cookies library.
Related
Before going into more detail about the title, I just want to describe the problem at a basic level. I'm getting the dreaded 422, Unprocessable Entity error (ActionController::InvalidAuthenticityToken) after asynchronous requests in my app. What's the best way to handle this?
Loads of answers will say do:
skip_before_action :verify_authenticity_token
(which is now skip_forgery_protection in Rails 6), and then usually in the comments people get way more upvotes asking about whether or not that's a security risk. There are probably 5 threads like that.
The alternatives to doing this though, is sending the csrf token along with the POST request. So, most answers say make sure to include these headers
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
and also a csrf token like:
'X-CSRF-Token': csrfToken
The issue is getting the csrf token itself. I've tried displaying the contents of the header object with
const getHeaders = () => {
let headers = new window.Headers({
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
})
const csrfToken = document.head.querySelector("[name='csrf-token']")
if (csrfToken) { headers.append('X-CSRF-Token', csrfToken) }
return headers
}
and
let headers = getHeaders();
headers.forEach(function(value, name) {
console.log(name + ": " + value);
});
and that doesn't work. I'm guessing however I'm actually getting the token isn't working. Namely:
document.head.querySelector("[name='csrf-token']")
What's the best way to do this with plain ol' JavaScript, no other libraries? I've tried other suggestions and methods to attempt to get the token but so far all have failed. I'm assuming this lives somewhere on my browser but where? I can see all the data in Redux dev tools so the issue is definitely just this token as opposed to sending the correct data correctly (not to mention skip_forgery_protection completely solves it).
In the mean time sadly skip_forgery_protection works perfectly fine added to my Rails controllers, as security isn't the biggest concern in this stage, but I would rather fix the token issue. Thanks!
I have the following stack:
Rails API (backend)
Next.js (frontend)
In my Rails API, I am tracking where the request is coming from such as ip, user_agent, city, country, latitude, and longitude.
But the problem is that, instead of the user's data, it's the server's data that is being sent.
In my Next.js app, I use https://nextjs.org/docs/api-routes/introduction, since the access_token for the API is stored in the session. And I don't want to expose it to the client.
So the process would be, from React, I will make a POST request to /api/process/ of Next.js API endpoint. Then in /api/process I will take the access_token from the session and make a request to Rails API.
Since the Next.js API endpoint is the one made the request to Rails API. My Rails API stores the location of the frontend server instead of the actual user's info.
Pseudocode:
In React Frontend:
fetch('/api/process', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({ some: 'data' })
})
In pages/api/process.js:
export default function handler(req, res) {
fetch(RAILS_API_ENDPOINT, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: `Bearer ${req.session.token}` // attach token here
},
body: req.body
})
}
Is there any workaround for this? I don't seem to want to expose the access_token in the client and make the request there to the Rails API directly.
You've set up a proxy API, as a result the Rails server knows nothing about the FE client because theres a layer in between. So there are 2 options:
Set up the FE client to call rails directly (which you said you don't want to do).
Forward this FE client information as part of either headers or in the payload to the rails API. From there the Rails API can then do whatever it wants with this data.
I'm trying to connect to an rss api provider 'Inoreader' and I'm using react native. I am able to get the authorization code but when I submit a post request for exchanging with tokens, I get 400 bad request. The response text is undefined. I checked and all their parameters are matching with my app's. I have tried.
This is their documentation: https://www.inoreader.com/developers/oauth
fetch('https://www.inoreader.com/oauth2/token', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Host': 'www.inoreader.com',
'Content-length': '217',
'User-Agent': navigator.userAgent,
'Content-type': 'application/x-www-form-urlencoded'
},
body: JSON.stringify({
'code':`${this.state.auth_code}&redirect_uri=${this.state.gizmos}&client_id=${this.state.userId}&client_secret=${this.state.userKey}&scope=&grant_type=authorization_code`
})
})
.then((res) => {
this.setState({
userName: res.access_token
});
console.log(res.status);
});
I see three problems in your code
You have a fixed Content-length value (217) from the Inoreader example. This way, the server reads just 217 characters of the request and the rest is discarded if the request is longer.
The request Content-type is urlencoded, but you probably don't URL encode the values. You can use the [encodeURIComponent()][1] function to do it.
The /token endpoint requires you to send a client secret, but your application cannot keep it safe, so the secret can easily get compromised. As they write in the guide, the request should be done from a backend. Or you can ask them to support OAuth2 for native apps.
I have the following code in my component:
fetch('https://domain.com/api', {
method: 'POST',
headers: {'Accept': 'application/json', 'Content-Type': 'application/json'},
body: JSON.stringify({
key: 'value'
})
}).
then((response) => {
console.log('Done', response);
});
And every time the request is a GET (checked server logs). I thought it was something to do with CORS (but apparently no such thing in react-native) and ATS (but already turned off by default, plus my domain is HTTPS). I've tried from a browser and from a curl and it worked perfectly, so a priori no issue on server configuration. Any idea what's going on here?
I'm using the latest react-native version.
After further digging, it was definitely an issue with the API + fetch. I was missing a slash at the end of the URL and the API issued a 301, that fetch didn't handle correctly. So I don't know if there is something to fix in the fetch function (and underlying mechanisms) but this fixed my issue :)
When a POST is redirected (in my case from http to https) it gets transformed into a GET. (Don't know why...)
I've installed Luracast's Restler API framework and am having marvelous success with it all except when sending PUT or DELETE across domains. The below works fine when all on the same server, but when I cross domains, Firebug shows the the PUT or GET as OPTIONS, and it is not found on the server. Am baffled how to stop "OPTIONS" being sent instead of PUT or DELETE.
$.ajax({
url: url,
type: 'PUT',
data: "thename="+ $('#TheName').val(),
success: function(xhr, status) {
console.info(xhr);
},
error: function(xhr, status) {
console.info(xhr.responseText);
},
complete: function(xhr, status) {
$('#showResponse').val(xhr.responseText);
}
});
Per another thread somewhere, I've added the below to the Restler output:
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, DELETE, PUT, OPTIONS');
You've got the right response headers, but you have to have your server respond to an OPTIONS request with those headers, too.
This is a cross-origin request, and is subject to something called preflighting. Before making the PUT or DELETE request the browser asks the target web server if it's safe to do so from a web page at another domain. It asks that using the OPTIONS method. Unless the target server says it's okay, the web browser will never make the PUT or DELETE request. It has to preflight the request, because once it's made the PUT or DELETE, it's too late to honor the response; sensitive information may have been leaked.
GET and POST are a bit more complicated, as sometimes the browser decides they are safe without asking first, while other times the browser will also do a preflight check. It depends on whether certain headers are used in the request.
The CORS spec has the gory details. The bottom line is that the code on your web page will not be allowed to make these requests unless the target web server supports the OPTIONS method, and the response to the OPTIONS method includes the headers saying that such requests are allowed.