gitroomhq/postiz-app · error · HttpException
Invalid redirect_uri
Error message
Invalid redirect_uri
What it means
Thrown during OAuth authorization request validation when the app was registered dynamically (RFC 7591) and the redirect_uri supplied in the request is missing or not in the client's registered redirectUris list. Dynamic clients use strict redirect_uri matching per the OAuth 2.0 spec, unlike statically registered Postiz apps which follow a lenient flow.
Source
Thrown at libraries/nestjs-libraries/src/database/prisma/oauth/oauth.service.ts:197
async validateAuthorizationRequest(
clientId: string,
options?: {
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
);
}
}View on GitHub (pinned to 0f1647f749)
Solutions
- Compare the exact redirect_uri string (scheme, host, port, path, query) against the JSON array in oauthApp.redirectUris for that clientId
- Re-register the client (dynamic registration endpoint) including the exact callback URL you redirect to
- If the app should use the lenient flow, verify it wasn't created via dynamic registration (app.dynamic flag)
- Check that redirect_uri is properly URL-encoded and not being stripped by your OAuth library
Example fix
// before
const url = `https://postiz.example.com/oauth/authorize?client_id=${id}`;
// after
const url = `https://postiz.example.com/oauth/authorize?client_id=${id}&redirect_uri=${encodeURIComponent('http://localhost:3000/callback')}`; Defensive patterns
Strategy: validation
Validate before calling
const registered = app.redirectUris; // from dynamic registration response
if (!redirectUri || !registered.includes(redirectUri)) {
throw new Error(`redirect_uri must be one of: ${registered.join(', ')}`);
} Type guard
const isValidRedirectUri = (uri: string, registered: string[]): boolean => registered.includes(uri);
Try / catch
try { await authorize(req); } catch (e) { if (e instanceof HttpException && e.message === 'Invalid redirect_uri') { await reRegisterClient(); } throw e; } Prevention
- Store the redirectUris array from dynamic registration and build authorize URLs from it
- Use one config constant for the callback URL across the app
When it happens
Trigger: Calling the authorization endpoint with a dynamically registered client_id where the redirect_uri query param is omitted, has a different scheme/host/port/path, trailing slash, or was rotated after registration (stored as JSON in app.redirectUris).
Common situations: Dev server on http://localhost:3000 but registered http://127.0.0.1:3000; behind a proxy so the callback URL differs; client re-registered with new URIs but cached authorization URL; URL-encoding issues dropping the param.
Related errors
- code_challenge is required for this client
- Only the S256 code_challenge_method is supported
- invalid_grant
- Unauthorized
- Unauthorized
AI-assisted analysis of gitroomhq/postiz-app@0f1647f749 (2026-08-27).
Data as JSON: /api/errors/f882f74387445077.
Report an issue: GitHub.