Skip to content

Instantly share code, notes, and snippets.

@ffeldhaus
Forked from dinvlad/pre_request.js
Last active July 14, 2024 00:22
Show Gist options
  • Save ffeldhaus/7753b24cf3631a9ddc1127e6fd835767 to your computer and use it in GitHub Desktop.
Save ffeldhaus/7753b24cf3631a9ddc1127e6fd835767 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 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';
// add/remove your own scopes as needed
const SCOPES = [
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile',
'https://www.googleapis.com/auth/cloud-platform',
];
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, token_uri }, iat, callback) => {
getJWS((err, JWS) => {
if (err) return callback(err);
const header = {
typ: 'JWT',
alg: 'RS256',
};
const exp = iat + 3600;
const payload = {
aud: token_uri,
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: serviceAccountKey.token_uri,
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;
});
});
@ffeldhaus
Copy link
Author

Updated the original gist with the following changes to make it work for Google Cloud Platform:

  • Removed GOOGLE_OAUTH constant and replaced it with the dynamic serviceAccount.token_uri. This is necessary as Google Cloud Platform uses a different token URI than consumer Google APIs. This approach may work for all Google APIs if the Service Account JSON always contains token_uri
  • Added https://www.googleapis.com/auth/cloud-platform to the list of scopes to allow access to Google Cloud Platform services. It may be possible to limit the scope further for some specific Google Cloud Services, but usually the scope should be limited via IAM permissions instead of scopes in the case of Google Cloud Platform (see https://cloud.google.com/iam/docs/service-accounts#access_scopes)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment