pilcrowonpaper / oslo

A collection of auth-related utilities
https://oslo.js.org
MIT License
1.06k stars 35 forks source link

OAuth2RequestError: invalid_request - unknown field "client_id" when validating Dropbox authorization code #90

Open manishrc opened 1 month ago

manishrc commented 1 month ago

When attempting to validate the authorization code received from Dropbox, I'm encountering an OAuth2RequestError with the message "invalid_request" and description "unknown field 'client_id'". This occurs in the validateAuthorizationCode method of the Dropbox provider.

The error description states:

unknown field 'client_id'

Environment:

Steps to reproduce:

  1. Set up Dropbox OAuth2 provider with Arctic
  2. Initiate the OAuth2 flow
  3. After receiving the authorization code, attempt to validate it using dropbox.validateAuthorizationCode(code)

Expected behavior: The authorization code should be successfully validated and exchanged for access tokens.

Actual behavior: An OAuth2RequestError is thrown with the message "invalid_request" and description "unknown field 'client_id'".

Any help or insights into this issue would be greatly appreciated. Let me know if you need any additional information.

Stack Trace

OAuth2RequestError: invalid_request
    at OAuth2Client.sendTokenRequest (webpack-internal:///(rsc)/./node_modules/oslo/dist/oauth2/index.js:108:19)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async OAuth2Client.validateAuthorizationCode (webpack-internal:///(rsc)/./node_modules/oslo/dist/oauth2/index.js:68:16)
    at async Dropbox.validateAuthorizationCode (webpack-internal:///(rsc)/./node_modules/arctic/dist/providers/dropbox.js:28:24)
    at async GET (webpack-internal:///(rsc)/./app/login/dropbox/callback/route.js:22:24)
    at async /Users/admin/Projects/test-lucia-next/node_modules/next/dist/compiled/next-server/app-route.runtime.dev.js:6:55038
    at async ek.execute (/Users/admin/Projects/test-lucia-next/node_modules/next/dist/compiled/next-server/app-route.runtime.dev.js:6:45808)
    at async ek.handle (/Users/admin/Projects/test-lucia-next/node_modules/next/dist/compiled/next-server/app-route.runtime.dev.js:6:56292)
    at async doRender (/Users/admin/Projects/test-lucia-next/node_modules/next/dist/server/base-server.js:1377:42)
    at async cacheEntry.responseCache.get.routeKind (/Users/admin/Projects/test-lucia-next/node_modules/next/dist/server/base-server.js:1599:28)
    at async DevServer.renderToResponseWithComponentsImpl (/Users/admin/Projects/test-lucia-next/node_modules/next/dist/server/base-server.js:1507:28)
    at async DevServer.renderPageComponent (/Users/admin/Projects/test-lucia-next/node_modules/next/dist/server/base-server.js:1931:24)
    at async DevServer.renderToResponseImpl (/Users/admin/Projects/test-lucia-next/node_modules/next/dist/server/base-server.js:1969:32)
    at async DevServer.pipeImpl (/Users/admin/Projects/test-lucia-next/node_modules/next/dist/server/base-server.js:920:25)
    at async NextNodeServer.handleCatchallRenderRequest (/Users/admin/Projects/test-lucia-next/node_modules/next/dist/server/next-server.js:272:17)
    at async DevServer.handleRequestImpl (/Users/admin/Projects/test-lucia-next/node_modules/next/dist/server/base-server.js:816:17)
    at async /Users/admin/Projects/test-lucia-next/node_modules/next/dist/server/dev/next-dev-server.js:339:20
    at async Span.traceAsyncFn (/Users/admin/Projects/test-lucia-next/node_modules/next/dist/trace/trace.js:154:20)
    at async DevServer.handleRequest (/Users/admin/Projects/test-lucia-next/node_modules/next/dist/server/dev/next-dev-server.js:336:24)
    at async invokeRender (/Users/admin/Projects/test-lucia-next/node_modules/next/dist/server/lib/router-server.js:174:21)
    at async handleRequest (/Users/admin/Projects/test-lucia-next/node_modules/next/dist/server/lib/router-server.js:353:24)
    at async requestHandlerImpl (/Users/admin/Projects/test-lucia-next/node_modules/next/dist/server/lib/router-server.js:377:13)
    at async Server.requestListener (/Users/admin/Projects/test-lucia-next/node_modules/next/dist/server/lib/start-server.js:141:13) {
  request: Request {
    [Symbol(realm)]: { settingsObject: [Object] },
    [Symbol(state)]: {
      method: 'POST',
      localURLsOnly: false,
      unsafeRequest: false,
      body: [Object],
      client: [Object],
      reservedClient: null,
      replacesClientId: '',
      window: 'client',
      keepalive: false,
      serviceWorkers: 'all',
      initiator: '',
      destination: '',
      priority: null,
      origin: 'client',
      policyContainer: 'client',
      referrer: 'client',
      referrerPolicy: '',
      mode: 'cors',
      useCORSPreflightFlag: false,
      credentials: 'same-origin',
      useCredentials: false,
      cache: 'default',
      redirect: 'follow',
      integrity: '',
      cryptoGraphicsNonceMetadata: '',
      parserMetadata: '',
      reloadNavigation: false,
      historyNavigation: false,
      userActivation: false,
      taintedOrigin: false,
      redirectCount: 0,
      responseTainting: 'basic',
      preventNoCacheCacheControlHeaderModification: false,
      done: false,
      timingAllowFailed: false,
      headersList: [HeadersList],
      urlList: [Array],
      url: URL {}
    },
    [Symbol(signal)]: AbortSignal { aborted: false },
    [Symbol(headers)]: HeadersList {
      cookies: null,
      [Symbol(headers map)]: [Map],
      [Symbol(headers map sorted)]: [Array]
    }
  },
  description: 'unknown field "client_id"'
}

Relevant Code

// /auth/index.js
export const dropbox = new Dropbox(
  process.env.DROPBOX_CLIENT_ID,
  process.env.DROPBOX_CLIENT_SECRET
  'https://remove-callback-url/login/dropbox/callback'
);
// app/login/dropbox/route.js
import { dropbox } from '@/auth';
import { generateState } from 'arctic';
import { cookies } from 'next/headers';

export async function GET() {
  const state = generateState();
  const url = await dropbox.createAuthorizationURL(state, {
    scopes: ['openid', 'email', 'profile'],
  });
  url.searchParams.set('access_type', 'offline');

  https: cookies().set('dropbox_oauth_state', state, {
    path: '/',
    secure: process.env.NODE_ENV === 'production',
    httpOnly: true,
    maxAge: 60 * 10,
    sameSite: 'lax',
  });

  return Response.redirect(url);
}
// app/login/dropbox/callback/route.js
export async function GET(request) {
  const url = new URL(request.url);
  const code = url.searchParams.get('code');
  const state = url.searchParams.get('state');
  const storedState = cookies().get('dropbox_oauth_state')?.value ?? null;

  if (!code || !state || !storedState || state !== storedState) {
    return new Response(null, {
      status: 400,
    });
  }

  try {
    const tokens = await dropbox.validateAuthorizationCode(code);
    console.log({ tokens });
  } catch (e) {
    console.error(e)
  }
}