Question regarding Google Apps Script oAuth2 Third Party Authentification Code - javascript

this is my first time posting a question to stack overflow since I have an issue I cant get past.
What I am trying to do:
I am trying to extract data of my car from the "Mercedes me" platform. It's a pure hobby project.
"Mercedes me" has an API with oAuth2 verification.
I am trying to realize this in Google Sheets since I want to do calculations with the data retrieved.
Hence I started coding in Apps Script.
I am able to exchange an Authentification Code with a Token and then get data from the platform using the following code:
var options = {
headers : { Authorization: 'Basic '+ Utilities.base64Encode('Client ID:Client Secret')},
method : 'post',
'content-type' : 'application/x-www-form-urlencoded',
muteHttpExceptions : false,
}
var authorization_code = 'TGVWD9pNif1wuDBYa6fFPn4QHNW9h6_f2-kxL34V'
var authUrl = 'https://id.mercedes-benz.com/as/token.oauth2?grant_type=authorization_code&code='+authorization_code+'&redirect_uri=https://localhost/';
var response = UrlFetchApp.fetch(authUrl, options);
var data = JSON.parse(response);
var token = data.access_token
'Logger.log(token);'
var options = {
headers : { Authorization: `Bearer ${token}`},
accept : 'application/json'
}
var response = UrlFetchApp.fetch('https://api.mercedes-benz.com/vehicledata/v2/vehicles/<vehicle identification number>/resources/', options);
'var jsonObject = JSON.parse(response.getContentText()); '
'var jsonObject = JSON.stringify(response);'
Logger.log(response);
So this is not the issue. The issue is that currently I need to manually insert the Authorization Code (as you can see in the code snippet above) which will then be exchanged with the Token by my script. The goal would be to generate the Authorization Code automatically.
I have created an authorization URL which will give me the Authorization Code when entered and opened in the Browser.
However, when I try to achieve the same in Apps Script via UrlFetchApp it will send me back some unusable html code instead of an Authorization Code.
function Authentification() {
var url = 'https://id.mercedes-benz.com/as/authorization.oauth2?response_type=code&client_id=a9f67f5f-cb2b-413b-9ea5-78df02b668a1&redirect_uri=https://localhost/&scope=mb:vehicle:mbdata:payasyoudrive&state=1000';
var options = {
muteHttpExceptions : false, //theoretisch nicht notwendig
followRedirects : true, //theoretisch nicht notwendig
method : 'get', //theoretisch nicht notwendig
}
var response = UrlFetchApp.fetch(url, options);
var json = response.getHeaders;
var data = JSON.stringify(json);
Logger.log(response)
}
Result is some html like this:
<!DOCTYPE html><html lang=en xmlns=http://www.w3.org/1999/xhtml><head><meta charset=utf-8><m
But what I am trying is to get a redirect Url like this (including the Authorization Code):
https://localhost/?code=DXPzKdV58t3rWlpuVmD3XT35BbL__n0auuAxL34V&state=1000
My assumption is, the problem is that when the link is opened by Apps Script's URLFetchApp it is never asked to enter my "Mercedes me" login credentials. Most likely I am not seing a basic OAuth logic here. Would really appreciate your help. Thanks a lot in advance.
Best Regards,
Chris

The solution came to my mind in my sleep :-D
I deployed the whole thing as a webapp and used the webapp-link as the redirect uri.
After this I was able to extract the authorization code from the url with the following code:
function doGet(e) {
var param = e.queryString;
param = e.parameter;
var code = param.code;

Related

API with Google Script Apps

I'm having trouble using Google Apps to interact with a management software called Kissflow.
function fun2() {
var id = "yyy";
var apisecretkey = "xxx";
var url ='https://'+id+'.kissflow.com/api/1/verify';
var options = {
method: 'post',
headers : {"Authorization" : " Basic " + Utilities.base64Encode(id + ":" + apisecretkey)},
payload: {
"grant_type": "client_credentials",
"scope": "basic+user"
},
muteHttpExceptions: true
};
var response = JSON.parse(UrlFetchApp.fetch(url, options).getContentText());
}
I would like to run this simple example of the API documentation, the goal is for me to be able to send data to the software through my interactions in a spreadsheet, for example. If you can help me in this I will be very grateful, I am new with API's :)
The following error appears: SyntaxError: unexpected token <in JSON at position 0 (line 30, file "Code") I don't know if I'm using this function correctly.
Kissflow API Documentation
JSON.parse(UrlFetchApp.fetch(url, options).getContentText())
Remove JSON.parse() from above line and output the response on your console using Logger.log() or to your browser log using console.log() and see the result. If there are errors it'll show more user friendly error message.
While I'm not familiar with the API, I suspect the issue is related to this line:
var response = JSON.parse(UrlFetchApp.fetch(url, options).getContentText());
You're fetching the response (which is probably JSON), then getting the content text of that response, then trying to parse that text as if it were JSON.
So (solution 1) if you set your response variable equal to
var response = UrlFetchApp.fetch(url, options).getContentText();
and try printing that variable to the console, you may find you have something you can work with.
Alternatively (solution 2), you might try:
var response = JSON.parse(UrlFetchApp.fetch(url, options));
if you'd rather have a JavaScript object to work with instead of a string (and assuming the .fetch method you're calling does indeed provide a JSON-formatted response.)

How to convert a cURL request into a Google Apps Script UrlFetchApp

I'm aware this is probably basic stuff.
I've read a lot of the other questions on here around converting cURL into a Google Apps Script UrlFetchApp request however, I'm unable to get it to work. I'm a marketer, not a developer, so I have limited knowledge of Javascript but I'm usually able to manipulate reference scripts for my own needs. This one just won't work for me.
All I get is "Authentication has failed" as a response.
My current code is:
function wisepopsData() {
var API_KEY = 'APIKEYHERE';
var root = 'https://app.wisepops.com/api1/wisepops';
var params = {
'headers': {
'Authorization': 'WISEPOPS-API ' + Utilities.Base64Encode(API_KEY)
}
};
var response = UrlFetchApp.fetch(root, params);
var data = response.getContentText();
var json = JSON.parse(data);
Logger.log(json);
}
The documentation I'm using is below. I'm trying to implement the performance data part --> https://support.wisepops.com/en/articles/572165-wisepops-api-basics#performance-data-on-your-wisepops
The part I can't get my head around is 'Authorization: WISEPOPS-API key="YOUR_API_KEY_HERE"'. I've tried lots of combinations in the header of the request to get this bit to work and still nothing.
Try changing params variable intialization.
something like :
var params = {
'headers': {
'Authorization': 'WISEPOPS-API key="' + API_KEY + '"'
}};

What headers to include in a UrlFetch / UrlFetchApp request when given a user ID?

I've recently gotten credentials to pull from this API (CareerOneStop.org) and they sent me credentials that include an API token and a user ID. I have to admit that I'm a newbie when it comes to APIs, but I have used the Google Apps Script UrlFetchApp method to successfully make a GET request from an API that only requires a secret token.
For example, this JavaScript successfully calls the Udemy API:
function callUdemyInstructorAPI () {
var baseUrl = 'https://www.udemy.com/instructor-api/v1/';
var url = baseUrl + 'taught-courses/courses/?fields%5Bcourse%5D=#all&ordering=is_published';
var params = {
"method" : "GET",
"headers" : {
'Authorization' : 'bearer authToken',
}
};
var response = UrlFetchApp.fetch(url, params);
var parsedResponse = JSON.parse(response.getContentText());
}
However, the particular CareerOneStop.org API issues users an ID also, and I'm having a hard time figuring out how to write a request function that integrates it.
It looks like someone had a somewhat similar question on Stackoverflow, but they needed to provide a username and password, and I'm not sure that this ID / token authentication combination has the same needs.
Using what I've found in other places, I've built this function, but I get a 401 return:
function apiTest(){
var url = 'https://api.careeronestop.org/v1/occupation/I3ojbDKR1o97Veh/nurse/Y/0/10?datasettype=occ'; // this URL works, and you'll see that the user ID they give is part of the URL after occupation/
var token = "secretToken";
var headers =
{
Authorization : "Bearer " + token
}
var options =
{
"method" : "get",
"headers": headers
};
var result = UrlFetchApp.fetch(url, options);
var state = result.getContentText();
}
If you can give me any tips on formatting this API request, I'd be very grateful. I'n not sure what I need to do here...
Thanks in advance!

