Skip to content

OIDC: 307 redirect to https:// redirect_uri does not trigger iOS app open — needs interstitial for all non-browser targets #514

Description

@openapphub

Description

When a native OIDC client (e.g. Element X on iOS) uses an https:// redirect_uri such as https://element.io/oauth/ios/io.element.elementx, the _complete endpoint returns a 307 redirect directly to that URL. On iOS, the browser does not trigger the app open via HTTP 307 redirect — the user sees a blank page or is redirected to element.io which does not handle the path. The login silently fails and the user is stuck.

Root cause

In src/api/oidc/complete.rs, needs_interstitial() only returns true when redirect_url.scheme().contains(".") (i.e. private-use reverse-DNS schemes like io.element.android). For https:// redirect URIs — which Element X iOS uses (https://element.io/oauth/ios/io.element.elementx) — it returns false, so the server sends a bare 307 redirect.

However, iOS Safari/Chrome do not trigger universal link / app deep link opening via HTTP 3xx redirects. They require either:

  1. A user-initiated navigation (click on a link), or
  2. A page load that calls window.location via JavaScript

A 307/302 redirect is followed silently by the browser networking stack and does not trigger the app open. This means Element X on iOS can never complete the OIDC login flow.

Reproduction

  1. Deploy Tuwunel 1.8.1 (main branch, commit 92bef7a) with oidc_native_auth = true
  2. Configure server_name, well_known.client, well_known.server correctly
  3. Use Element X on iOS to connect → OIDC flow starts
  4. Browser opens, login page appears, enter credentials
  5. POST /_tuwunel/oidc/native → 303 → GET /_tuwunel/oidc/_complete
  6. _complete returns 307 → https://element.io/oauth/ios/io.element.elementx?code=...&state=...
  7. Browser follows 307 to element.io — app does not open
  8. User sees blank page or element.io 404 page
  9. If user goes back and submits login again → 404 Unknown or expired authorization request (oidc_req_id already consumed)

Request/response trace

POST /_tuwunel/oidc/native → 303
  location: /_tuwunel/oidc/_complete?oidc_req_id=...&loginToken=...

GET /_tuwunel/oidc/_complete → 307
  location: https://element.io/oauth/ios/io.element.elementx?code=...&state=...
  (no body — direct redirect, no interstitial page)

Element X on iOS registers https://element.io/oauth/ios/io.element.elementx as a universal link. iOS should intercept this URL and open the app. But iOS does not intercept URLs reached via HTTP 3xx redirects — only direct user navigation.

Suggested fix

Option A (broad): Show the interstitial "Continue" page for all non-http://localhost redirect URIs, not just those with . in the scheme. A native app redirect URI — whether io.element.android.x:/callback or https://element.io/oauth/ios/io.element.elementx — always needs a user gesture (click) on iOS.

Option B (narrower): Show the interstitial when the redirect_uri host matches a known app-link pattern (e.g. path contains /oauth/ or /callback), or when the request includes prompt=consent.

Option C: Always show the interstitial for application_type: "native" clients regardless of redirect_uri scheme.

Environment

  • Tuwunel: 1.8.1 (main, commit 92bef7a)
  • Client: Element X iOS (latest)
  • redirect_uri: https://element.io/oauth/ios/io.element.elementx
  • Platform: iOS (Safari and in-app browser)
  • Server behind Traefik reverse proxy with TLS

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't right.client compatProblem with client-server interaction. Issue is preventing a client from working.

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions