Skip to content

Add sign-in to your site

The common case. Your site redirects to the provider, the person signs in there, and they come back with a token that says who they are.

You do not integrate Omxus. You configure an OpenID Connect provider that happens to be Omxus — so if your framework already has a "Sign in with Google" path, this is the same path with a different URL.

The live provider is https://auth.omxus.com.

1. Register your site

A client is the provider's record of your site. Make one yourself at auth.omxus.com/developers, or by API: Registering a site. Settle two things first:

  • Redirect URI — exactly where you want people sent back to, character for character. https://example.com/callback does not match https://example.com/callback/, and that is deliberate: a loose match is how someone else's URL gets into the list.
  • Whether your code runs in a browser. If it does, you are a public client: you get no client secret, because a browser cannot keep one. If it runs on a server, you are confidential and you get a secret.

2. Point your library at the issuer

Every OIDC library takes one URL and reads the rest from /.well-known/openid-configuration.

Ask for the profile scope, and keep PKCE on: it is required for every client, with or without a secret.

// Auth.js (NextAuth v5)
providers: [{
  id: 'omxus', name: 'Omxus', type: 'oidc',
  issuer: 'https://auth.omxus.com',
  clientId: process.env.OMXUS_CLIENT_ID,
  clientSecret: process.env.OMXUS_CLIENT_SECRET,
  checks: ['pkce', 'state'],
  authorization: { params: { scope: 'openid profile' } },
}]

Untested: not run against a Next.js app. A passport sample that was run against auth.omxus.com is in the Quickstart.

More framework snippets — openid-client, Authlib, Spring, Rails — are in Any app, via OIDC.

3. If your code runs in a browser

A browser cannot keep a secret, so register a public client (no secret) and do the code exchange in the page. omxus-oidc.mjs does all of it:

Not served yet

omxus-oidc.mjs answers 404 at auth.omxus.com/sdk/ (checked 30 Oct 2026) and @omxus/auth is not on npm. Until it is served, use any browser OIDC library that does the code flow with PKCE (S256).

import { createOidcClient } from 'https://auth.omxus.com/sdk/omxus-oidc.mjs';

const auth = createOidcClient({
  issuer: 'https://auth.omxus.com',
  clientId: 'my-site',
  redirectUri: location.origin + '/callback',
});

myOwnButton.onclick = () => auth.signIn();        // your button, your styling
const { claims } = await auth.handleCallback();   // on the callback page

It makes the PKCE verifier, state and nonce, parks them in sessionStorage, exchanges the code and checks the ID token's signature, issuer, audience, expiry and nonce. The scope is openid profile unless you pass scope. It renders nothing, so the button is yours.

Two more ways in, same client:

  • auth.signInPopup(): a small window; the page underneath never leaves. Call it from the click itself. The callback page calls finishPopup(). A blocked popup falls back to a redirect.
  • auth.signInSilently(): on page load, when someone is already signed in to Omxus, they arrive signed in here with no screen. A miss throws an error with silent: true; show nothing. More: Signed in everywhere.

signIn() was run in Node against the live discovery document (30 Oct 2026): it builds a code + S256 + state + nonce request to /oidc/authorize. The callback half is untested from outside the auth repo.

4. What you get back

{
  "iss": "https://auth.omxus.com",
  "sub": "23cf34129785242b1ea072ec3669a3af6af2528b",
  "aud": "q3V8n0bX1kZ2mH7cT9wLpA",
  "iat": 1793318400,
  "exp": 1793322000,
  "auth_time": 1793318395,
  "omxus_address": "0xc6140a760bfbc99428a8e53a08546657fad14c76"
}

sub is the person, as far as your site is concerned. It is stable for them at your site and different at every other site, so it is a good primary key and worthless to anyone who obtains it.

omxus_address comes only with the profile scope. It is the same at every site, so never key your users on it. On the standard door, profile also brings preferred_username, name and picture, and email brings the email address. Every claim: Profiles and claims.

Limits today

  • Refresh tokens on the standard door only. On the other doors ID and access tokens last an hour with no renewal. For a site that uses OIDC to start its own session (the normal case) this does not matter.
  • No scope-by-scope consent screen. A registered client gets the claims for the scopes it asks for. On the standard door a person's first visit to a site needs one press on "Choose an account"; until then prompt=none answers consent_required. The operator's own first-party sites skip it.
  • No back-channel logout. Ending the provider session does not notify your site.