Last active
November 27, 2024 15:27
-
-
Save dinvlad/425a072c8d23c1895e9d345b67909af0 to your computer and use it in GitHub Desktop.
Auto-generate Google Access and ID tokens from a Service Account key and save it in Postman
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* This script auto-generates a Google OAuth token from a Service Account key, | |
* and stores that token in accessToken variable in Postman. | |
* | |
* Prior to invoking it, please paste the contents of the key JSON | |
* into serviceAccountKey variable in a Postman environment. | |
* | |
* Then, paste the script into the "Pre-request Script" section | |
* of a Postman request or collection. | |
* | |
* The script will cache and reuse the token until it's within | |
* a margin of expiration defined in EXPIRES_MARGIN. | |
* | |
* Thanks to: | |
* https://paw.cloud/docs/examples/google-service-apis | |
* https://developers.google.com/identity/protocols/OAuth2ServiceAccount#authorizingrequests | |
* https://gist.github.com/madebysid/b57985b0649d3407a7aa9de1bd327990 | |
* https://github.com/postmanlabs/postman-app-support/issues/1607#issuecomment-401611119 | |
*/ | |
const ENV_SERVICE_ACCOUNT_KEY = 'serviceAccountKey'; | |
const ENV_JS_RSA_SIGN = 'jsrsasign'; | |
const ENV_TOKEN_EXPIRES_AT = 'tokenExpiresAt'; | |
const ENV_ACCESS_TOKEN = 'accessToken'; | |
const JS_RSA_SIGN_SRC = 'https://kjur.github.io/jsrsasign/jsrsasign-latest-all-min.js'; | |
const GOOGLE_OAUTH = 'https://www.googleapis.com/oauth2/v4/token'; | |
// add/remove your own scopes as needed | |
const SCOPES = [ | |
'https://www.googleapis.com/auth/userinfo.email', | |
'https://www.googleapis.com/auth/userinfo.profile', | |
]; | |
const EXPIRES_MARGIN = 300; // seconds before expiration | |
const getEnv = name => | |
pm.environment.get(name); | |
const setEnv = (name, value) => | |
pm.environment.set(name, value); | |
const getJWS = callback => { | |
// workaround for compatibility with jsrsasign | |
const navigator = {}; | |
const window = {}; | |
let jsrsasign = getEnv(ENV_JS_RSA_SIGN); | |
if (jsrsasign) { | |
eval(jsrsasign); | |
return callback(null, KJUR.jws.JWS); | |
} | |
pm.sendRequest(JS_RSA_SIGN_SRC, (err, res) => { | |
if (err) return callback(err); | |
jsrsasign = res.text(); | |
setEnv(ENV_JS_RSA_SIGN, jsrsasign); | |
eval(jsrsasign); | |
callback(null, KJUR.jws.JWS); | |
}); | |
}; | |
const getJwt = ({ client_email, private_key }, iat, callback) => { | |
getJWS((err, JWS) => { | |
if (err) return callback(err); | |
const header = { | |
typ: 'JWT', | |
alg: 'RS256', | |
}; | |
const exp = iat + 3600; | |
const payload = { | |
aud: GOOGLE_OAUTH, | |
iss: client_email, | |
scope: SCOPES.join(' '), | |
iat, | |
exp, | |
}; | |
const jwt = JWS.sign(null, header, payload, private_key); | |
callback(null, jwt, exp); | |
}); | |
}; | |
const getToken = (serviceAccountKey, callback) => { | |
const now = Math.floor(Date.now() / 1000); | |
if (now + EXPIRES_MARGIN < getEnv(ENV_TOKEN_EXPIRES_AT)) { | |
return callback(); | |
} | |
getJwt(serviceAccountKey, now, (err, jwt, exp) => { | |
if (err) return callback(err); | |
const req = { | |
url: GOOGLE_OAUTH, | |
method: 'POST', | |
header: { | |
'Content-Type': 'application/x-www-form-urlencoded', | |
}, | |
body: { | |
mode: 'urlencoded', | |
urlencoded: [{ | |
key: 'grant_type', | |
value: 'urn:ietf:params:oauth:grant-type:jwt-bearer', | |
},{ | |
key: 'assertion', | |
value: jwt, | |
}], | |
}, | |
}; | |
pm.sendRequest(req, (err, res) => { | |
if (err) return callback(err); | |
const accessToken = res.json().access_token; | |
setEnv(ENV_ACCESS_TOKEN, accessToken); | |
setEnv(ENV_TOKEN_EXPIRES_AT, exp); | |
callback(); | |
}); | |
}); | |
}; | |
const getServiceAccountKey = callback => { | |
try { | |
const keyMaterial = getEnv(ENV_SERVICE_ACCOUNT_KEY); | |
const serviceAccountKey = JSON.parse(keyMaterial); | |
callback(null, serviceAccountKey); | |
} catch (err) { | |
callback(err); | |
} | |
}; | |
getServiceAccountKey((err, serviceAccountKey) => { | |
if (err) throw err; | |
getToken(serviceAccountKey, err => { | |
if (err) throw err; | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks Denis (and everyone else as well ) for being so responsive to everyone's queries.
@johnchandlerbaldwin , just curious to see what did you actually mean when you said split the pm in 2 individual requests. Can you send an excerpt of those 2 splits ? I have a feeling i might be eventually going there