Send message to discord via google apps script

I'd like to send a bot message in a discord channel via google apps script when a certain event is triggered, but I don't know where to start. Is this even possible? If not, is there a way to do it via github?
EDIT: I have figured out how to get the OAuth tokens, now how do I make the bot send a message?
I know that there's pretty much no chance that the OP still needs this answer, but I'm putting it up here so that others who google this question will be able to find the answer
var webhooks = {
test: "Obtain a webhook for the channel you'd like and put it here."
};
function sendMessage(message, channel)
{
if(webhooks.hasOwnProperty(channel))
var url = webhooks[channel];
else {
Logger.log("Error Sending Message to Channel " + channel);
return "NoStoredWebhookException";
}
var payload = JSON.stringify({content: message});
var params = {
headers: {"Content-Type": "application/x-www-form-urlencoded"},
method: "POST",
payload: payload,
muteHttpExceptions: true
};
var res = UrlFetchApp.fetch(url, params);
Logger.log(res.getContentText());
}
// to call and send a message
sendMessage("Hi!", "test");
This is what I typically use for sending messages. Unfortunately, there's no way to receive triggers from the webhooks to my knowledge.
Note: The above answer references discord.js, which is not compatible with Google Apps Script
To start with, here is a documentation from discord.js.
To let your apps script communicate with discord, you can check the External APIs
Google Apps Script can interact with APIs from all over the web. This
guide shows how to work with different types of APIs in your scripts.
Examples are available in the provided documentations.
See these useful links for further details.
Website (source)
Documentation
Discord.js server
Discord API server
GitHub
NPM
Related libraries (see also discord-rpc)
Super easy. Just go to your Discord channel, choose "Edit Channel" > "Webhooks". You name the bot and set its profile picture. It will give you a webhook URL that already contains the authorization token.
Then you just POST to that public URL. TADA, a message will appear in the given channel, sent by the bot.
function postMessageToDiscord(message) {
message = message || "Hello World!";
var discordUrl = 'https://discordapp.com/api/webhooks/labnol/123';
var payload = JSON.stringify({content: message});
var params = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
method: "POST",
payload: payload,
muteHttpExceptions: true
};
var response = UrlFetchApp.fetch(discordUrl, params);
Logger.log(response.getContentText());
}
Source: https://ctrlq.org/code/20563-post-message-to-discord-webhooks
For anyone still looking and not having luck with previous answers, like me:
After making my message_string, this snippet successfully sent to my desired channel's webhook:
// Send the message to the Discord channel webhook.
let options = {
"method": "post",
"headers": {
"Content-Type": "application/json",
},
"payload": JSON.stringify({
"content": message_string
})
};
Logger.log(options, null, 2);
UrlFetchApp.fetch("your_webhook_url_here", options);
I can't make a comment yet, but if you are having issues with one of the above answers - try changing the content type from:'Content-Type':"application/x-www-form-urlencoded" to 'Content-Type':"application/json".

Flickr API throws up "Invalid API Key (Key has invalid format)" when POSTing data with oauth signing

