Cannot exchange authorization code for access token

Hello!

I’m trying to migrate from token without scopes to token with granular scopes for an App I’m working on. I know I could use Forge App, but this is legacy code and would require too much work.

Switching to scoped token requires using OAuth 2.0 3LO for obtaining Cloud ID and interacting with API. I’m stuck on step “2. Exchange authorization code for access token” - every request I send comes with a respone 401 Unauthorized.

I’ve:

  1. created the app in developer console,
  2. configured permissions and requested scopes,
  3. configured authorization “OAuth 2.0 (3LO)” and set callback URL to http://localhost:52225/callback
  4. used authorization URL taken from " Authorization URL generator" in Authorization page, with substituting ${YOUR_USER_BOUND_VALUE} with a value from openssl rand -hex 32 (I have only single user, so I don’t mint it being static),
  5. Configured my .env appropriately with Client ID and Secret matching the ones in developer console (Settings tab),
  6. Ran the server, visit authorization URL, choose site and click Accept (give consent for my App),
  7. I’m being redirected to the http://localhost:52225/callback, the app reads code from query params, and sends the request described in the docs in step 2. Yet every time it fails with 401 Unauthorized.

Doesn’t matter which space I select - the one I’m admin of or not. It just doesn’t work.

The code responsible for making the request looks as follows:

app.get("/callback", async (ctx) => {
  const { code, state } = ctx.req.query();

  const res = await fetch("https://auth.atlassian.com/oauth/token", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      grant_type: "authorization_code",
      client_id: env.CLIENT_ID,
      client_secret: env.CLIENT_SECRET,
      code: code,
      redirect_uri: "http://localhost:52225/callback",
    }),
  });

  // rest of code
});

At the same time, trying to exchange the code with curl works fine. What am I missing?

#!/usr/bin/env bash

CLIENT_ID=""
CLIENT_SECRET=""
AUTH_CODE=''

curl --request POST \
    --url 'https://auth.atlassian.com/oauth/token' \
    --header 'Content-Type: application/json' \
    --data '{
        "grant_type": "authorization_code",
        "client_id": "'"$CLIENT_ID"'",
        "client_secret": "'"$CLIENT_SECRET"'",
        "code": "'"$AUTH_CODE"'",
        "redirect_uri": "http://localhost:52225/callback"
    }'

Both the code and bash script can be found in GitHub - jk-bug-reports/atlassian-oauth2-token

Thanks for any help in advance.

Turned out I was using wrong value for client_secret. Leaving repository for future generations of confused devs. Case solved.