gitroomhq/postiz-app · error · HttpException

code_challenge is required for this client

Error message

code_challenge is required for this client

What it means

Thrown when a public dynamic client (token_endpoint_auth_method=none, e.g. an SPA, CLI, or AI agent like an MCP client) sends an authorization request without a PKCE code_challenge. RFC 7636 PKCE is mandatory for public clients because they have no client secret to protect code interception.

Source

Thrown at libraries/nestjs-libraries/src/database/prisma/oauth/oauth.service.ts:200

      redirectUri?: string;
      codeChallenge?: string;
      codeChallengeMethod?: string;
    }
  ) {
    const app = await this._oauthRepository.getAppByClientId(clientId);
    if (!app) {
      throw new HttpException('Invalid client_id', HttpStatus.BAD_REQUEST);
    }

    // Dynamically registered clients must use their registered redirect_uris
    // and PKCE; statically registered apps keep the existing lenient flow
    if (app.dynamic) {
      const registered: string[] = JSON.parse(app.redirectUris || '[]');
      if (!options?.redirectUri || !registered.includes(options.redirectUri)) {
        throw new HttpException('Invalid redirect_uri', HttpStatus.BAD_REQUEST);
      }
      if (app.tokenEndpointAuthMethod === 'none' && !options?.codeChallenge) {
        throw new HttpException(
          'code_challenge is required for this client',
          HttpStatus.BAD_REQUEST
        );
      }
      if (
        options?.codeChallenge &&
        options?.codeChallengeMethod &&
        options.codeChallengeMethod !== 'S256'
      ) {
        throw new HttpException(
          'Only the S256 code_challenge_method is supported',
          HttpStatus.BAD_REQUEST
        );
      }
    }

    return app;
  }

View on GitHub (pinned to 0f1647f749)

Solutions

  1. Enable PKCE (S256) in your OAuth client library and retry the authorization request
  2. Verify code_challenge and code_challenge_method=S256 are present and URL-encoded in the authorize URL
  3. If the client can keep a secret, re-register with a confidential token_endpoint_auth_method instead

Example fix

// before
const client = new AuthorizationCode({
  clientId,
  redirectUri,
  tokenEndpointAuthMethod: 'none',
});
// after
const client = new AuthorizationCode({
  clientId,
  redirectUri,
  tokenEndpointAuthMethod: 'none',
});
const url = client.authorizeURL({
  scope: 'openid',
  code_challenge: await pkceChallengeFromVerifier(verifier),
  code_challenge_method: 'S256',
});
Defensive patterns

Strategy: validation

Validate before calling

if (client.tokenEndpointAuthMethod === 'none' && !codeChallenge) {
  codeChallenge = await computeS256Challenge(generateVerifier());
}

Type guard

const needsPkce = (app: {dynamic: boolean; tokenEndpointAuthMethod: string}) => app.dynamic && app.tokenEndpointAuthMethod === 'none';

Try / catch

try { await authorize(req); } catch (e) { if (e?.response?.status === 400 && /code_challenge/.test(e.message)) { return restartFlowWithPkce(); } throw e; }

Prevention

When it happens

Trigger: Authorization request for a dynamic client with tokenEndpointAuthMethod='none' and no code_challenge parameter in the query string, or the parameter name misspelled/mangled by your OAuth library config.

Common situations: Using a plain fetch/redirect flow instead of an OAuth library with PKCE enabled; library has PKCE disabled by default (e.g. some SDKs require usePkce: true / code_challenge_method config); switching a confidential client to public without updating the client code.

Related errors


AI-assisted analysis of gitroomhq/postiz-app@0f1647f749 (2026-08-27). Data as JSON: /api/errors/57d76613a03662b5. Report an issue: GitHub.