Skip to content

# Fix OAuth callback crash on malformed state parameter #1285

Open
7vignesh wants to merge 1 commit intoRocketChat:developfrom
7vignesh:fix/1284-oauth-state-decode-guard
Open

# Fix OAuth callback crash on malformed state parameter #1285
7vignesh wants to merge 1 commit intoRocketChat:developfrom
7vignesh:fix/1284-oauth-state-decode-guard

Conversation

@7vignesh
Copy link
Copy Markdown

Fixes #1284

The OAuth callback endpoint crashes when receiving a malformed state query parameter because decodeURIComponent throws an unhandled URIError. This breaks the entire OAuth callback flow and login for affected users.

Changes

  • Add type validation for state parameter (must be string)
  • Wrap decodeURIComponent in try/catch to catch URIError
  • Return controlled 400 response with user-friendly error message instead of crashing endpoint
  • Extract CSP header to constant to reduce duplication

The endpoint now gracefully handles both missing and malformed state values by returning a safe callback HTML response with appropriate error messaging.

Testing

Manual Testing:

  1. Trigger callback endpoint with malformed state: /api/apps/public/{appId}/callback?code=valid_code&state=%E0%A4%A
  2. Verify endpoint returns 400 with error page instead of crashing
  3. Verify valid state continues to work normally

Regression Testing:

  • Full workspace build completed successfully (9 packages, 0 failures)
  • Type checking passed for rc-app package

Fixes RocketChat#1284

The OAuth callback endpoint crashes when receiving a malformed  query
parameter because decodeURIComponent throws an unhandled URIError. This breaks
the entire OAuth callback flow and login for affected users.

Changes:
- Add type validation for state parameter (must be string)
- Wrap decodeURIComponent in try/catch to catch URIError
- Return controlled 400 response with user-friendly error message instead of
  crashing endpoint
- Extract CSP header to constant to reduce duplication

The endpoint now gracefully handles both missing and malformed state values
by returning a safe callback HTML response with appropriate error messaging.
Copilot AI review requested due to automatic review settings April 20, 2026 13:55
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a crash in the rc-app OAuth callback endpoint when the state query param is missing or malformed by validating the type and guarding decodeURIComponent, returning a controlled 400 HTML response instead of throwing.

Changes:

  • Validate state is a string before decoding.
  • Wrap decodeURIComponent(state) in try/catch and return a 400 error page on failure.
  • Deduplicate the CSP header value via a local constant.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +31 to +33
const contentSecurityPolicy =
"default-src 'self'; script-src 'self' 'unsafe-inline'; connect-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; frame-src 'self'; font-src 'self'; object-src 'none'";

Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

contentSecurityPolicy is created on every request even though it’s a true constant. Consider moving it to a module-level const (e.g., CONTENT_SECURITY_POLICY) to avoid repeated allocation and make the intent clearer.

Copilot uses AI. Check for mistakes.
Comment on lines +34 to +65
if (typeof state !== "string") {
return {
status: 400,
content: await getCallbackContent(
read,
null,
"",
"Invalid state parameter"
),
headers: {
"Content-Security-Policy": contentSecurityPolicy,
},
};
}

let origin;
try {
origin = decodeURIComponent(state);
} catch (e) {
return {
status: 400,
content: await getCallbackContent(
read,
null,
"",
"Invalid state parameter"
),
headers: {
"Content-Security-Policy": contentSecurityPolicy,
},
};
}
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 400-response blocks for invalid/malformed state are duplicated. Consider extracting a small helper (e.g., buildInvalidStateResponse()) to return the common { status, content, headers } shape so future changes (message, headers, etc.) only need to be made once.

Copilot uses AI. Check for mistakes.
Comment on lines +49 to +52
let origin;
try {
origin = decodeURIComponent(state);
} catch (e) {
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let origin; introduces an implicit any and catch (e) declares an unused variable. Prefer typing origin as string and using an optional catch binding (catch { ... }) or _e to avoid unused-variable lint warnings.

Suggested change
let origin;
try {
origin = decodeURIComponent(state);
} catch (e) {
let origin: string;
try {
origin = decodeURIComponent(state);
} catch {

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: OAuth callback crashes on malformed state due to unhandled decodeURIComponent error

2 participants