Skip to content

Registering a site

Before anyone can sign in to your site, the provider needs to know it: its name, and the exact addresses people are sent back to. That record is a client, and you make one yourself. No email, no waiting on anyone.

Two ways, same result:

  • The form. Open auth.omxus.com/developers, fill in the name and redirect URIs, and leave with a client_id and snippets to paste.
  • The API. POST /oidc/register, standard OIDC Dynamic Client Registration (RFC 7591). The form calls this.

A client on its own is worth almost nothing: it signs nobody in and reads no claim. Every sign-in still needs a real person at the sign-in page, an exact-match redirect URI and PKCE. That is why registration can be open.

The request

curl -s https://auth.omxus.com/oidc/register \
  -H 'content-type: application/json' \
  -d '{
    "client_name": "Vex",
    "redirect_uris": ["https://vex.example/callback"]
  }'

Untested against production on purpose (each call makes a real client). The same call with an empty body was run on 30 Oct 2026 and returned the 400 shown under Errors.

Field Required Values
client_name yes 1 to 80 characters, shown on the sign-in page
redirect_uris yes array of 1 to 10 URIs, see Redirect URIs
token_endpoint_auth_method no none (default), client_secret_post, client_secret_basic
door no minimal (default), full, standard, see Doors
username_scope standard door only shared (default), site
theme standard door only omxus (default), horror, gold, tome
sector_identifier_uri no see One person, several apps

The answer

201 Created (the client_id here is made up):

{
  "client_id": "q3V8n0bX1kZ2mH7cT9wLpA",
  "client_name": "Vex",
  "redirect_uris": ["https://vex.example/callback"],
  "token_endpoint_auth_method": "none",
  "door": "minimal",
  "grant_types": ["authorization_code"],
  "response_types": ["code"]
}
  • client_id is 22 random characters (16 bytes, base64url). It carries no part of your site name, so nobody can guess another site's id.
  • A standard-door client also gets refresh_token in grant_types, and its username_scope and theme back.
  • A confidential client (client_secret_post or client_secret_basic) gets a client_secret. It is shown once. The provider keeps only its SHA-256, so nobody, including the provider, can read it back. Lose it and register again.

Public or confidential

If your code runs in a browser or an app on someone's phone, it cannot keep a secret: register with none (the default). PKCE is required for every client here, confidential ones too, so a public client is not a weaker one.

If your code runs only on your server, you may take a secret and send it to the token endpoint in the body (client_secret_post) or as HTTP Basic (client_secret_basic).

Redirect URIs

Matched character for character at sign-in. https://vex.example/callback does not match https://vex.example/callback/.

Allowed:

  • https:// anywhere;
  • http:// on localhost, 127.0.0.1 or [::1] only, for development;
  • a native app's reverse-DNS scheme, such as com.example.app:/callback (RFC 8252). Public clients only: an app binary cannot keep a secret.

Refused: a fragment (#…), a relative URI, and any other scheme (javascript:, data:, file:, a bare app name).

Names

client_name is shown to people at the moment they trust the page, so it is checked:

  • it must not contain "omxus" or "auth", including lookalike spellings (Cyrillic letters, 0 for o, rn for m);
  • it must not be a URL;
  • if it looks like a domain, that domain must be one of your redirect hosts. vex.example is fine for a client redirecting to vex.example; bank.example is not.

Doors

The door is the sign-in screen your people see.

  • minimal (default): email and password first, everything else folded behind one line.
  • full: the whole Omxus app.
  • standard: ordinary accounts. Email or mobile, a username, a password or passkey, on a plain page or on your own page. One account works on every standard-door site. username_scope: "site" gives your site its own usernames; shared uses the one name across all shared sites. theme picks the look.

username_scope and theme sent with any other door are refused.

One person, several apps

By default a person's sub is different at every site (see How identity is derived). A website and its iOS app usually want the same sub. Both declare the same sector_identifier_uri: an https URL on one of your redirect hosts (or their registrable domain) serving a JSON array of every redirect URI.

The provider does not fetch it at registration. The answer carries "sector_identifier_status": "pending_operator_approval", and the client cannot sign anyone in (unauthorized_client) until the provider's operator approves it (Operator routes). When they do, the file must answer 200 within 5 seconds, be 64 KB or less, follow no redirects, and list every redirect URI you registered.

Limits

Ten registrations an hour from one address. Malformed requests count too.

Errors

Every error is { "error": "...", "error_description": "..." }.

Status error When
400 invalid_client_metadata no JSON body; bad name, auth method, door, username_scope, theme or sector URI
400 invalid_redirect_uri not 1 to 10 URIs, or one breaks the rules above
429 too_many_requests eleventh registration within the hour
{
  "error": "invalid_client_metadata",
  "error_description": "client_name must be 1 to 80 characters"
}

Next

Point your library at https://auth.omxus.com: Add sign-in to a site, or Any app, via OIDC for the full flow.