Skip to content

Layer 3 — Return rules

View as Markdown

Layer 3 controls how an authentication result reaches the application. It is checked twice: once at /establish (against each return method declared on the request) and again at runtime when the chosen method actually runs (for example on /status-poll, or inside the OIDC /token exchange).

MethodPayload (per inquiry)Used for
CALLBACK{ "callbackUrl": "https://..." }Web applications — the browser is redirected to a URL on the application after authentication.
STATUS_POLL{}Native clients — the client polls connect POST /status-poll and is told when the inquiry is realized.
REVEAL{}Developer / CLI / manual integration — the access and/or refresh tokens are shown directly on via.sudomimus.com after login (masked, with a “Reveal” button) so the user can copy them out by hand. No application sits on the other end.
DIRECT_ISSUE{}Opts the application in to one-shot native-api issuance — Steam ticket (POST /direct-issue/steam-ticket) or AccessKey (POST /direct-issue/access-key). Tokens are minted in the same request that authenticates; no /establish or /redeem is involved.
OIDC(not declared per inquiry)Enables this application to be used as an OIDC relying party via oidc.sudomimus.com. Required for the OIDC authorization_code + PKCE flow.

A CALLBACK rule on the application carries the allowed hostnames for the callback URL — not the URL itself. The actual URL is supplied per-inquiry on /establish; the rule decides whether that URL’s hostname is acceptable.

{
"returnMethod": "CALLBACK",
"payload": {
"allowedCallbackDomains": ["client.example.com", "admin.example.com"]
},
"accessTokenTtlSeconds": null,
"refreshTokenTtlSeconds": null
}

Hostname comparison is exact, case-insensitiveclient.example.com does not implicitly cover sub.client.example.com. To allow both, list both.

STATUS_POLL rules have no payload — having the rule on the application is the whole configuration.

{
"returnMethod": "STATUS_POLL",
"payload": {},
"accessTokenTtlSeconds": null,
"refreshTokenTtlSeconds": null
}

REVEAL rules configure which tokens are shown to the user after a successful login. At least one of includeAccessToken and includeRefreshToken must be true.

{
"returnMethod": "REVEAL",
"payload": {
"includeAccessToken": true,
"includeRefreshToken": true
},
"accessTokenTtlSeconds": null,
"refreshTokenTtlSeconds": null
}

When an inquiry declares REVEAL, Sudomimus signs the tokens at realize time and returns them inline; the inquiry is marked redeemed immediately, so any subsequent /redeem call for the same login fails with InquiryAlreadyRedeemed. Multiple matching REVEAL rules are combined with OR semantics — if any matching rule allows the access token, it is included; same for the refresh token.

REVEAL takes precedence on the via.sudomimus.com page: it suppresses the automatic CALLBACK redirect so the user can copy the tokens first. The user then sees a “Continue to app” button if a CALLBACK was also declared. STATUS_POLL still signals realized to a polling client, but the subsequent /redeem will fail because REVEAL has already redeemed the inquiry.

DIRECT_ISSUE rules have no payload — the rule’s presence is the entire configuration. Add this rule to opt an application in to the native-api direct-issue endpoints (Steam ticket and AccessKey).

{
"returnMethod": "DIRECT_ISSUE",
"payload": {},
"accessTokenTtlSeconds": null,
"refreshTokenTtlSeconds": null
}

DIRECT_ISSUE does not appear in /establish returnMethods declarations — those endpoints have no /establish step. Layer 3 is still checked at runtime inside the native-api handlers.

OIDC rules opt the application in to being used as an OpenID Connect relying party via oidc.sudomimus.com. The payload carries the standard OIDC client configuration:

{
"returnMethod": "OIDC",
"payload": {
"redirectUris": ["https://app.example.com/oidc/callback"],
"postLogoutRedirectUris": ["https://app.example.com/"],
"allowedScopes": ["openid", "email", "profile", "offline_access"],
"tokenEndpointAuthMethod": "private_key_jwt"
},
"accessTokenTtlSeconds": null,
"refreshTokenTtlSeconds": null
}
  • redirectUris — full-URI exact match. The redirect_uri parameter the RP sends to /authorize must equal one of these strings byte-for-byte. No prefix or wildcard matching.
  • postLogoutRedirectUris — full-URI exact match, used by /end-session.
  • allowedScopes — the set of OIDC scopes this client is permitted to request. openid is always required; email, profile, and offline_access are supported. Requesting a scope outside this list fails at /authorize.
  • tokenEndpointAuthMethod — one of "private_key_jwt" (confidential client; signs a JWT assertion to authenticate at /token), "client_secret_basic" (confidential client; presents a shared secret in the HTTP Authorization: Basic header), "client_secret_post" (confidential client; sends a shared secret in the /token form body), or "none" (public client; PKCE is required for these).

OIDC rules are not declared per-inquiry on /establish — the OIDC flow has its own /authorize endpoint instead.

End-to-end usage and library setup is in OIDC relying parties.

The returnMethods field on /establish plays two roles at once: it declares which methods will be used for this inquiry and it carries the concrete callbackUrl (which the application’s rules cannot know in advance).

{
"applicationAnchor": "my-app",
"returnMethods": [
{
"type": "CALLBACK",
"payload": { "callbackUrl": "https://client.example.com/auth/return" }
},
{
"type": "STATUS_POLL",
"payload": {}
},
{
"type": "REVEAL",
"payload": {}
}
]
}

DIRECT_ISSUE and OIDC are not declared on /establish — those flows do not pass through it.

  • Field absent → no Layer 3 narrowing; the application’s rules still apply at runtime.
  • Field present and empty array → rejected.
  • Field present and non-empty → every entry is validated against the application’s Layer 3 rules at /establish time. For CALLBACK, the URL’s hostname must match an entry in some Layer 3 CALLBACK rule’s allowedCallbackDomains. For STATUS_POLL and REVEAL, a Layer 3 rule of the same method must exist on the application.

The application has one Layer 3 rule: CALLBACK with allowedCallbackDomains: ["client.example.com"].

Inquiry callbackUrlResult
https://client.example.com/returnaccepted
https://Client.Example.Com/returnaccepted (case-insensitive)
https://sub.client.example.com/returnrejected (no implicit subdomain match)
https://attacker.com/?redirect=client.example.comrejected (hostname is attacker.com)

For STATUS_POLL, the runtime check on /status-poll re-verifies both that the application still has a STATUS_POLL rule and that the inquiry’s per-inquiry narrowing (if any) still allows it.

The check matches only the hostname, not the path or query string. Sudomimus does not own the application’s URL structure and would create a maintenance burden by requiring exact-path allowlists. Application owners are expected to keep their callback handlers safe at the routing level — Sudomimus restricts the origin the browser is sent to.

(OIDC redirectUris are a different model — they require full-URI exact match because the OIDC standard demands it and because most OIDC libraries already produce a single fixed redirect URI per client.)