Bad Request when trying to get an access token - javascript

I'm trying to use the Nest Device API, however I am unable to get an Access Token as it throws
{
"error": "invalid_grant",
"error_description": "Bad Request"
}
I've downloaded my credentials.json from GCP and I open a new tab with the AUTH_URL below:
const credentials = require('../../../credentials.json');
const PROJECT_ID = <NEST DEVICE API PROJECT ID>;
const REDIRECT_URL = 'http://localhost:3000/connect/callback';
const AUTH_URL =
`https://nestservices.google.com/partnerconnections/${PROJECT_ID}/auth?` +
`redirect_uri=${REDIRECT_URL}&access_type=offline&prompt=consent&client_id=${credentials.web.client_id}&` +
`response_type=code&scope=https://www.googleapis.com/auth/sdm.service`;
From that, I have my callback page that gets the authcode.
const credentials = require('../../../credentials.json');
const { code } = router.query; // Auth Code
try {
const url =
`https://www.googleapis.com/oauth2/v4/token?client_id=${credentials.web.client_id}` +
`&client_secret=${credentials.web.client_secret}&code=${code}&grant_type=authorization_code&` +
`redirect_uri=${REDIRECT_URL}`;
const response = await fetch(url, {
method: 'POST', // *GET, POST, PUT, DELETE, etc.
});
console.log(response);
} catch (e) {
console.error(e);
}
This is where the API returns the above error. I have also tried taking this URL and doing curl -L -X POST <url> however I get exactly the same results.
Any ideas?

Related

send Oauth 2.0 authentication request with Google Apps Script

I'm using apps script to create an interaction between a spreadsheet and a website using its API.
I must first authenticate with Oauth 2.0, here is the documentation:
Authentication - OAuth 2.0
Authentication is required before any other API call.
POST
/oauth/token
Body :
grant_type=client_credentials
Header :
Champ
Type
Description
Authorization
String
Autorization method "Basic" followed by your public_key and private_key in your settings > developer > API combined into a string "public_key:private_key" and encoded using Base64
Content-Type
String
Must be : application/x-www-form-urlencoded
Header (example) :
```
Authorization: Basic dGVzdGNsaWVudDp0ZXN0cGFzcw==
Content-Type: application/x-www-form-urlencoded
```
I'm completely new to API requests, and I don't understand how to format the request, I found this post:
Send POST request in Google Apps Script with Headers and Body
And as I understand, application/x-www-form-urlencoded is by default with UrlFetchApp, so I tried:
function authentication() {
const ENDPOINT = 'api url'
const CLIENT_ID = 'public key'
const CLIENT_SECRET = 'secret key'
const TOKEN_URL = ENDPOINT + '/oauth/token'
const HEADERS = {
'Authorization' : 'Basic ' + CLIENT_ID + ':' + CLIENT_SECRET
}
const BODY = 'grant_type=client_credentials'
const OPTIONS = {
'method' : 'post',
'headers' : HEADERS
}
let response = UrlFetchApp.fetch(TOKEN_URL + "?" + BODY,OPTIONS)
Logger.log(response.getContentText());
}
But I get a 404 error and know an unknown error.
I guess I'm doing something wrong at least with the body but I don't understand how to format properly the request.
Can someone help me?
Thanks
I would suggest that you refer to this documentation, Apps script has a library that allows you to use Oauth2 you can find it here.
Here is an example:
function accessProtectedResource(url, method_opt, headers_opt) {
var service = getOAuthService();
var maybeAuthorized = service.hasAccess();
if (maybeAuthorized) {
var accessToken = service.getAccessToken();
var method = method_opt || 'get';
var headers = headers_opt || {};
headers['Authorization'] =
Utilities.formatString('Bearer %s', accessToken);
var resp = UrlFetchApp.fetch(url, {
'headers': headers,
'method' : method,
'muteHttpExceptions': true, // Prevents thrown HTTP exceptions.
});
var code = resp.getResponseCode();
if (code >= 200 && code < 300) {
return resp.getContentText("utf-8"); // Success
} else if (code == 401 || code == 403) {
// Not fully authorized for this action.
maybeAuthorized = false;
} else {
// Handle other response codes by logging them and throwing an
// exception.
console.error("Backend server error (%s): %s", code.toString(),
resp.getContentText("utf-8"));
throw ("Backend server error: " + code);
}
}
if (!maybeAuthorized) {
// Invoke the authorization flow using the default authorization
// prompt card.
CardService.newAuthorizationException()
.setAuthorizationUrl(service.getAuthorizationUrl())
.setResourceDisplayName("Display name to show to the user")
.throwException();
}
}
function getOAuthService() {
return OAuth2.createService('SERVICE_NAME')
.setAuthorizationBaseUrl('SERVICE_AUTH_URL')
.setTokenUrl('SERVICE_AUTH_TOKEN_URL')
.setClientId('CLIENT_ID')
.setClientSecret('CLIENT_SECRET')
.setScope('SERVICE_SCOPE_REQUESTS')
.setCallbackFunction('authCallback')
.setCache(CacheService.getUserCache())
.setPropertyStore(PropertiesService.getUserProperties());
}
function authCallback(callbackRequest) {
var authorized = getOAuthService().handleCallback(callbackRequest);
if (authorized) {
return HtmlService.createHtmlOutput(
'Success! <script>setTimeout(function() { top.window.close() }, 1);</script>');
} else {
return HtmlService.createHtmlOutput('Denied');
}
}
/**
* Unauthorizes the non-Google service. This is useful for OAuth
* development/testing. Run this method (Run > resetOAuth in the script
* editor) to reset OAuth to re-prompt the user for OAuth.
*/
function resetOAuth() {
getOAuthService().reset();
}