I can't seem to find this answered anywhere on SO or even Google - I have an oauth-signed call to the Flickr upload API, and following the docs I've signed the POST operation the usual oauth way (but without the photo data). For testing purposes I've only passed along a title and the photo data, which means I end up a var flickrURI that contains the following url for POSTing to:
https://api.flickr.com/services/upload/
? format=json
& oauth_consumer_key=...
& oauth_nonce=2e57b73fec6630a30588e22383cc3b25
& oauth_signature_method=HMAC-SHA1
& oauth_timestamp=1411933792346
& oauth_token=...
& title=test
& oauth_signature=O7JPn1m06vl5Rl95Z2in32YWp7Q%3D
(split over multiple lines for legibility in this question, the actual URL has no whitespacing around the ? and & for obvious reasons).
The oauth signing itself is quite correct, and code used for accessing the not-upload-API all over the place with correct behaviour, so it seems pretty much impossible for that to get the signing wrong, other than perhaps signing with "not enough data" or perhaps signing with "too much data".
The auth signing first forms the auth query string, in this case:
oauth_consumer_key=...
&oauth_nonce=60028905f65cf9d7649b3bce98f718f8
&oauth_signature_method=HMAC-SHA1
&oauth_timestamp=1411939726691
&oauth_token=...
&title=test
which is then used to form the verb + address + encoded base string:
POST&https%3A%2F%2Fapi.flickr.com%2Fservices%2Fupload%2F&oauth_consumer_key%3D...%26oauth_nonce%3D60028905f65cf9d7649b3bce98f718f8%26oauth_signature_method%3DHMAC
-SHA1%26oauth_timestamp%3D1411939726691%26oauth_token%3D...%26title%3Dtest
This is then HMAC-SHA1 digested using the Flickr and oauth secrets:
function sign = (data, key, secret) {
var hmacKey = key + "&" + (secret ? secret : ''),
hmac = crypto.createHmac("SHA1", hmacKey);
hmac.update(data);
var digest = hmac.digest("base64");
return encodeURIComponent(digest);
}
And for GET requests, this works perfectly fine. For POST requests things seem to be difference, despite the docs not explain which part is supposedly different, so I the tried to use the Nodejs request package to perform the POST action in what seemed a normal way, using:
uploadOptions = {
oauth_consumer_key = auth.api_key,
oauth_nonce = auth.oauth_nonce,
oauth_timestamp = auth.oauth_timestamp,
oauth_token = auth.access_token,
oauth_signature_method = "HMAC-SHA1",
title: title,
photo: <binary data buffer>
};
flickrURL = formSignedURL(auth);
request.post({
url: flickrURI,
headers: {
"Authorization": 'oauth_consumer_key="...",oauth_token="...",oauth_signature_method="HMAC-SHA1",oauth_signature="...",oauth_timestamp="...",oauth_nonce="...",oauth_version="1.0"'
},
multipart: [{
'content-type': 'application/json',
body: JSON.stringify(signOptions)
}]
},function(error, response, body) {
console.log("error");
console.log(error);
console.log("body");
console.log(body);
}
);
which yields a body that contains:
<?xml version="1.0" encoding="utf-8" ?>
<rsp stat="fail">
<err code="100" msg="Invalid API Key (Key has invalid format)" />
</rsp>
As the oauth signing doesn't really give me a choice in which API key to provide (there is only one) and the signing works just fine for the not-upload API, I can't figure out what this error message is trying to tell me. The key is definitely the right format because that's the format Flickr gives you, and it's the correct value, because it works just fine outside of uploading. I also made sure to get the oauth token and secret for that key with "delete" permission (widest possible permissions) so the included access token and access token secret should pass the "does this token for this key have permissions to write" test.
What obvious thing am I missing here that's preventing the upload to go through?
It looks like you're using the https://up.flickr.com/services/upload/ endpoint, which uses the old authentication scheme.
For OAuth, it should be https://api.flickr.com/services/upload/. Make sure the endpoint is included in signing process.
I don't think it's documented anywhere, but I remember having same issue a while back.
It turns out adding the data as request.post multipart information isn't good enough, and will make the Flickr API throw an "Invalid API Key (Key has invalid format)" error instead of saying what's actually wrong. The following request call, with exactly the same data, works:
var uploadOptions = ...
var flickrURL = ...;
var req = request.post(flickrURL, function(error, response, body) {
callback(error, body);
});
var form = req.form();
uploadOptions.photo = fs.createReadStream(...);
Object.keys(photoOptions).forEach(function(prop) {
form.append(prop, photoOptions[prop]);
});
Despite not making all that much sense call wise (why would the POST not already be done by the time we call form = req.form()?) this is request's "proper" way to send the POST payload over the wire, and makes the Flickr API process the photo upload just fine.

Categories