Spotify API OAUTH2 Server Error Response when requesting token - javascript

Spotify Oauth2 Docs - Client Credential Flow
I asked a similar question, and I was able to get the code running in google apps script (javascript). I am using Client Credential Flow. However, I tried to recreate this code in Nodejs. When running this code, I receive a server error.
error: "server_error"
This is the problem code in Nodejs
const defaultConfig = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
// this encodes the app_id and api_key into a base64 string like the documentation states
'Authorization': `Basic ${btoa(spotify.API_ID + ':' + spotify.API_KEY)}`
},
body: JSON.stringify({
'grant_type': 'client_credentials',
})
};
// this function is wrapped in a list titled, "apiSettings" with other functions. I just included the authorization function here:
const apiSettings = {
getSpotifyAuthorizeToken: async () => {
const endpoint = "https://accounts.spotify.com/api/token";
return await (await fetch(endpoint, defaultConfig)).json();
},
};
I got this similar code to work in google apps script with no problems.
Could the server error be a result of me running Nodejs on a "localhost:xxxx"? The documentation states that, "The Client Credentials flow is used in server-to-server authentication."
if this is not the case, could you provide a working

Spotify API OAUTH2 Server Error Response when requesting token
From your previous question, I would like to propose the following sample scripts.
Sample script 1:
If you want to convert the script of this answer to Node.js and node-fetch, how about the following script?
const spotify = { API_ID: "API_ID", API_KEY: "API_KEY" }; // Please set your client ID and client secret.
const buf = Buffer.from(spotify.API_ID + ":" + spotify.API_KEY);
const para = new URLSearchParams();
para.append("grant_type", "client_credentials");
const defaultConfig = {
method: "POST",
headers: { Authorization: `Basic ${buf.toString("base64")}` },
body: para,
};
const apiSettings = {
getSpotifyAuthorizeToken: async () => {
const endpoint = "https://accounts.spotify.com/api/token";
return await (await fetch(endpoint, defaultConfig)).json();
},
};
Sample script 2:
If you want to convert the script of this answer to Node.js and node-fetch, how about the following script?
const spotify = { API_ID: "API_ID", API_KEY: "API_KEY" }; // Please set your client ID and client secret.
const para = new URLSearchParams();
para.append("grant_type", "client_credentials");
para.append("client_id", spotify.API_ID);
para.append("client_secret", spotify.API_KEY);
const defaultConfig = {
method: "POST",
body: para,
};
const apiSettings = {
getSpotifyAuthorizeToken: async () => {
const endpoint = "https://accounts.spotify.com/api/token";
return await (await fetch(endpoint, defaultConfig)).json();
},
};
Reference:
node-fetch

Related

Using fetch to get access token from Django OAuth Toolkit

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;
}

How to sign API Gateway URL that has a query string

I am trying to use SignatureV4 to sign a request for an API Gateway endpoint that uses IAM Authorizor. An issue is that I keep getting a 403 error whenever I append a query string to my URL, i.e. /pets?type=1. Everything works fine when a query string is not included, i.e. /pets
This is how I build a request:
const region = 'xxx'
const method = 'GET'
const protocol = 'https:'
const host = `xxx.execute-api.${region}.amazonaws.com`
const path = '/dev/pets'
const query = {
type: 1,
}
const request = new HttpRequest({
method: method,
protocol: protocol,
hostname: host,
path: path,
query: query,
headers: {
'Content-Type': 'application/json',
host: host ,
},
})
const signer = new SignatureV4({
credentials: AWS.config.credentials,
service: 'execute-api',
region: region,
sha256: Sha256,
})
const { headers } = await signer.sign(request)
const response = await fetch(`${protocol}//${host}${path}?type=1`, {
headers,
method
}).then((res) => res.json())
I've tried running the same query within Postman and it worked just fine. So, I have to assume that an issue is with my implementation.
An issue was due to getCanonicalQuery ignoring values that are not string or array:
https://github.com/aws/aws-sdk-js-v3/blob/main/packages/signature-v4/src/getCanonicalQuery.ts#L19
So, to fix this I had to swap my query to below:
const query = {
type: '1',
}

Authorization Header missing on POST request [duplicate]

