usebruno/bruno · error · Error

Access token URL is required for OAuth2

Error message

Access token URL is required for OAuth2

What it means

Thrown by getOAuth2AccessToken when grantType is present but accessTokenUrl is falsy. This is the top-level URL check, distinct from the per-flow checks at lines 122/215; it runs after the grant-type check and before the supported-grant-type check, so it catches the omission regardless of flow.

Source

Thrown at packages/bruno-requests/src/auth/oauth2-helper.ts:337

export const getOAuth2Token = async (oauth2Config: OAuth2Config, tokenStore: TokenStore, verbose: string, axiosInstance?: AxiosInstance): Promise<string | null> => {
  const {
    grantType,
    accessTokenUrl,
    credentialsId = 'default',
    autoFetchToken = true,
    tokenSource = 'access_token'
  } = oauth2Config;

  if (verbose) {
    debug.enable('oauth2');
  }

  if (!grantType) {
    throw new Error('Grant type is required for OAuth2');
  }

  if (!accessTokenUrl) {
    throw new Error('Access token URL is required for OAuth2');
  }

  if (!['client_credentials', 'password'].includes(grantType)) {
    throw new Error(`Unsupported grant type: ${grantType}. Supported types: client_credentials, password`);
  }

  // Check if we already have credentials stored
  const existingToken = await tokenStore.getCredential({ url: accessTokenUrl, credentialsId });

  if (existingToken) {
    // Check if token is expired
    if (!isTokenExpired(existingToken)) {
      // Token is valid, use it
      return tokenSource === 'id_token' ? existingToken.id_token : existingToken.access_token;
    } else {
      // Token is expired
      if (autoFetchToken) {
        // Clear expired token and proceed to fetch new token

View on GitHub (pinned to 9bdd81c7bd)

Solutions

  1. Populate accessTokenUrl with the authorization server's token endpoint.
  2. If the URL is templated, resolve it (env substitution) before passing the config to the helper.
  3. Add an assertion at config build time so missing URLs fail loudly and early.

Example fix

// before
const config = { grantType: 'client_credentials', clientId: id, clientSecret: secret };

// after
const config = {
  grantType: 'client_credentials',
  accessTokenUrl: process.env.OAUTH_TOKEN_URL || 'https://auth.example.com/oauth/token',
  clientId: id,
  clientSecret: secret
};
Defensive patterns

Strategy: validation

Validate before calling

if (!config.accessTokenUrl) throw new Error('accessTokenUrl must be set on OAuth2Config');

Type guard

function hasAccessTokenUrl(c) { return typeof c.accessTokenUrl === 'string' && c.accessTokenUrl.length > 0; }

Try / catch

try { await getOAuth2AccessToken(config, tokenStore); }
catch (e) { if (e.message === 'Access token URL is required for OAuth2') { /* set URL */ } else throw e; }

Prevention

When it happens

Trigger: Calling getOAuth2AccessToken with a valid grantType but no accessTokenUrl. Note this fires before flow-specific helpers, so it pre-empts errors 380 and 382.

Common situations: Config built from a template that left the token URL as a placeholder; env var for the URL not set in the deployed environment; the URL was stripped by a sanitizer.

Related errors


AI-assisted analysis of usebruno/bruno@9bdd81c7bd (2026-08-13). Data as JSON: /api/errors/535ba76e5d8b3184. Report an issue: GitHub.