{"record":{"id":"57d76613a03662b5","repo":"gitroomhq/postiz-app","slug":"code-challenge-is-required-for-this-client","errorCode":null,"errorMessage":"code_challenge is required for this client","messagePattern":"code_challenge is required for this client","errorType":"http","errorClass":"HttpException","httpStatus":400,"severity":"error","filePath":"libraries/nestjs-libraries/src/database/prisma/oauth/oauth.service.ts","lineNumber":200,"sourceCode":"      redirectUri?: string;\n      codeChallenge?: string;\n      codeChallengeMethod?: string;\n    }\n  ) {\n    const app = await this._oauthRepository.getAppByClientId(clientId);\n    if (!app) {\n      throw new HttpException('Invalid client_id', HttpStatus.BAD_REQUEST);\n    }\n\n    // Dynamically registered clients must use their registered redirect_uris\n    // and PKCE; statically registered apps keep the existing lenient flow\n    if (app.dynamic) {\n      const registered: string[] = JSON.parse(app.redirectUris || '[]');\n      if (!options?.redirectUri || !registered.includes(options.redirectUri)) {\n        throw new HttpException('Invalid redirect_uri', HttpStatus.BAD_REQUEST);\n      }\n      if (app.tokenEndpointAuthMethod === 'none' && !options?.codeChallenge) {\n        throw new HttpException(\n          'code_challenge is required for this client',\n          HttpStatus.BAD_REQUEST\n        );\n      }\n      if (\n        options?.codeChallenge &&\n        options?.codeChallengeMethod &&\n        options.codeChallengeMethod !== 'S256'\n      ) {\n        throw new HttpException(\n          'Only the S256 code_challenge_method is supported',\n          HttpStatus.BAD_REQUEST\n        );\n      }\n    }\n\n    return app;\n  }","sourceCodeStart":182,"sourceCodeEnd":218,"githubUrl":"https://github.com/gitroomhq/postiz-app/blob/0f1647f7491a217d43eb5ae7a480484bdf0aff3e/libraries/nestjs-libraries/src/database/prisma/oauth/oauth.service.ts#L182-L218","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Enable PKCE (S256) in your OAuth client library and retry the authorization request","Verify code_challenge and code_challenge_method=S256 are present and URL-encoded in the authorize URL","If the client can keep a secret, re-register with a confidential token_endpoint_auth_method instead"],"exampleFix":"// before\nconst client = new AuthorizationCode({\n  clientId,\n  redirectUri,\n  tokenEndpointAuthMethod: 'none',\n});\n// after\nconst client = new AuthorizationCode({\n  clientId,\n  redirectUri,\n  tokenEndpointAuthMethod: 'none',\n});\nconst url = client.authorizeURL({\n  scope: 'openid',\n  code_challenge: await pkceChallengeFromVerifier(verifier),\n  code_challenge_method: 'S256',\n});","handlingStrategy":"validation","validationCode":"if (client.tokenEndpointAuthMethod === 'none' && !codeChallenge) {\n  codeChallenge = await computeS256Challenge(generateVerifier());\n}","typeGuard":"const needsPkce = (app: {dynamic: boolean; tokenEndpointAuthMethod: string}) => app.dynamic && app.tokenEndpointAuthMethod === 'none';","tryCatchPattern":"try { await authorize(req); } catch (e) { if (e?.response?.status === 400 && /code_challenge/.test(e.message)) { return restartFlowWithPkce(); } throw e; }","preventionTips":["Always use an OAuth library with PKCE S256 enabled for public clients","Generate and persist the verifier before building the authorize URL"],"tags":["oauth2","pkce","public-client","authorization"],"backgroundTag":"oauth-pkce-required","analyzedSha":"0f1647f7491a217d43eb5ae7a480484bdf0aff3e","analyzedAt":"2026-08-27T12:09:55.020Z","schemaVersion":2},"datasetVersion":"2026-08-27T13:17:12.746Z"}