Skip to content

Instantly share code, notes, and snippets.

@karthik2804
Last active August 12, 2021 12:23
Show Gist options
  • Select an option

  • Save karthik2804/1f9adfc358d002ca3c184bd286dd77d2 to your computer and use it in GitHub Desktop.

Select an option

Save karthik2804/1f9adfc358d002ca3c184bd286dd77d2 to your computer and use it in GitHub Desktop.
Get Google Oauth2 Access Token
/*
* Store the private key and email id of the service account as environment variables
* PRIVATE_KEY - Service account private key
* SERVICE_ACCOUNT_EMAIL - email id of the service account
*/
async function getToken(request) {
try {
// Function to encode an object with base64
function objectToBase64url(object) {
return arrayBufferToBase64Url(
new TextEncoder().encode(JSON.stringify(object)),
)
}
// Function to encode array buffer with base64
function arrayBufferToBase64Url(buffer) {
return btoa(String.fromCharCode(...new Uint8Array(buffer)))
.replace(/=/g, '')
.replace(/\+/g, '-')
.replace(/\//g, '_')
}
// Function to convert string to array buffer
function str2ab(str) {
const buf = new ArrayBuffer(str.length);
const bufView = new Uint8Array(buf);
for (let i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
//
// Step 1 - Form the JWT header and encode to base64
const GOOGLE_JWT_HEADER = objectToBase64url({
// RSA SHA-256 algorithm is mandatory for Google
alg: 'RS256',
// JWT token format is mandatory for Google
typ: 'JWT'
})
//
// Step 2 - Form the JWT claim set and encode to base64
// Define the time the assertion was issued
let assertiontime = Math.round(Date.now() / 1000)
// Define the expiration time of the assertion, maximum 1 hour
let expirytime = assertiontime + 3600
// JWT claim payload
const GOOGLE_JWT_CLAIMSET = objectToBase64url({
// Service account email address from https://console.cloud.google.com
// This should be defined as a secret rather than hardcoded!!
'iss': SERVICE_ACCOUNT_EMAIL,
// A listing of available scopes is provided here:
// https://developers.google.com/identity/protocols/oauth2/scopes
'scope': 'https://www.googleapis.com/auth/spreadsheets',
'aud': 'https://oauth2.googleapis.com/token',
'exp': expirytime,
'iat': assertiontime
})
// Combine the JWT header + claim and convert to byte array for signing
const GOOGLE_JWT_COMBINED = str2ab(
"{" +
GOOGLE_JWT_HEADER +
"}.{" +
GOOGLE_JWT_CLAIMSET +
"}"
)
//
// Step 3 - Sign the combined GOOGLE_JWT_HEADER and GOOGLE_JWT_CLAIMSET
const PRIVATE_KEY = PRIVATE_KEY
const private_key_raw = PRIVATE_KEY.replace(/\\n/g, "\n");
// Tidy up the key ahead of importing
var private_key_clean = private_key_raw.replace('-----BEGIN PRIVATE KEY-----', '');
var private_key_clean = private_key_clean.replace('-----END PRIVATE KEY-----', '');
var private_key_clean = private_key_clean.replace(/(\r\n|\n|\r)/gm, "");
// base64 decode the string to get the binary data
var private_key_binary = atob(private_key_clean);
// convert from a binary string to an ArrayBuffer
var private_key_binary = str2ab(private_key_binary);
// Import the private key into the crypto store
const SIGNING_KEY = await crypto.subtle.importKey(
"pkcs8",
private_key_binary,
{
name: "RSASSA-PKCS1-V1_5",
hash: { name: "SHA-256" }
},
false,
["sign"]
);
// Sign the GOOGLE_JWT_HEADER and GOOGLE_JWT_CLAIMSET
const rawToken = await crypto.subtle.sign(
{ name: 'RSASSA-PKCS1-V1_5' },
SIGNING_KEY,
GOOGLE_JWT_COMBINED
);
// Convert the signature to Base64URL format
const GOOGLE_JWT_SIGNED = arrayBufferToBase64Url(rawToken);
// Combine the headers with signature
var combined_headers_signed =
"{" +
GOOGLE_JWT_HEADER +
"}.{" +
GOOGLE_JWT_CLAIMSET +
"}.{"
+ GOOGLE_JWT_SIGNED +
"}";
//
// Step 4 - Send the request
// Create payload
const GOOGLE_JWT_PAYLOAD =
"grant_type=" +
"urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=" +
combined_headers_signed;
// Make the OAUTH request
const response = await fetch('https://oauth2.googleapis.com/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Cache-Control': 'no-cache',
'Host': 'oauth2.googleapis.com'
},
body: GOOGLE_JWT_PAYLOAD
});
const oauth = await response.json();
return oauth.access_token;
} catch (err) {
console.log(err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment