227

I'm trying to use fetch in React Native to grab information from the Product Hunt API. I've obtained the proper Access Token and have saved it to State, but don't seem to be able to pass it along within the Authorization header for a GET request.

Here's what I have so far:

var Products = React.createClass({
  getInitialState: function() {
    return {
      clientToken: false,
      loaded: false
    }
  },
  componentWillMount: function () {
    fetch(api.token.link, api.token.object)
      .then((response) => response.json())
      .then((responseData) => {
          console.log(responseData);
        this.setState({
          clientToken: responseData.access_token,
        });
      })
      .then(() => {
        this.getPosts();
      })
      .done();
  },
  getPosts: function() {
    var obj = {
      link: 'https://api.producthunt.com/v1/posts',
      object: {
        method: 'GET',
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json',
          'Authorization': 'Bearer ' + this.state.clientToken,
          'Host': 'api.producthunt.com'
        }
      }
    }
    fetch(api.posts.link, obj)
      .then((response) => response.json())
      .then((responseData) => {
        console.log(responseData);
      })
      .done();
  },

The expectation I have for my code is the following:

  1. First, I will fetch an access token with data from my imported API module
  2. After that, I will set the clientToken property of this.state to equal the access token received.
  3. Then, I will run getPosts which should return a response containing an array of current posts from Product Hunt.

I am able to verify that the access token is being received and that this.state is receiving it as its clientToken property. I am also able to verify that getPosts is being run.

The error I'm receiving is the following:

{"error":"unauthorized_oauth", "error_description":"Please supply a valid access token. Refer to our api documentation about how to authorize an api request. Please also make sure you require the correct scopes. Eg \"private public\" for to access private endpoints."}

I've been working off the assumption that I'm somehow not passing along the access token properly in my authorization header, but don't seem to be able to figure out exactly why.

3
  • 2
    As noted in this SO, headers are intended to be lowercase (some servers respect this, others do not.) I only share because I was bitten by it not knowing myself (and wasted time trying to debug the issue.) It is unfortunate that so many projects, examples, and articles do not seem to respect this.
    – t.j.
    Commented May 25, 2018 at 17:57
  • @t.j. Header names are not case sensitive, and that's exactly what the accepted + top answer says on the question you linked.
    – coreyward
    Commented Jan 2, 2019 at 22:33
  • 1
    5.5 years later and I was setting a fetch header just like the OP: 'Authorization': 'Bearer ' + myJWT, Turns out myJWT was getting wrapped in double quotes! Authorization: Bearer "yadda.yadda.yadda" After many attempts at a solution, I filtered the double quotes on my back-end. Commented Jan 17, 2021 at 19:08

5 Answers 5

313

Example fetch with authorization header:

fetch('URL_GOES_HERE', { 
    method: 'post', 
    headers: new Headers({
        'Authorization': 'Basic '+btoa('username:password'), 
        'Content-Type': 'application/x-www-form-urlencoded'
    }), 
    body: 'A=1&B=2'
});
13
  • 10
    This is not working for me. The 'Authorization' header silently fails to attach per firebug. I've even tried including credentials: 'include' in the optional object. Commented Dec 6, 2016 at 4:13
  • 8
    @RonRoyston are you looking at the OPTIONS call? if the API endpoint doesn't have CORS enabled (Access-Control-Allow-Origin: * if accessing from a different domain), then it might fail at the OPTIONS call.
    – Cody Moniz
    Commented Dec 7, 2016 at 23:49
  • 1
    api endpoint does not have CORS enabled, so that's probably why it didn't work for me. Thanks. I ended up installing 'cors everywhere' add-on for firefox and it worked. Commented Dec 8, 2016 at 17:03
  • 4
    Re the issue @RonRoyston is seeing, you need to import the btoa library, which is not native to node. (It's a port of a browser lib.) Otherwise auth header creation silently fails. We were experiencing the same thing.
    – Freewalker
    Commented Feb 24, 2017 at 21:15
  • 3
    developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch per docs, need to wrap headers with new Headers() Commented Mar 22, 2018 at 14:39
102

It turns out I was using the fetch method incorrectly.

fetch accepts two parameters: an endpoint to the API, and an optional object which can contain body and headers.

I was wrapping the intended object within a second object, which did not get me any desired result.

Here's how it looks on a high level:

    fetch('API_ENDPOINT', options)  
      .then(function(res) {
        return res.json();
       })
      .then(function(resJson) {
        return resJson;
       })

I structured my options object as follows:

    var options = {  
      method: 'POST',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
        'Origin': '',
        'Host': 'api.producthunt.com'
      },
      body: JSON.stringify({
        'client_id': '(API KEY)',
        'client_secret': '(API SECRET)',
        'grant_type': 'client_credentials'
      })
    }
5
  • could you perhaps provide the now working code? I'm trying to use fetch with an authorization header and I don't think my auth code is being passed as a header, because I'm getting a 401 response.
    – MikaelC
    Commented Aug 4, 2015 at 22:53
  • 2
    Done, hope it's helpful Commented Aug 4, 2015 at 22:55
  • 1
    Oh i've been on your personal site with that example! That's how I modeled mine the first time. I figured out my issue though, it was just that my url was wrong. It required a / at the end that I was missing...
    – MikaelC
    Commented Aug 5, 2015 at 0:42
  • 1
    Thanks, this was helpful. Worth noting that while the fetch documentation points out that fetch doesn't handle cookies, you can manually add cookies to the header with this code as well. Just save the uid and key and do something like: var obj = { method: 'GET', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Cookie': 'uid='+uid+'; key='+key });
    – Dustin
    Commented Oct 23, 2015 at 13:44
  • @RichardKho could you please check my post here. Was not able to get an answer until now.. stackoverflow.com/questions/75235933/…
    – 3awny
    Commented Feb 1, 2023 at 11:31
