Environment specific OAuth2 Authorization in Collections

My question: I’ve created a Collection and setup the Authorization to use OAuth2 - however this collection has requests that are designed to be used with a number of different environments, each environment with its own client_id and client_secret.

While I can parameterise the client_id and client_secret in the “Configure New Token” section, the access token has to be a specific value from the list of known tokens, so swapping between environments ends up using the same token (and doesn’t request a new one as the existing one from the original environment is still valid).

Is there anyway to parameterise the tokens themselves? Ultimately I’m looking to be able to run a request (which will automatically retrieve a token), change the environment, re-run the request (which will automatically retrieve another token for that environment) and have both requests execute with their appropriate tokens.

I recently put this pre-request script together to help me with my oauth requests in different environments.

You can set the Environment variables for each environment and it will appropriately use that oauth client/secret/token.

// OAUTH 2.0 Token Management Script 

/* 
 Checks for existing tokens and expiration, and will automatically update tokens if needed.

 Can be copy-pasted to other collections. This allows for using an Environment with different 
 OAuth client/secret and tokens for separate sources.

  Required variables for each collection using OAuth:
    Collection:
        "source": snake_case source name
    Environment:
        "{source}_client_id"
        "{source}_client_secret"
        "{source}_auth_url"
        "{source}_auth_scope"

 Returned variables if successful:
    Environment:
        "{source}_token_expiry"
        "{source}_access_token"

Example:

    Collection:
        "source": "azure"
    Environment:
        "azure_client_id"
        "azure_client_secret"
        "azure_auth_url"
        "azure_auth_scope"

 Returned variables if successful:
    Environment:
        "azure_token_expiry"
        "azure_access_token"

 Authorization for the collection needs to be Bearer Token.
 Token value needs to be {{{{source}}_access_token}}
 Requests in the collection should inherit auth from parent.
*/

// Static variables
const env = pm.collectionVariables.get("source");
const currentTime = Date.now();

// Environment variables
const tokenExpiry = pm.environment.get(`${env}_token_expiry`);
const accessToken = pm.environment.get(`${env}_access_token`);
const clientId = pm.environment.get(`${env}_client_id`);
const clientSecret = pm.environment.get(`${env}_client_secret`);
const requestUrl = pm.environment.get(`${env}_auth_url`);
const scope = pm.environment.get(`${env}_auth_scope`);
const missingVariables = !clientId || !clientSecret || !requestUrl || !scope;
const shouldUpdate = !tokenExpiry || !accessToken || (tokenExpiry <= currentTime);

if (missingVariables) {
    var missing = ""
    const required = new Map();
    required.set(`${env}_client_id`, clientId)
    required.set(`${env}_client_secret`, clientSecret)
    required.set(`${env}_auth_url`, requestUrl)
    required.set(`${env}_auth_scope`, scope)

    for (let [key, value] of required) {
        if (!value) {
            console.warn(`Missing environment variable ${key}`)
            missing += ` ${key} `
        }
    }
    throw ReferenceError(`Missing Required Environment Variables: ${missing}`)
}

if (shouldUpdate) {
    console.log("Retrieving new access token")
    pm.sendRequest({
        url: requestUrl,
        method: 'POST',
        header: {
            'Authorization': `Basic ${btoa(`${clientId}:${clientSecret}`)}`,
            'Content-Type': 'application/x-www-form-urlencoded',
            'Accept': 'application/json'
        },
        body: {
            mode: 'raw',
            raw: `grant_type=client_credentials&scope=${scope}`
        }
    }, function (err, response) {
        if (err) {
            console.warn("Token request failed");
            throw err;
        }

        const json = response.json();
        if (json.error) throw Error(`FAILED TO ACQUIRE ACCESS TOKEN.  ${json.error_description}`);

        pm.environment.set(`${env}_access_token`, json.access_token);
        pm.environment.set(`${env}_token_expiry`, currentTime + (json.expires_in * 1000));
    });
}

I hope this helps!

1 Like

That’s a rather excellent script, though I was hoping that Postman had an out-of-the-box solution to what seems like a common set of functionality - using Environments and Authorization together - but if there isn’t a better way, I’m going to steal your script :slight_smile:

Thanks mate.

How to set up this script in postman?
Or Is it running outside the postman and token is picked by user then added to the postman?
Let help on this. I am facing issue with tons of API , switching between them with token is headache.

The script is certainly very nice, but I also would like to see that functionality being available natively.

In my usecase, I have requests which I run, but on different platforms and with different creds. I either have to switch tokens manually, or do other duplication shenanigangs. Would be pretty cool to switch to enviroment_29405 and have postman request token_29405 seamlessly.

It’s crazy to me that this isn’t built-in functionality. Hell, it’d be more convenient if it didn’t save the tokens at all and just fetched a new one with every request (I mean, that’d be kinda bad, but not as bad as the current behavior). Why are tokens stored globally rather than per-environment to begin with? Who does that benefit?

Hi @adam.gardner. Can you share a screenshot of your current experience and more detail? (If it’s sensitive or something, you can DM me on Twitter: x.com)

This actually does seem like a good use case for the scripting capabilities of Postman since they’re flexible to generic user problems, but I’m always eager to learn if we can support something specific that helps benefit users.

I am mostly not 100% sure how we would know how to infer and fetch tokens in a generic way which is why the scripting API exists.

1 Like

Having the same issue now and came over this thread. Seems like the functionality still isn’t in Postman. As has been stated, it would be better for Postman to fetch the token on every request rather than storing it. Now we have to fetch the token manually when the environment is changed.

Alternatively postman could check if the variables values has changed in the setting and fetch a new token if nescessary.

I believe this is instead a use case that should not require scripting because it’s a very common use case. I believe 99% of the users have multiple accounts that they want to test and in some cases they use code flow with PKCE. Scripting should be there for custom stuff that is not the standard.
Is there any way to trigger the configure OAuth token request from a script?