Save the date! Join us on October 16 for our Product Ops launch event. Register here.
May 24, 2023 05:21 AM - edited May 24, 2023 06:57 AM
I can't seem to get a valid access token in Airtable based on JavaScript code that works in a Google Apps Script. Here is the working code:
var clientId = '...';
var clientSecret = '...';
var url = 'https://accounts.spotify.com/api/token';
var headers = {
'Authorization': 'Basic ' + Utilities.base64Encode(clientId + ':' + clientSecret)
};
var payload = {
'grant_type': 'client_credentials'
};
var options = {
'method': 'post',
'headers': headers,
'payload': payload
};
var response = UrlFetchApp.fetch(url, options);
var accessToken = JSON.parse(response.getContentText()).access_token;
This code relies on objects and methods that don't exist in Airtable's automation coding environment, namely Utilities.base64encode() and UrlFetchApp.fetch(), so I have tried rewriting the script to work in Airtable. To bypass an encoding method, I simply encoded the clientId and clientSecret manually and substituted the encoded string for Utilities.base64encode(clientId + ':' + clientSecret). In order to fetch the access token, I have tried a few lines of declaring variables. This is what the Airtable script looks like:
var url = 'https://accounts.spotify.com/api/token';
var headers = {
'Authorization': 'Basic ' + '...'}; // '...' is the encoded string
var payload = {
'grant_type': 'client_credentials'};
var options = {
'method': 'post',
'headers': headers,
'payload': payload};
var responseToken = await fetch(url, options);
var dataToken = await responseToken;
var accessToken = await responseToken.access_token;
Have I approached this incorrectly? I appreciate any help.
Solved! Go to Solution.
May 25, 2023 09:14 AM - edited May 25, 2023 09:23 AM
Hello @DPG - i just created a Spotify endpoint and tested it with the following code, which successfully returns a token. And it uses the btoa function that I referenced earlier, included below. So this should give you everything you need.
async function getSpotifyToken() { const client_id = 'xxx' const client_secret = 'xxx'
const url ='https://accounts.spotify.com/api/token' const authOptions = { method: 'POST', headers: { 'Authorization': 'Basic ' + btoa(client_id + ':' + client_secret), 'Content-Type': 'application/x-www-form-urlencoded' }, body: 'grant_type=client_credentials' }; try { const response = await fetch(url, authOptions); if (response.ok) { const data = await response.json() const token = data.access_token console.log(token) return token } else { throw new Error('Request failed with status: ' + response.status) } } catch (error) { console.error('Error:', error) } } function btoa(token) { var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(e){var t="";var n,r,i,s,o,u,a;var f=0;e=Base64._utf8_encode(e);while(f<e.length){n=e.charCodeAt(f++);r=e.charCodeAt(f++);i=e.charCodeAt(f++);s=n>>2;o=(n&3)<<4|r>>4;u=(r&15)<<2|i>>6;a=i&63;if(isNaN(r)){u=a=64}else if(isNaN(i)){a=64}t=t+this._keyStr.charAt(s)+this._keyStr.charAt(o)+this._keyStr.charAt(u)+this._keyStr.charAt(a)}return t},decode:function(e){var t="";var n,r,i;var s,o,u,a;var f=0;e=e.replace(/[^A-Za-z0-9\+\/\=]/g,"");while(f<e.length){s=this._keyStr.indexOf(e.charAt(f++));o=this._keyStr.indexOf(e.charAt(f++));u=this._keyStr.indexOf(e.charAt(f++));a=this._keyStr.indexOf(e.charAt(f++));n=s<<2|o>>4;r=(o&15)<<4|u>>2;i=(u&3)<<6|a;t=t+String.fromCharCode(n);if(u!=64){t=t+String.fromCharCode(r)}if(a!=64){t=t+String.fromCharCode(i)}}t=Base64._utf8_decode(t);return t},_utf8_encode:function(e){e=e.replace(/\r\n/g,"\n");var t="";for(var n=0;n<e.length;n++){var r=e.charCodeAt(n);if(r<128){t+=String.fromCharCode(r)}else if(r>127&&r<2048){t+=String.fromCharCode(r>>6|192);t+=String.fromCharCode(r&63|128)}else{t+=String.fromCharCode(r>>12|224);t+=String.fromCharCode(r>>6&63|128);t+=String.fromCharCode(r&63|128)}}return t},_utf8_decode:function(e){var t="";var n=0;var r=c1=c2=0;while(n<e.length){r=e.charCodeAt(n);if(r<128){t+=String.fromCharCode(r);n++}else if(r>191&&r<224){c2=e.charCodeAt(n+1);t+=String.fromCharCode((r&31)<<6|c2&63);n+=2}else{c2=e.charCodeAt(n+1);c3=e.charCodeAt(n+2);t+=String.fromCharCode((r&15)<<12|(c2&63)<<6|c3&63);n+=3}}return t}} return Base64.encode(token) }
May 24, 2023 07:57 AM
What error messages are you getting? Are you using scripting extension or an automation script? If you are using Scripting extension, have you tried remoteFetchAsync()? Airtable fetch also has a few other limitations listed here.
May 24, 2023 08:50 AM
Thanks for your quick response. Not so much an error, but when including console.log(accessToken) after the entire script, it returns "undefined". It evidently isn't properly fetching the access token. I am implementing this code in an automation script.
May 24, 2023 06:16 PM
What are you seeing in the responseToken?
Also, is seems like you are missing a json method to extract data from the fetch response. I would have expected something more along these lines:
const response = await fetch(url, options) const data = await response.json() const accessToken = data.access_token
Lastly, fwiw, I wrote about how to use Base64-encoding in an Airtable automation script in this thread: https://community.airtable.com/t5/development-apis/base64-conversion/td-p/103578. Scroll down close to the end of the thread you'll see an implementation for btoa that is quite helpful for accessing various external services from within automation scripts.
May 25, 2023 01:36 AM
I appreciate your help.
In the script I have, console.log(responseToken) gives me the following:
{type: "basic", url: "https://accounts.spotify.com/api/token", status: 400, statusText: "Bad Request", ok: false…}
type: "basic"
url: "https://accounts.spotify.com/api/token"
status: 400
statusText: "Bad Request"
ok: false
headers: Object
redirected: false
Clearly I’ve improperly formatted the request, but I can’t pinpoint my mistake.
When I try adding .json() to responseToken, I get the following error: "SyntaxError: Unexpected token < in JSON at position 0 at main on line 18". Line 18 is var dataToken = await responseToken.json(). Is the request in the wrong format?
I suspect there's yet another revision I need to make for this script to work. "var accessToken = await responseToken.access_token;" returns an error when I add the .json() method to responseToken: "Property 'access_token' does not exist on type 'Response'.(2339)". Will I need to convert responseToken into a format that isn't a response?
In the meantime I’ll try implementing your getBambookToken function and see if it resolves anything.
Thanks!
May 25, 2023 08:34 AM
Send me the full fetch with options please. Taking a quick peek at Spotify's API docs and it looks like there is a form component in there. Which means the content-type must be set to x-www-form-urlencoded. In the request, that would look something like this:
headers: { 'Authorization': 'Basic ' + btoa(client_id + ':' client_secret) 'Content-Type': 'application/x-www-form-urlencoded' }
May 25, 2023 09:14 AM - edited May 25, 2023 09:23 AM
Hello @DPG - i just created a Spotify endpoint and tested it with the following code, which successfully returns a token. And it uses the btoa function that I referenced earlier, included below. So this should give you everything you need.
async function getSpotifyToken() { const client_id = 'xxx' const client_secret = 'xxx'
const url ='https://accounts.spotify.com/api/token' const authOptions = { method: 'POST', headers: { 'Authorization': 'Basic ' + btoa(client_id + ':' + client_secret), 'Content-Type': 'application/x-www-form-urlencoded' }, body: 'grant_type=client_credentials' }; try { const response = await fetch(url, authOptions); if (response.ok) { const data = await response.json() const token = data.access_token console.log(token) return token } else { throw new Error('Request failed with status: ' + response.status) } } catch (error) { console.error('Error:', error) } } function btoa(token) { var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(e){var t="";var n,r,i,s,o,u,a;var f=0;e=Base64._utf8_encode(e);while(f<e.length){n=e.charCodeAt(f++);r=e.charCodeAt(f++);i=e.charCodeAt(f++);s=n>>2;o=(n&3)<<4|r>>4;u=(r&15)<<2|i>>6;a=i&63;if(isNaN(r)){u=a=64}else if(isNaN(i)){a=64}t=t+this._keyStr.charAt(s)+this._keyStr.charAt(o)+this._keyStr.charAt(u)+this._keyStr.charAt(a)}return t},decode:function(e){var t="";var n,r,i;var s,o,u,a;var f=0;e=e.replace(/[^A-Za-z0-9\+\/\=]/g,"");while(f<e.length){s=this._keyStr.indexOf(e.charAt(f++));o=this._keyStr.indexOf(e.charAt(f++));u=this._keyStr.indexOf(e.charAt(f++));a=this._keyStr.indexOf(e.charAt(f++));n=s<<2|o>>4;r=(o&15)<<4|u>>2;i=(u&3)<<6|a;t=t+String.fromCharCode(n);if(u!=64){t=t+String.fromCharCode(r)}if(a!=64){t=t+String.fromCharCode(i)}}t=Base64._utf8_decode(t);return t},_utf8_encode:function(e){e=e.replace(/\r\n/g,"\n");var t="";for(var n=0;n<e.length;n++){var r=e.charCodeAt(n);if(r<128){t+=String.fromCharCode(r)}else if(r>127&&r<2048){t+=String.fromCharCode(r>>6|192);t+=String.fromCharCode(r&63|128)}else{t+=String.fromCharCode(r>>12|224);t+=String.fromCharCode(r>>6&63|128);t+=String.fromCharCode(r&63|128)}}return t},_utf8_decode:function(e){var t="";var n=0;var r=c1=c2=0;while(n<e.length){r=e.charCodeAt(n);if(r<128){t+=String.fromCharCode(r);n++}else if(r>191&&r<224){c2=e.charCodeAt(n+1);t+=String.fromCharCode((r&31)<<6|c2&63);n+=2}else{c2=e.charCodeAt(n+1);c3=e.charCodeAt(n+2);t+=String.fromCharCode((r&15)<<12|(c2&63)<<6|c3&63);n+=3}}return t}} return Base64.encode(token) }
May 25, 2023 01:32 PM
You're an invaluable resource for me. Thank you. Here's my revised script, which I still can't seem to get working:
let token
async function getSpotifyToken() {
const client_id = '...';
const client_secret = '...';
const url ='https://accounts.spotify.com/api/token'
const authOptions = {
method: 'POST',
headers: {
'Authorization': 'Basic ' + btoa(client_id + ':' + client_secret),
'Content-Type': 'application/x-www-form-urlencoded'
},
body: 'grant_type=client_credentials'
};
try {
const response = await fetch(url, authOptions);
if (response.ok) {
const data = await response.json()
const token = data.access_token
console.log(token)
return token
} else {
throw new Error('Request failed with status: ' + response.status)
}
} catch (error) {
console.error('Error:', error)
}
}
function btoa(token) {
var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(e){var t="";var n,r,i,s,o,u,a;var f=0;e=Base64._utf8_encode(e);while(f<e.length){n=e.charCodeAt(f++);r=e.charCodeAt(f++);i=e.charCodeAt(f++);s=n>>2;o=(n&3)<<4|r>>4;u=(r&15)<<2|i>>6;a=i&63;if(isNaN(r)){u=a=64}else if(isNaN(i)){a=64}t=t+this._keyStr.charAt(s)+this._keyStr.charAt(o)+this._keyStr.charAt(u)+this._keyStr.charAt(a)}return t},decode:function(e){var t="";var n,r,i;var s,o,u,a;var f=0;e=e.replace(/[^A-Za-z0-9\+\/\=]/g,"");while(f<e.length){s=this._keyStr.indexOf(e.charAt(f++));o=this._keyStr.indexOf(e.charAt(f++));u=this._keyStr.indexOf(e.charAt(f++));a=this._keyStr.indexOf(e.charAt(f++));n=s<<2|o>>4;r=(o&15)<<4|u>>2;i=(u&3)<<6|a;t=t+String.fromCharCode(n);if(u!=64){t=t+String.fromCharCode(r)}if(a!=64){t=t+String.fromCharCode(i)}}t=Base64._utf8_decode(t);return t},_utf8_encode:function(e){e=e.replace(/\r\n/g,"\n");var t="";for(var n=0;n<e.length;n++){var r=e.charCodeAt(n);if(r<128){t+=String.fromCharCode(r)}else if(r>127&&r<2048){t+=String.fromCharCode(r>>6|192);t+=String.fromCharCode(r&63|128)}else{t+=String.fromCharCode(r>>12|224);t+=String.fromCharCode(r>>6&63|128);t+=String.fromCharCode(r&63|128)}}return t},_utf8_decode:function(e){var t="";var n=0;var r=c1=c2=0;while(n<e.length){r=e.charCodeAt(n);if(r<128){t+=String.fromCharCode(r);n++}else if(r>191&&r<224){c2=e.charCodeAt(n+1);t+=String.fromCharCode((r&31)<<6|c2&63);n+=2}else{c2=e.charCodeAt(n+1);c3=e.charCodeAt(n+2);t+=String.fromCharCode((r&15)<<12|(c2&63)<<6|c3&63);n+=3}}return t}}
return Base64.encode(token)
}
getSpotifyToken()
console.log(token)
I added "let token" at the top because console.log(token) returned the following error without it: "ReferenceError: token is not defined at main"
What am I doing wrong? Any ideas?
May 25, 2023 01:52 PM
Very close. Get rid of the "let token" at the top. and where you have "getSpotifyToken()" replace it with this:
const token = await getSpotifyToken()
Since the function is an async function, you need to include the "await" to wait for it to finish. then, after the line above, you can console.log(token) and you'll see the value.
May 29, 2023 12:05 PM
Hi , i am getting same error this code was running perfectly in Google App script but giving error at "Utilities"
async function uploadMediaAndAssociateWithPost(free) {
var wordpressUrl = "";
var wordpressUsername = "";
var wordpressPassword = "";
var imageUrl = free; // Replace with the actual image URL
var associatedPostId = 123; // Replace with the actual post ID
var authHeader = "Basic " + Utilities.base64Encode(wordpressUsername + ":" + wordpressPassword);
// Download the image file
var imageBlob = UrlFetchApp.fetch(imageUrl).getBlob();
// Upload the media file
var uploadUrl = wordpressUrl + "/media";
var uploadOptions = {
method: "post",
headers: {
"Authorization": authHeader
},
payload: {
file: imageBlob
},
muteHttpExceptions: true
};
var uploadResponse = await fetch(uploadUrl, uploadOptions);
var data = await uploadResponse.text();
var uploadData = JSON.parse(data);
console.log(uploadData);
var newImageId = uploadData.id;
console.log(newImageId);
return newImageId;
}
Kindly suggest me Solutions