OAuth 2.0 Refresh Token - How To Use "In Body" Client Credentials

My question:

Is it possible to coerce Postman’s built-in OAuth 2.0 “Refresh Token” function to use the same Client Authentication mode as the “Fetch Access Token” function? As best as I can tell, it always uses Basic Auth to fetch the refresh token.

Details (like screenshots):

I’m using the built-in “OAuth 2.0” Authorization that Postman provides to fetch an access token. Because my OAuth AS is configured to support “form-based” (aka: “post”, or “in-body”) client authentication, I select “Send client credentials in body” as the “Client Authentication” mode and I’m able to fetch the access token just fine:

Here’s the “fetch access token” request after clicking “Get New Access Token” that appears in the Postman console:

POST https://my.oauth.server.com/as/token          200   199 ms
  Request Headers
    Content-Type: application/x-www-form-urlencoded
    User-Agent: PostmanRuntime/7.30.0
    Accept: */*
    Cache-Control: no-cache
    Host: my.oauth.server.com
    Accept-Encoding: gzip, deflate, br
    Connection: keep-alive
    Content-Length: 280
  Request Body
    grant_type: "authorization_code"
    code: "some-auth-code"
    redirect_uri: "http://localhost/login/redirect-target"
    client_id: "my-client-id"
    client_secret: "my-client-secret"

So far, so good.

However, when it’s time to refresh the token, either automatically after the access token expires or explicitly when I click the “Refresh” link underneath my selected access token, I’m getting a 401 status code response with an error message that states “unsupported authentication method.”

Here’s the “refresh token” request that appears in the Postman console:

POST https://my.oauth.server.com/as/token          401  115 ms
  Request Headers
    Content-Type: application/x-www-form-urlencoded
    Authorization: Basic GsP...16=
    User-Agent: PostmanRuntime/7.30.0
    Accept: */*
    Cache-Control: no-cache
    Host: my.oauth.server.com
    Accept-Encoding: gzip, deflate, br
    Connection: keep-alive
    Content-Length: 712
  Request Body
    grant_type: "refresh_token"
    refresh_token: "eyJ...egQ"
  Response Body
    {
      "error" : "invalid_client",
      "error_description" : "Request denied: Unsupported authentication method"
    }

I’ve already tried:

If I choose “Send as Basic Auth header” when fetching the access token, I get an identical 401 response as shown above. This leads me to believe that if the refresh token request used the same “Client Authentication” mode as the fetch access token request, everything would work as expected.

I’ve also tried looking in the documentation, community forums, and the app’s settings to see if there’s an option for coercing this behavior, but I’m not finding anything.

2 Likes

Really Appreciate it if anyone could explain how to use Client Authentication: send client credentials in the post body for refresh token call of oauth2 authentication of the postman

@sagars.esds

Another option is to use a pre-request script instead of the built in authentication.

This is an example of authenticating to Microsoft.

It uses the expiry date and only gets a new token if it thinks the current one has expired.

pm.test("Check for collectionVariables", function () {
    let vars = ['clientId', 'clientSecret', 'tenantId', 'username', 'password', 'scope'];
    vars.forEach(function (item, index, array) {
        console.log(item, index);
        pm.expect(pm.collectionVariables.get(item), item + " variable not set").to.not.be.undefined;
        pm.expect(pm.collectionVariables.get(item), item + " variable not set").to.not.be.empty; 
    });

    if (!pm.collectionVariables.get("bearerToken") || Date.now() > new Date(pm.collectionVariables.get("bearerTokenExpiresOn") * 1000)) {
        pm.sendRequest({
            url: 'https://login.microsoftonline.com/' + pm.collectionVariables.get("tenantId") + '/oauth2/v2.0/token',
            method: 'POST',
            header: 'Content-Type: application/x-www-form-urlencoded',
            body: {
                mode: 'urlencoded',
                urlencoded: [
                    { key: "client_id", value: pm.collectionVariables.get("clientId"), disabled: false },
                    { key: "scope", value: pm.collectionVariables.get("scope"), disabled: false },
                    { key: "username", value: pm.collectionVariables.get("username"), disabled: false },
                    { key: "password", value: pm.collectionVariables.get("password"), disabled: false },                    
                    { key: "client_secret", value: pm.collectionVariables.get("clientSecret"), disabled: false },
                    { key: "grant_type", value: "password", disabled: false },
                ]
            }
        }, function (err, res) {
            if (err) {
                console.log(err);
            } else {
                pm.test("Status code is 200", () => {
                    pm.expect(res).to.have.status(200);
                });
                let resJson = res.json();
                pm.collectionVariables.set("bearerTokenExpiresOn", resJson.expires_in);
                pm.collectionVariables.set("bearerToken", resJson.id_token);
            }
        });
    }
});
2 Likes

I’ve just run into this same issue; I need the client_id sent in the refresh request body, and it’s not clear where this can be turned on (if at all).

1 Like

Hi, I’m fairly new to Postman and was having this issue. I solved it by setting this under Advanced parameters

image

In my case I have set the clientId variable for the collection I’m working with.

Which I admit is weird and may be a bug because the documentation for the API I’m using doesn’t say I need to explicitly pass the client_id parameter for the token refresh. I just decided to try this and it worked.