Skip to Content
MuoCeon Docs
Third-party OAuth IntegrationPermissions and security

Redirect URI policy

The five redirect platforms, address shape rules, matching semantics and the port exception.

The redirect URI is where the authorization code is delivered, and the easiest part of the flow to misconfigure. This page makes "which addresses can be registered" and "which addresses can match" explicit.

Five platforms

PlatformAddress shapeSecret requiredAccess tokenRefresh token
web_serverhttps://host/path; loopback may use http://localhost/path✅ Confidential1 hour30 days
web_clientSame as above❌ Public30 minutes14 days
desktopLoopback address (with path) or a custom scheme such as myapp://callback❌ Public30 minutes14 days
ios_macosOnly com.muoceon.<bundle-id>://auth❌ Public30 minutes14 days
androidOnly com.muoceon.<package>://auth/<43-char signature hash>❌ Public30 minutes14 days

Token lifetimes depend on whether the client holds a secret, not on the platform itself: confidential clients have a server-side fallback and can use longer tokens; public clients keep tokens on the user's device, where a leak has no second line of defence, so only short lifetimes make sense.

Common validation (all platforms)

Re-run at both registration and matching time:

  • No more than 2048 characters, containing no whitespace.
  • Must parse as a valid URL.
  • No query, fragment, username or password:
    • https://example.com/cb?x=1 ❌
    • https://example.com/cb#frag ❌
    • https://user:pass@example.com/cb ❌
  • Scheme blocklist: javascript, data, file, blob, about, vbscript. These schemes "execute content" rather than "send the user back"; allowing them hands the code to a script.

Shape rules per platform

web_server / web_client

  • Must be https with a non-root path: https://example.com/cb ✅, https://example.com ❌.
  • Loopback may use http: http://localhost:3000/callback ✅.

desktop

One of two shapes:

  • A loopback address with a path: http://127.0.0.1:51789/callback ✅.
  • A custom scheme (not http/https): myapp://callback ✅.

ios_macos / android

Only reverse-domain schemes generated by convention are accepted; free-form values are rejected:

com.muoceon.com.yourcompany.yourapp://auth
com.muoceon.com.yourcompany.yourapp://auth/<signature hash>

The Android last segment is a 43-character signature hash (A-Z, a-z, 0-9, -, _). iOS and Android use independent schemes so that multiple apps declaring the same scheme do not force the OS to show a chooser.

Matching semantics

The redirect_uri in the authorization request is compared against usable registered rows:

  1. Exact match first: the whole normalised string must be equal (scheme, host, port, path and trailing slash included).
  2. Loopback port relaxation: when both the registered and requested addresses are loopback hosts (localhost / 127.0.0.1 / [::1]), only the port is ignored; the path must still match. This applies only to web_server, web_client and desktop.
  3. Missing redirect_uri: falls back only when the client has exactly one usable registered row; with multiple rows the request is rejected — multiple rows mean the platform cannot be inferred, and falling back to any of them delivers the code to a guessed address.

Requests must carry redirect_uri

The fallback is only a safety net. In production, send the same redirect_uri explicitly in both the authorization request and the token request; otherwise adding a second callback address later will suddenly break a flow that used to work.

Platform and secret must agree

Only web_server presumes "the client holds a secret"; the other four platforms run on user devices. Therefore:

  • A web_server redirect row cannot sit on a client without a secret (the row claims "I have a server" while the client cannot present a secret).
  • A device-shape redirect row cannot sit on a confidential client (confidential clients must present a secret everywhere, so shipping it in a distributed binary or a browser publishes it).

All usable redirect rows of one client must belong to the same tier. If you need both a server-side and a device-side shape, split into two clients.

Common rejection causes

SymptomCause
Authorization request rejected with an invalid redirect URIAddress not registered; or differs by a trailing slash / port (non-loopback) / host case
invalid_grant at token exchangeThe redirect_uri sent at exchange differs from the one sent in the authorization request
Works locally, fails in productionLocal loopback http happened to match; the production address is not https or lacks a path
Mobile callback does not open the appScheme does not follow the com.muoceon.<bundle-id>://auth convention

On this page