Skip to main content

Troubleshooting OAuth 2.0 Connections

This pages focuses on troubleshooting OAuth 2.0 authorization code connections, but similar debugging concepts can be applied to client credentials connections. Note that every application implements OAuth 2.0 a little differently, but this provides general recommendations for debugging OAuth 2.0 connections.

Troubleshooting authorization endpoints

When a customer user clicks Connect, they are brought to a third-party app's Authorization URL. Your client ID, the config variable's ID, permission scopes and the redirect URI is appended as search parameters to the Authorize URL.

For example, if the external application's authorize URL is https://auth.example.com/authorize, and your client ID is abc-123, your user will be directed to

https://auth.example.com/authorize?client_id=abc-123&redirect_uri=https%3A%2F%2Foauth2.prismatic.io%2Fcallback&scope=widget%3Aread+widget%3Awrite&state=SW5example

Here, state represents the config variable's ID in Prismatic, and is used when the user returns to our callback URL to determine which config variable to update.

Invalid client_id errors

If your users arrive at an authorization page that says "Invalid client_id parameter" (or a similar error), you should double-check your client ID. Verify that there are no leading or trailing whitespace characters, and that the client ID matches the client ID that you saw when you configured your application in the third-party system.

Incorrect redirect_uri errors

If your customers see an authorization page that says "redirect_uri mismatch" (or a similar error), you should verify that the callback URL you configured in the third-party system is correct. For the US region, the callback URL is https://oauth2.prismatic.io/callback. For other public regions, private cloud hosted options, or white-labeled callback URLs, see Authorization code callback URL.

Troubleshooting code token exchange

After authenticating with a third-party app and walking through the app's consent screen, a user will return to Prismatic's OAuth 2.0 callback URL with their authorization code in hand. Depending on if you white-label the callback URL or if you're hosted in a different region, they'll end up on a URL that looks similar to:

https://oauth2.prismatic.io/callback?code=some-unique-auth-code&state=SW5example

The Prismatic OAuth 2.0 service then loads the config variable from the state parameter to match a config variable's ID, and attempts to exchange the code for an access_token using the third party app's token URL.

Flavors of auth code token exchange

Different apps implement auth code exchange in different ways. Some apps expect you to pass your client ID and secret as a base64-encoded auth header. Others expect that they're passed in a body. Some apps expect that your body is formdata-encoded, while others expect JSON.

The Prismatic OAuth 2.0 service attempts each of the six common "flavors" of auth code token exchange, in order of popularity, and succeeds once one has succeeded, and fails if none succeed. Suppose your client ID is my-client-id, your client secret is my-client-secret, and the code your customer returned with is some-unique-auth-code. Prismatic would attempt these token exchanges:

  1. Client ID / secret are URL-encoded and then base64-encoded and sent as an auth header. Body is formdata-encoded

    curl -X POST \
    --header 'Authorization: Basic bXktY2xpZW50LWlkOm15LWNsaWVudC1zZWNyZXQ=' \
    --header 'Content-Type: application/x-www-form-urlencoded' \
    https://app.example.com/oauth2/token \
    --data 'grant_type=authorization_code&scope=widgets%3Aread%20widgets%3Awrite%20offline_access&redirect_uri=https%3A%2F%2Foauth2.prismatic.io%2Fcallback&code=some-unique-auth-code'
  2. Client ID / secret are URL-encoded and then base64-encoded and sent as an auth header. Body is JSON-encoded

    curl -X POST \
    --header 'Authorization: Basic bXktY2xpZW50LWlkOm15LWNsaWVudC1zZWNyZXQ=' \
    --header 'Content-Type: application/json' \
    https://app.example.com/oauth2/token \
    --data '{"code":"some-unique-auth-code","grant_type":"authorization_code","redirect_uri":"https://oauth2.prismatic.io/callback","scope":"widgets:read widgets:write offline_access"}'
  3. Client ID / secret are sent within the body. Body is formdata-encoded

    curl -X POST \
    --header 'Content-Type: application/x-www-form-urlencoded' \
    https://app.example.com/oauth2/token \
    --data 'grant_type=authorization_code&scope=widgets%3Aread%20widgets%3Awrite%20offline_access&redirect_uri=https%3A%2F%2Foauth2.prismatic.io%2Fcallback&code=some-unique-auth-code&client_id=my-client-id&client_secret=my-client-secret'
  4. Client ID / secret are sent within the body. Body is JSON-encoded

    curl -X POST \
    --header 'Content-Type: application/json' \
    https://app.example.com/oauth2/token \
    --data '{"client_id":"my-client-id","client_secret":"my-client-secret","code":"some-unique-auth-code","grant_type":"authorization_code","redirect_uri":"https://oauth2.prismatic.io/callback","scope":"widgets:read widgets:write offline_access"}'
  5. Client ID / secret are just base64-encoded and sent as an auth header. Body is formdata-encoded. This will be the same as flavor #1, but characters (like whitespace) are not URL-encoded before base64-encoded.

  6. Client ID / secret are just base64-encoded and sent as an auth header. Body is JSON-encoded. This will be the same as flavor #2, but characters (like whitespace) are not URL-encoded before base64-encoded.

Mocking a token exchange endpoint

If you'd like to see exactly what the Prismatic OAuth 2.0 service attempts to send a token endpoint, you can spin up a Docker container locally to simulate a token endpoint.

Run a Smocker container:

docker run -d --restart=always \
-p 8080:8080 \
-p 8081:8081 \
--name smocker \
thiht/smocker

Then, declare a token "smock" endpoint:

curl -XPOST \
localhost:8081/mocks \
--header "Content-Type: application/x-yaml" \
--data \
'
- request:
method: POST
path: /oauth2/token
response:
status: 200
headers:
Content-Type: application/json
body: >
{
"access_token": "my-access-token",
"token_type": "bearer",
"expires_in": 60,
"example_parameter": "example_value",
"refresh_token": "my-refresh-token"
}
'

This endpoint will accept any request, and return a fake access token response.

Next, expose your Docker container with ngrok:

ngrok http 8080

From there, you can take note of your ngrok endpoint and set your Token URL in your connection to something like https://31ce-123-123-123-123.ngrok-free.app/oauth/token.

By visiting http://localhost:8081, you'll be able to view each request Prismatic's OAuth 2.0 service made. If you change the status in the smock to status: 500, you'll see all six token exchange requests that Prismatic's OAuth 2.0 token exchange service made.

Mocking OAuth 2.0 token endpoint with Smocker

Verify token exchange works with Postman

Once you've captured a token request, try to send that equivalent request to your third-party app either with curl or Postman. If you get a 404, you may have the wrong token URL configured. You may also get a more descriptive response which can help you identify changes you need to make to scopes, etc.

Tokens refresh errors

If a token exchange works initially, but later the token fails to refresh, it's possible that you omitted an offline_access scope, which many apps use to indicate that you need long-term access.