38

The following code snippet should work if you're using the bearer token :

const token = localStorage.getItem('token')

const response = await fetch(apiURL, {
    method: 'POST',
    headers: {
        'Content-type': 'application/json',
        'Authorization': `Bearer ${token}`, // notice the Bearer before your token
    },
    body: JSON.stringify(yourNewData)
})
16

I had this identical problem, I was using django-rest-knox for authentication tokens. It turns out that nothing was wrong with my fetch method which looked like this:

...
    let headers = {"Content-Type": "application/json"};
    if (token) {
      headers["Authorization"] = `Token ${token}`;
    }
    return fetch("/api/instruments/", {headers,})
      .then(res => {
...

I was running apache.

What solved this problem for me was changing WSGIPassAuthorization to 'On' in wsgi.conf.

I had a Django app deployed on AWS EC2, and I used Elastic Beanstalk to manage my application, so in the django.config, I did this:

container_commands:
  01wsgipass:
    command: 'echo "WSGIPassAuthorization On" >> ../wsgi.conf'
1
  • django.config its in a project or in apache config? Commented May 10, 2021 at 13:26
-7
completed = (id) => {
    var details = {
        'id': id,

    };

    var formBody = [];
    for (var property in details) {
        var encodedKey = encodeURIComponent(property);
        var encodedValue = encodeURIComponent(details[property]);
        formBody.push(encodedKey + "=" + encodedValue);
    }
    formBody = formBody.join("&");

    fetch(markcompleted, {
        method: 'POST',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/x-www-form-urlencoded'
        },
        body: formBody
    })
        .then((response) => response.json())
        .then((responseJson) => {
            console.log(responseJson, 'res JSON');
            if (responseJson.status == "success") {
                console.log(this.state);
                alert("your todolist is completed!!");
            }
        })
        .catch((error) => {
            console.error(error);
        });
};
0

Not the answer you're looking for? Browse other questions tagged or ask your own question.