This question already has an answer here:
Error when accessing API with fetch while setting mode to 'no-cors' [duplicate]
(1 answer)
Closed 2 years ago.
I'm trying to send a POST request to a URL, using fetch().
I'm deliberately setting the Authorization header but when I inspect the response in Firefox Dev Tools it outputs following error "Missing request header 'Authorization' for method parameter of type String".
var target_url = "https://api.sonos.com/login/v3/oauth/access";
var encoded_msg = btoa(client_id + ':' + secret); // base64-encodes client_id and secret using semicolon as delimiter
var params = `grant_type=authorization_code` + `&code=${authCode}` + `&redirect_uri=${redirect_uri}`;
var myHeaders = new Headers({
'Authorization': `Basic ${encoded_msg}`,
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST',
'Content-Length': params.length,
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
});
fetch(target_url, {
method: 'POST',
mode: 'no-cors',
credentials: 'include',
redirect: 'follow',
headers: myHeaders,
body: params
})
.then(response => {
console.log("Status: " + response.status);
console.log("StatusText: " + response.statusText);
console.log("Type: " + response.type);
console.log("URL: " + response.url);
});
What removes the Authorization-Header, why and how do I prevent it?
Edit:
For Clarification, I'm using Firebase Cloud Functions to host my webpage from which I send the request to the Sonos Authorization API.
Using Postman, the request goes through and I get the correct response.
The step you're performing to retrieve an access token must be performed in a Cloud function endpoint.
Get an access token
Once you’ve gotten an authorization code, use it to get an access token and refresh token. Use the access token to send API calls to the household through the Sonos cloud. This step uses your client secret, so it should be a server-side request rather than a client-side (browser-based) request.
Reference:
https://developer.sonos.com/build/direct-control/authorize/
Bring in node-fetch as a dependency in your package.json because it's API implementation follows closely with browser fetch.
Add an endpoint as follows:
const fetch = require('node-fetch');
const functions = require('firebase-functions');
const secret = functions.config().sonos.secret;
const client_id = functions.config().sonos.client_id;
const redirect_uri = functions.config().sonos.redirect_uri;
exports.retrieveAccessToken = functions.https.onRequest(async (req, res) => {
const {authCode} = req.query;
const target_url = "https://api.sonos.com/login/v3/oauth/access";
const encoded_msg = btoa(`${client_id}:${secret}`); // base64-encodes client_id and secret using semicolon as delimiter
const body = `grant_type=authorization_code&code=${authCode}&redirect_uri=${redirect_uri}`;
const headers = new Headers({
'Authorization': `Basic ${encoded_msg}`,
'Content-Length': body.length,
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
});
const response = await fetch(target_url, {
method: 'POST',
redirect: 'follow',
headers,
body
});
const token_data = await response.json();
return token_data;
});
Modify code in your webpage to make a request to the cloud function endpoint after user returns from Sonos login service.
const authCode = new URLSearchParams(window.location.search).get('code');
fetch(`https://us-central1-<project-id>.cloudfunctions.net/retrieveAccessToken?authCode=${code}`);

How to use Stripe's secret key and publishable key?

I would like to connect with the Stripe API using a https call using the https library.
var https = require('https');
I have gotten the secret key and publishable key and put it inside a object:
var stripe = {
secret_key: 'secret_key_given_in_the_dashboard',
publishable_key: 'publishable_key_given_in_the_dashboard'
}
I am now in the part of creating the requestDetail object:
var requestDetails = {
'protocol' : 'https:',
'hostname' : 'api.stripe.com',
'method' : 'POST', //WHICH OF POST GET PUT DELETE SHOULD I USE?
'path' : '???????????????????????',// WHICH ENDPOINT SHOULD I USE?
'auth' : '???????????????????????',// SHOULD I USE THE SECRET AND PUBLISHABLE KEY HERE?
'headers' : {
'Content-Type' : 'application/x-www-form-urlencoded',
'Content-Length' : Buffer.byteLength(stringPayload)
}
};
I plan to make use of the requestDetails object in the call using https:
var req = https.request(requestDetails, function(res){
// Grab the status of the sent request
var status = res.statusCode;
//Callback successfully if the request went through
if(status == 200 || status == 201) {
callback(false);
} else {
callback('Status code returned was ' + status);
}
});
Where and how should I use the secret key and publishable key in order to make a call to the stripe API?
Which endpoint?
Which method (POST, GET, PUT,or DELETE)?
I would like to eventually create a order and pay through the STRIPE api.
But for now just any authenticated call through the stripe api will do as I need a sample format that works....
I'm not too sure where to add the secret key and publishable key....
You should install official stripe package (source: https://github.com/stripe/stripe-node), require the package and authenticate it using your secret key ( example from the github docs):
const stripe = require('stripe')('your_stripe_secret_key');
stripe.customers.create({
email: 'customer#example.com',
})
.then(customer => console.log(customer.id))
.catch(error => console.error(error));
The package is an abstraction to make the API requests for you.
More docs: https://stripe.com/docs/api?lang=node
However, if you want to use the https directly for Stripe API requests, which is not recommended, you can check the docs and examples for using the cURL, since it shows the endpoints for each example.
https://stripe.com/docs/api/authentication?lang=curl
try using fetch, 'Authorization': 'Bearer ' + sk.
My working example of retrieving a customer based on the customer_id:
const url = `https://api.stripe.com/v1/customers/${stripe_customer_id}`;
return await fetch(url, {
method: "get",
headers: {
"Content-Type": "application/json",
'Authorization': 'Bearer ' + sk,
}
})
.then(function(response) {
return response.json();
})
.then(function(response) {
// console.log(response);
return response;
});
};

CoinMarketCap API to Angular request

I have my head in water with the new CoinMarketCap API.
Below is an example of a request in Node. How can I make a request in Angular?
Any suggestions?
Thanks.
/* Example in Node.js ES6 using request-promise, concepts should translate
to your language of choice */
const rp = require('request-promise');
const requestOptions = {
method: 'GET',
uri: 'https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest',
qs: {
start: 1,
limit: 5000,
convert: 'USD'
},
headers: {
'X-CMC_PRO_API_KEY': 'API_KEY_HERE'
},
json: true,
gzip: true
};
rp(requestOptions).then(response => {
console.log('API call response:', response);
}).catch((err) => {
console.log('API call error:', err.message);
});
As per the documentation found here you cannot perform this HTTP call from a web client:
Making HTTP requests on the client side with Javascript is currently prohibited through CORS configuration. This is to protect your API Key which should not be visible to users of your application so your API Key is not stolen. Secure your API Key by routing calls through your own backend service.
A solution would be to create your own back-end API. This API can then perform the HTTP call to Coinmarketcap. Your website then communicates with your custom made back-end API.
getAllCoinsListing() {
const httpOptions = {
headers: new HttpHeaders({
'X-CMC_PRO_API_KEY': 'API_KEY_HERE'
})
};
return this.http.get(this.apiUrl, httpOptions);
}

Categories

Resources