Getting HTTP 415 Unsupported Media Type during authorization in the Spotify API

Viewed 1035

I want to access the Spotify web API using the "Client Credentials Flow" as mentioned here. I tried to access it using some tutorial mention here, but couldn't follow it because it uses the request library in node which has now been deprecated so, I tried using axios in it's place and keep getting the Status Code 415 error in the browser console. I'm trying to create a side project using this API for practice because I'm a beginner. I've done this using NodeJS and the normal browser console(i have used the code for browser console here) and i had axios and express libraries installed(while using node).

const client_id = '**redacted**';
const client_secret = '**redacted**';

const res = axios.post('https://accounts.spotify.com/api/token', {
  headers: {
    'Authorization': 'Basic ' + btoa(`${client_id}` + ':' + `${client_secret}`),
  },
  form: {
    grant_type: 'client_credentials'
  }
});

console.log(res)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AXIOS DOC</title>
</head>
<body>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script src="script2.js"></script>
</body>
</html>

2 Answers

The Spotify's API requires you to encode the body of POST request in application/x-www-form-urlencoded. To do this with axios you need to use other library or make serialize() function on your own (I used serialize(obj) function from this answer).

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AXIOS DOC</title>
</head>
<body>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script>
        const client_id = '**redacted**';
        const client_secret = '**redacted**';

        const serialize = function(obj) {
            var str = [];
            for (var p in obj) {
                if (obj.hasOwnProperty(p)) {
                    str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
                }
            }
            return str.join("&");
        }

        axios
            .post('https://accounts.spotify.com/api/token',
                serialize({
                    grant_type: 'client_credentials'
                }), {
                headers: {
                    'Authorization': 'Basic ' + btoa(client_id + ':' + client_secret),
                }
            })
            .then(res => console.log(res.data.access_token))
            .catch(err => {
                console.log(err);
            });
    </script>
</body>
</html>

The other solution is to simply use request library that serializes data automatically.

Also, be aware of making requests using Client Credential grant type from the client-side. This way you expose your client_secret and give other people a way to impersonate your application.

Client Credential grant type should be used only on the server-side, where you can be sure that your client_secret doesn't get exposed.

If you want to safety authenticate users on the client-side use Authorization Code With PKCE (better) or Implicit grant type.

I stumbled onto a recent code snippet that is using axios to make the POST request instead of the request library. I just tried it and was able to receive an access token! Let me know if you have any questions. Here is the link. Be sure to install the qs package.

The code looks something like this:

// Headers object.
const headers = {
 headers: {
    Accept: 'application/json',
    'Content-Type': 'application/x-www-form-urlencoded',
  },
  auth: {
    username: client_id,
    password: client_secret,
  },
};

// Data object.
const data = {
  grant_type: 'client_credentials',
};

// Make the request using the URL, query string, data, and headers.
const res = axios.post('https://accounts.spotify.com/api/token', qs.stringify(data), headers);

// Retrieve the access token from the response.
const access_token = res.data.access_token;
Related