Reputation: 353
I am trying to authorize spotify api requests using Client Credentials Flow on the Spotify API Docs page. Here is my code in javascript ES6 format using the fetch API
const response = await fetch('https://accounts.spotify.com/api/token', {
mode: 'no-cors',
method: 'POST',
headers: {
'Authorization': 'Basic Yzg4OWYzMjM5MjI0NGM4MGIyMzIyOTI5ODQ2ZjZmZWQ6MmUzZTM2YTMzMTM5NDM1Mzk3NzM4ZDMxMTg4MzM0Mjc=',
'Content-type': 'application/x-www-form-urlencoded'
},
body: 'grant_type=client_credentials'
});
The console is saying it is a bad request and doesn't return any JSON.
Another thing that really confuses me is that when I send the request using POSTMAN with those headers and that body, it returns exactly what I want (It works) I don't see how this is different from what I'm doing...? Could anyone please help?
Also here is the code from postman in Javascript Jquery Ajax if this helpls:
var settings = {
"async": true,
"crossDomain": true,
"url": "https://accounts.spotify.com/api/token",
"method": "POST",
"headers": {
"Authorization": "Basic Yzg4OWYzMjM5MjI0NGM4MGIyMzIyOTI5ODQ2ZjZmZWQ6MmUzZTM2YTMzMTM5NDM1Mzk3NzM4ZDMxMTg4MzM0Mjc=",
"Content-Type": "application/x-www-form-urlencoded",
"Cache-Control": "no-cache",
"Postman-Token": "2f93918d-2e8e-4fb0-a168-7e153dd83912"
},
"data": {
"grant_type": "client_credentials"
}
}
$.ajax(settings).done(function (response) {
console.log(response);
});
This is what the request looks like in DevTools
Upvotes: 4
Views: 18519
Reputation: 76
As Patrick has already mentioned, client credentials is for server side script.
I was testing Spotify API calls locally and wanted to use fetch
as opposed to the request
library as is done in the client credentials documentation, but found it harder than anticipated.
This approach is similar to the docs, using fetch
instead of post.request
, with a similar set of request options.
Main differences are in adding the 'Content-Type'
option within the headers, and replacing the form
object property with a body
string property.
const client_id = require('./keys.js').client_id; // Your client id
const client_secret = require('./keys.js').client_secret; // Your secret
// get authorisation token from Spotify
async function getSpotifyToken() {
const url = 'https://accounts.spotify.com/api/token';
response = await fetch(url, {
method: 'POST',
headers: {
'Authorization': 'Basic ' + (Buffer.from(client_id + ':' + client_secret).toString('base64')),
'Content-Type': 'application/x-www-form-urlencoded'
},
body: 'grant_type=client_credentials',
json: true
});
if (response.ok) {
const jsonResponse = await response.json();
console.log(jsonResponse);
} else {
console.log(response.statusText);
throw new Error(`Request failed! Status code: ${response.status} ${response.statusText}`);
}
}
getSpotifyToken()
It'll work if you're making requests from your machine, or from a server, I assume. If you're calling the API from the browser, it's probably easiest to use a different authorisation flow.
Hope that helps.
Upvotes: 0
Reputation: 42736
That particular endpoint is not meant to be consumed client side. You are supposed to use it in some server side script.
The Client Credentials flow is used in server-to-server authentication
Another hint that it is meant to be server side only is that it uses your client secret as its name implies it is meant to be kept secret and having it viewable on the client isn't very secret.
So from that endpoint you get the access token which you can then use on the client side to make requests to the other api endpoints like https://api.spotify.com/v1/tracks
Now as for why it doesn't work in your calls. It works in postman because it ignores CORS, and it properly sends the authorization header. In the browser however you set the fetch()
request mode to no-cors
. In this mode only certain headers can be sent AND the response back cannot be read by javascript.
Due to this your request does not send the authorization header as it is not one of the simple headers allowed in no-cors mode. And so your request fails. Even if the authorization went through you wouldn't have been able to read the response anyways as per no-cors rules.
So if you want to continue using the Client Credentials flow you would:
From the browser, make a request to your own server.
fetch("http://myserver.com/getToken")
On the server you would then do the https://accounts.spotify.com/api/token
request from there sending all the correct information. Then send the returned access token back to the client
//this is assuming a nodejs server environment
var postQuery = 'grant_type=client_credentials ';
var request = require('request');
var express = require('express');
var app = express();
app.get('/getToken', function(req, res){
request({
url: "https://accounts.spotify.com/api/token",
method: "POST",
headers: {
'Authorization': 'Basic YourBase64EncodedCredentials',
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': postQuery.length
},
body: postQuery
}, function (error, response, data){
//send the access token back to client
res.end(data);
});
});
Use that access token in a normal fetch request to the endpoints you need to use as they are setup with the proper CORS headers
fetch("http://myserver.com/getToken")
.then(token=>{
//token will be the token returned from your own server side script
//now we can make a new request to the tracks (or any other api)
return fetch("https://api.spotify.com/v1/tracks",{
headers:{
'Authorization': `Bearer ${token}`
}
}).then(r=>r.json())
})
.then(data=>{
//data will be the data returned from tracks api endpoint
});
Upvotes: 6