Different headers used in Axios patch

I spent an hour looking in the Chrome console and I cannot see where this bug comes from.
I am finishing an update of OAuth implementation in my Vue app.
The story begins when socialLink.js finds out that a new user must be created. Vue component Vue-authentication depends on the presence of access_token in a response so I return some dummy text:
return api.sendResponse(res, { email, name, socialId, access_token: 'abcd' });
The library stores this value in localStorage:
After a redirect, the SignUp.vue is rendered and I complete the form. The first communication with the server is a Vuex call to create a new user:
response = await this.$store.dispatch('CREATE_USER_PROFILE', payload);
Which returns a real short lived JWT token:
const token = auth.createToken(userId, nickname, new Date(), null, false, '1m');
return api.sendCreated(res, api.createResponse(token));
Which I store in the Vue page afterwards:
const { data } = response;
const token = data.data;
if (token === undefined) {
this.error = this.$t('sign-up.something-went-wrong');
return false;
}
I checked that the token contains what the server returned:
Request URL: https://beta.mezinamiridici.cz/api/v1/users
Request Method: POST
Status Code: 201 Created
{"success":true,"data":"eyJhbGciOiJIUzI1NiIs...Tl8JFw2HZ3VMXJk"}
Then I call another Vuex method and pass the current JWT token:
await this.$store.dispatch('UPDATE_USER_PROFILE', {
I checked in the Vuex devtools that there really is the correct JWT token. I then pass it further to api.js.
Here I create an Axios configuration holding an Authorization header:
function getAuthHeader(context, jwt = undefined, upload) {
const config = { headers: { } };
if (jwt || (context && context.rootState.users.userToken)) {
config.headers.Authorization = `bearer ${jwt || context.rootState.users.userToken}`;
}
Again, I checked that the correct JWT token is used there.
Finally, I pass all data to Axios:
function patch(endpoint, url, body, context, jwt) {
const headers = getAuthHeader(context, jwt);
console.log(headers);
if (endpoint === 'BFF') {
return axios.patch(`${VUE_APP_BFF_ENDPOINT}${url}`, body, headers);
} else {
return axios.patch(`${VUE_APP_API_ENDPOINT}${url}`, body, headers);
}
}
Which I log and can confirm the correct JWT is still there:
bearer eyJhbGciOiJIUzI1N....8JFw2HZ3VMXJk
There is nothing that could change the header now to abcd, but, the 'Network' tab shows it:
And the server fails with a parse error.
Has anybody got an idea why Axios uses the Authorization header with a different value than I pass it?
Ok, mystery solved. vue-authenticate is the reason, because, it creates Axios interceptors and handles the Authorization header itself.
vue-authenticate.common.js:
var defaultOptions = {
bindRequestInterceptor: function ($auth) {
var tokenHeader = $auth.options.tokenHeader;
$auth.$http.interceptors.request.use(function (config) {
if ($auth.isAuthenticated()) {
config.headers[tokenHeader] = [
$auth.options.tokenType, $auth.getToken()
].join(' ');
} else {
delete config.headers[tokenHeader];
}
return config
});
},
My code is more complex and it supports internal accounts with email/password so this code is breaking mine. The interceptor must be present and be a function, so the solution was:
Vue.use(VueAuthenticate, {
tokenName: 'jwt',
baseUrl: process.env.VUE_APP_API_ENDPOINT,
storageType: 'localStorage',
bindRequestInterceptor() {},
bindResponseInterceptor() {},
providers: {
facebook: {
clientId: process.env.VUE_APP_FACEBOOK_CLIENT_ID,
redirectUri: process.env.VUE_APP_FACEBOOK_REDIRECT_URI,
},

Spotify API getting the error "No token provided" when fetching an endpoint

I am generating an access token every time the user tries to search a song with the spotify API beceuse I have seen that those token has an expiration time of 1h...
With the new access token I call the search endpoint but I get this response:
Object {
"error": Object {
"message": "No token provided",
"status": 401,
},
}
I don't have any idea why is that possible if I am passing the token to my GET request.
Here is the code:
export const searchMusic = async (query) => {
// Get an access token
const accessToken = await generateAccessToken();
console.log(accessToken); // <---- This shows the access token "BQAM...YualK"
// Request parameters
const params = {
q: query,
type: "track,album",
};
// Request url
const url = `https://api.spotify.com/v1/search${qs.stringify(params, {
addQueryPrefix: true,
})}`;
const response = await fetch(url, {
method: "GET",
header: {
Authorization: "Bearer " + accessToken, // <------- I pass the token as you can see here
},
});
const data = await response.json();
console.log(data); <----- Here comes the error
};
Does anybody knows what am I doing wrong? Also, is it bad to generate a new token everytime I try to fetch an endpoint? I mean, should I refresh it instead?
Thank you, I will really appreciate any guides.
For me, the problem was that I was using the api in the client, and my requirements needed to be run on the server... So, I moved my code to the server and all worked fine!
Maybe your API key isn't valid or doesn't give access to query that...
Code reference : https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401

How to make a fetch request with custom herokuapp proxy?

I'm trying to make a fetch request with custom herokuapp proxy to an API, but when I do that it gives an error. Error says "There is no Target-Endpoint header in the request". Here is my code.
var userTargetUrl = `http://api.steampowered.com/ISteamUser/ResolveVanityURL/v0001/?key=${steamApiKey}&vanityurl=${url}`
const response = await fetch(proxyUrl + userTargetUrl, {
headers: {
'Target-Endpoint': 'http://api.steampowered.com/ISteamUser/ResolveVanityURL/v0001/?'
}
})
const data = await response.json()
url = data['response']['steamid']
I'm following their instructions, but I couldn't figure it how to do it.
I won't pretend to be entirely sure, but maybe you can try
const response = await fetch(proxyUrl, {
headers: {
"Target-Endpoint": userTargetUrl
}
});

coinmarketcap api integration - 401 error - JavaScript

I am trying to integrate coinmarketcap api but cannot really get the data. I registered, got the API key, and wrote the following method to fetch the data:
let getPostsList = async () => {
const options = {
method: 'GET',
headers: {
'X-CMC_PRO_API_KEY': 'api-key-goes-here'
},
mode: 'no-cors'
};
try {
const response = await fetch(`https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest`, options);
const json = await response.body;
// console.log(json)
return json
} catch (err) {
console.log('Error: ', err)
}
};
All I get is 401 error, like this:
GET https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest
401
Any suggestions what I should fix? Docs says that 401 is likely connected to API key, but they say to provide it in the headers like the above...
From what I've tested after getting my own API key, the no-cors mode is problematic. You will need to use CORS, where https://cors-anywhere.herokuapp.com/ comes into handy.
Just send the request like this :
const options = {
method: 'GET',
headers: {
'X-CMC_PRO_API_KEY': 'api-key-goes-here'
},
};
try {
const response = await fetch(`https://cors-anywhere.herokuapp.com/https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest`, options);

Categories

Resources