I am trying to make a call using JavaScript's Fetch API to generate an OAuth Token but I keep receiving a 400 response code and I'm not sure why. I wrote the key and secret to the console to verify their values, and I made the same API call using cURL (with the response I expected). Is there a small issue in my syntax?
fetch('https://api.petfinder.com/v2/oauth2/token', {
method: 'POST',
body: 'grant_type=client_credentials&client_id=' + key + '&client_secret=' + secret
}).then(r => { response = r.json() });
If the request body is a string, the Content-Type header is set to text/plain;charset=UTF-8 by default. Since you're sending urlencoded data, you have to set the Content-Type header to application/x-www-form-urlencoded.
fetch('https://api.petfinder.com/v2/oauth2/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'grant_type=client_credentials&client_id=' + key + '&client_secret=' + secret
})
As I mentioned in a comment, you shouldn't make the above request from a browser since it exposes the client secret.
Thanks to #Arun's recommendation of adding Content-Type, I am getting the right response now.
Also, for any other JavaScript newbies playing around with the petfinder API, this is the chain that I used to extract the token from the response:
fetch('https://api.petfinder.com/v2/oauth2/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'grant_type=client_credentials&client_id=' + key + '&client_secret=' + secret
}).then(response => response.json().then(data => ({
data: data,
status: response.status})
).then(function(res) {
console.log(res.status, res.data.access_token);
}));
Related
I'm trying to use the fetch API in vanilla JavaScript to generate a token provided by Django OAuth Toolkit. The application I've created in DOT uses the "Resource owner password-based" authorization grant type. My code looks like this (grant_type, username and password are provided through request.formData()):
const data = await request.formData();
const oauth = await fetch(`${API_ROOT}/o/token`, {
method: 'POST',
headers: {
'Content-Type': 'multipart/form-data',
Authorization: `Basic ${Buffer.from(CLIENT_ID + ':' + CLIENT_SECRET).toString('base64')}`
},
body: data
});
This request imitates a successful GET request I've created using Insomnia (with Multipart Form data for grant_type, username and password + CLIENT_ID and CLIENT_SECRET as the username and password in Basic Auth). In other words, I don't understand why the JavaScript fetch request does not work even though it is supposed to be identical to the Insomnia request. The JavaScript fetch request returns a 400 error. When I remove the Content-Type header, I get a 500 error. What am I doing wrong?
EDIT: It may be worth noting that I am making this fetch call within a SvelteKit application.
As it turns out, in this particular case I DID need to set Content-Type. I found this answer: Trying to get access token with django-oauth-toolkit using fetch not working while working using jquery
My code works as follows:
const data = await request.formData();
const oauth = await fetch(`${API_ROOT}/oauth/token/`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
},
Authorization: `Basic ${Buffer.from(CLIENT_ID + ':' + CLIENT_SECRET).toString('base64')}`,
},
body: formDataToUrlEncoded(data)
});
The formDataToUrlEncoded function roughly ressembles the one posted in the above post:
export function formDataToUrlEncoded(formData) {
var url_encoded = '';
for (var pair of formData) {
if (url_encoded != '') {
url_encoded += '&';
}
url_encoded += encodeURIComponent(pair[0]) + '=' + encodeURIComponent(pair[1]);
}
return url_encoded;
}
I have a function which uses Axios to send a POST request which goes through successfully and I get the right response. Now I want to try using fetch to do the exact same POST request. Unfortunately, the fetch request returns a 415 Unsupported Media Type response error and I have no idea why.
Currently:
onBeforeUnload = () => {
try {
const defaultPresence = {
presence: 'AVAILABLE',
message: '',
};
const url = getServerURL() + urls.PRESENCE;
axios.post(
url,
defaultPresence,
{
headers: {
Authorization: `Bearer ${getAccessToken()}`,
},
},
);
} catch (error) {
console.log(error);
}
}
The fetch code I've used to replace the Axios POST request.
fetch(url, {
method: 'POST',
headers: {
Authorization: `Bearer ${getAccessToken()}`,
},
body: defaultPresence,
});
fetch does not recognise plain objects as the body.
If you want to send JSON then you need to encode the data and set the content-type header yourself.
fetch(url, {
method: 'POST',
headers: {
Authorization: `Bearer ${getAccessToken()}`,
"Content-Type": "application/json",
},
body: JSON.stringify(defaultPresence),
});
whats up!
i am trying to send a request with json parameters and i don't understand why in the server side
the parameter doesn't being send
let res = fetch("http://localhost:1000/vacations/" + vacation.id, {
method: "DELETE",
headers: { "Authorization": localStorage.token },
body: { "picture": vacation.picture }
})
i am trying to view the picture in the parameter in the server side
i use in node.js server the middleware express.json
and still i cant get this parameter :(
You may send a stringify body using JSON.stringify.
const res = fetch(`http://localhost:1000/vacations/${vacation.id}`, {
method: "DELETE",
headers: {
Authorization: localStorage.token
},
body: JSON.stringify({
picture: vacation.picture
})
})
EDIT:
I was reading the documentation wrong. In this world of JSON I didn't notice the request was sending form data. Silly mistake.
It was also the wrong endpoint.
The request should have looked like:
fetch(
'https://api.amazon.com/auth/o2/token/',
{
method: 'POST',
headers:{
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
},
body:
`?redirect_uri=${redirectUri}` +
`&code=${clientCode}` +
`&client_id=${clientId}` +
`&client_secret=${clientSecret}` +
'&grant_type=authorization_code'
}
)
I'm trying to trade a token obtained with the code flow in cognito's login page, but the request responds with a status 400 "malformed request".
The documentation I am following is: https://developer.amazon.com/docs/login-with-amazon/authorization-code-grant.html#access-token-request
I am using the browser to make the following request:
fetch(
'https://api.amazon.com/auth/o2/token/' +
`?redirect_uri=${encodeURIComponent(redirectUri)}` +
`&code=${encodeURIComponent(clientCode)}` +
`&client_id=${encodeURIComponent(clientId)}` +
`&client_secret=${encodeURIComponent(clientSecret)}` +
'&grant_type=authorization_code',
{
method: 'POST',
headers:{
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
}
}
)
Which results in the client sending the following request:
POST
https://api.amazon.com/auth/o2/token/?redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Floggedin&code=<code>&client_id=<client_id>o&client_secret=<client_secret>&grant_type=authorization_code
The response:
{
"error_description": "Malformed request",
"error":"invalid_request"
}
I m using Isomorphic fetch in my application and I m having some troubles dealing with CSRF.
Actually, I m having a backend that sends me a CSRF-TOKEN in set-cookies property :
I have read somewhere that it's not possible, or it's a bad practice to access this kind of cookies directly inside of my code.
This way, I tried to make something using the credentials property of fetch request :
const headers = new Headers({
'Content-Type': 'x-www-form-urlencoded'
});
return this.fetcher(url, {
method: 'POST',
headers,
credentials: 'include',
body: JSON.stringify({
email: 'mail#mail.fr',
password: 'password'
})
});
This way, I m able to send my CSRF cookie back to my server to serve my need (it's a different one, because it s not the same request) :
My problem
My problem is that my backend needs to receive a x-csrf-token header and so I can't set it to my POST request.
What I need
How can I do to put the value of set-cookies: CSRF-TOKEN into the next request x-csrf-token header ?
It looks like in your scenario you are supposed to read from CSRF-TOKEN cookie. Otherwise it would be marked HttpOnly as JSESSIONID. The later means you cannot access it from the web page but merely send back to server automatically.
In general there is nothing wrong in reading CSRF token from cookies. Please check this good discussion: Why is it common to put CSRF prevention tokens in cookies?
You can read your cookie (not HttpOnly, of cause) using the following code
function getCookie(name) {
if (!document.cookie) {
return null;
}
const xsrfCookies = document.cookie.split(';')
.map(c => c.trim())
.filter(c => c.startsWith(name + '='));
if (xsrfCookies.length === 0) {
return null;
}
return decodeURIComponent(xsrfCookies[0].split('=')[1]);
}
So fetch call could look like
const csrfToken = getCookie('CSRF-TOKEN');
const headers = new Headers({
'Content-Type': 'x-www-form-urlencoded',
'X-CSRF-TOKEN': csrfToken
});
return this.fetcher(url, {
method: 'POST',
headers,
credentials: 'include',
body: JSON.stringify({
email: 'test#example.com',
password: 'password'
})
});
Yes header name depends on your server. For example django usecase to setup CSRF token using fetch is like this:
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json; charset=UTF-8',
'X-CSRFToken': get_token
},