Refresh token exchanges to /oauth2/default/v1/token 401s from staging but not from local

We recently built out an endpoint in our API to allow a user to supply a refresh token and retrieve a new refresh token, using a request from our services as described here.

When testing on our local machines, this flow was able to be successfully executed (a user could supply an expired bearer token + their refresh token and was issued a new bearer + refresh token), but when we deployed endpoint to our staging environment, our call from our services to the Okta <env uri>/oauth2/default/v1/token endpoint was given a 401 response.

Token rotation is enabled, and the scope grant type we are requesting in the request params is refresh_token . The scopes we are requesting are offline_access and openid.

What would be a potential reason behind this? Is the request detailed in the linked article not intended to be made to the Okta API from a backend service, and rather made by our user?

The scope should be offline_access for getting a refresh token instead of refresh_token.

refresh_token was referring to grant_type; my mistake. The scopes are offline_access and openid.

The 401 response could mean the wrong client credentials were provided. Can you check if the value you’re using for the authorization header is valid?

authorization:'Basic MG9hYmg3M...'

When running this locally, the request completes successfully without an Authorization header, but not the case on our staging environment. I am assuming an expired authorization token cannot be passed as the Authorization header. We will add this and attempt it on our staging environment and see if it makes a difference.

Our use case is that a user on a mobile app has had their authorization token expire, and is attempting to use their refresh token to acquire a new one via our endpoint to avoid going through a login flow. If what we’re attempting isn’t the proper way to go about this, I would be more than open to alternatives!

The Authorization header containing the client credentials should be required. Could the local test app be appending it automatically?

The use case makes sense to me. If the access token or id token expires, you can use the refresh token to retrieve a new set of tokens.

Just tried using the user’s bearer token to make that request in the Authorization header and got a 401 back from Okta when doing so; this was done on our local instance.

I don’t believe the local application would be appending the request automatically - the code of our request is below:

NewTokenResponse response = webClientBuilder
          .baseUrl("https://<our okta domain>.okta.com/oauth2/default/v1/token")
          .defaultHeader(HttpHeaders.CONTENT_TYPE, APPLICATION_FORM)
          .build()
          .post()
          .uri(uriBuilder -> uriBuilder
              .queryParam("grant_type", GRANT_TYPE_VALUE) // grant type is "refresh_token"
              .queryParam("refresh_token", refreshToken)
              .queryParam("scope", SCOPE_VALUE) // scopes are "offline_access openid"
              .queryParam("client_id", clientId)
              .build())
          .retrieve()
          .bodyToMono(NewTokenResponse.class)
          .block();

How would one go about checking if the local version of the application is inserting a header of some kind?

As we’re using a mobile/native application, we followed instructions detailed here that state we can make the request without an authorization header.

We got our problem solved - turns out the clientId in the yml for our staging environment didn’t match the one we were using on our local environment. The clientId on local was for a native app, and the clientId on our staging was for a Web app.

For posterity: we found the oidc docs to be very helpful; they offered more details about what parameters are needed in which requests (hence why ours were working without a redirect uri nor authorization header)

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.