Live demo
See OpsAPI working end to end
A real OpsAPI deployment with every app switched on and filled with sample data, single sign-on through a SAML identity provider, and five apps in five languages (Java, PHP, Python, Rust and Go) that share one sign-in. Look around the dashboard with a read-only account or SSO, then sign in to the apps.
Demo login
Use this on the dashboard, or on OpsAPI's sign-in page when an app sends you there.
- demo@opsapi.org
- Password
- OpsAPI-Demo-2026!
Read-only, and reset every 30 minutes. Every page opens, every change is refused. The account lives in a showcase workspace whose other users are fictional, so you never see a real person's details.
What's running
| Host | What it is |
|---|---|
| showcase.demo.opsapi.org | Admin dashboard: every app, with sample data |
| showcase-api.demo.opsapi.org | OpsAPI API behind it (every module on) |
| saml-idp.demo.opsapi.org | Demo SAML identity provider (Mock SAML) |
| identity.demo.opsapi.org | OpsAPI sign-in for the five apps |
| hello-java.demo.opsapi.org | Java demo app |
| hello-php.demo.opsapi.org | PHP demo app |
| hello-python.demo.opsapi.org | Python demo app |
| hello-rust.demo.opsapi.org | Rust demo app |
| hello-go.demo.opsapi.org | Go demo app |
How the demo fits together
Under the hood
How each demo signs you in
Four ways in, all ending the same way: OpsAPI issues a short-lived JWT for one workspace, and every API call is checked against that workspace's roles. The diagrams follow the real requests, step by step.
| Method | Where to try it | Who checks the user | What you get |
|---|---|---|---|
| Email and password | Full dashboard, demo login | OpsAPI (bcrypt, optional emailed code) | JWT + refresh cookie |
| The five apps' sign-in page | Google, verified email only | JWT on the callback | |
| Login with OpsAPI | The five apps | OpsAPI hosted page; app checks a nonce, then /auth/me | App session + JWT |
| SAML single sign-on | Full dashboard, Sign in with SSO | The workspace's IdP; OpsAPI verifies the signature | One-time code → JWT |
Flow 1 · showcase.demo.opsapi.org, with the demo login
Email and password (+ emailed code)
The dashboard posts the password to OpsAPI. OpsAPI checks it, asks for an emailed code when the workspace requires one, and returns a short-lived JWT plus a refresh token in an HttpOnly cookie. Every API call then carries the JWT and the workspace, and the role decides read or write.
| Concern | How it works |
|---|---|
| Where the password lives | Only OpsAPI sees it; bcrypt hashes in users |
| Second factor | Emailed code, per workspace setting (off in showcase, nobody reads the demo inbox) |
| Session | JWT in the Authorization header; refresh token only in an HttpOnly cookie |
| What the demo login can do | Read everything in showcase; every change is refused with 403 |
Flow 2 · the five apps' sign-in page (identity.demo.opsapi.org)
Google sign-in (OAuth 2.0 / OpenID Connect)
OpsAPI runs the whole OAuth exchange: the dashboard only sends the browser to /auth/google. A nonce rides in both the state and a cookie so nobody can finish their own Google sign-in in your browser. Only an email Google has verified can sign in or be linked.
| Concern | How it works |
|---|---|
| Who talks to Google | Only OpsAPI; apps never hold a Google client secret |
| Login CSRF | Nonce in state must equal the nonce cookie |
| Account linking | By email, and only when Google says the email is verified |
| Where tokens may go | Only origins on the CORS / allowed-origins list |
Flow 3 · hello-java, hello-php, hello-python, hello-rust, hello-go
Login with OpsAPI (hosted sign-in for your apps)
Your app never sees a password. It sends the browser to OpsAPI's hosted page with a nonce, gets a JWT back on its callback, checks the nonce, and asks /auth/me who the user is. A second app finds the OpsAPI session already there and gets its token without a prompt.
| Concern | How it works |
|---|---|
| What the app stores | Its own session and the JWT; never a password |
| How it checks the token | One call to /auth/me; the signing key never leaves OpsAPI |
| Forged callback links | Refused: the nonce must match the session |
| Single sign-on across apps | One OpsAPI session, a fresh token per app |
Flow 4 · showcase.demo.opsapi.org → Sign in with SSO, with the demo IdP
SAML 2.0 single sign-on
The workspace's own identity provider vouches for the user. OpsAPI sends a SAML AuthnRequest, the IdP posts back a signed response, and OpsAPI checks the signature against the certificate saved for that workspace before anything else. The dashboard then swaps a one-time code for the JWT, so the token never appears in a URL.
| Concern | How it works |
|---|---|
| Trust | The IdP's certificate saved per workspace; certificates inside messages are ignored |
| Refused | Tampered, wrapped, unsigned, replayed, expired or misaddressed responses; DTDs; SHA-1 |
| Scope of trust | An IdP vouches for its own workspace and email domains only |
| Demo IdP | Mock SAML: no password, so only the read-only showcase trusts it |
Tour the admin dashboard
- Open showcase.demo.opsapi.org and sign in with the demo login. No emailed code is needed in this workspace.
- Apps: the catalog of every module this deployment runs; open CRM, invoices, projects, the shop, care home, hiring and more, each with sample records.
- Users: see the sample team and each person's role: owner, admin, developer, member.
- Roles: open a role to see its
module.actionpermissions, and the read-only viewer role you're signed in with. - Workspace and settings: see how a tenant is configured, including whether members need an emailed code, and the Single sign-on page with the workspace's SAML identity provider.
- Try to change something. The API refuses it with 403, which is what a viewer should get.
Sign in to the apps
- Open hello-java and choose “Login with OpsAPI”. The app sends you to OpsAPI's sign-in page.
- Sign in with the demo login, or with your own Google account. Google sign-ins join the private demo workspace as a member.
- You come back signed in. The app asked OpsAPI who you are with one call to
/auth/meand shows your workspace and role. - Open any of the others (PHP, Python, Rust, Go) and log in there too: same account, no second prompt.
- Sign out everywhere from either app; the next sign-in asks again.
Sign in with SAML single sign-on
The showcase workspace is connected to a demo SAML 2.0 identity provider. It stands in for your company's Okta, Entra ID or Google Workspace, but asks for no password: any address at example.com or example.org works.
- Open the dashboard sign-in page and choose Sign in with SSO.
- Enter a work email such as
visitor@example.comand press Continue. OpsAPI finds the workspace's provider from the domain. - You land on the demo IdP at
saml-idp.demo.opsapi.org. Press Sign In. - The IdP posts a signed SAML response back to OpsAPI, which checks the signature, audience, time window and request, then signs you in.
- You are in the showcase as a read-only viewer, under the address you typed. Open Single sign-on in the sidebar to see how the provider is set up.
Same rules as production. The demo IdP is trusted by the showcase workspace only, for its two domains only. A tampered, replayed, unsigned or misaddressed response is refused, and so is an account that belongs to another workspace. Details in the SAML technote.
SAML endpoints
| Endpoint | What it does |
|---|---|
GET /auth/saml/discover?email= | Finds the provider for an email domain |
GET /auth/saml/:id/metadata | Service-provider metadata to give your IdP |
GET /auth/saml/:id/login | Starts sign-in: redirects to the IdP |
POST /auth/saml/:id/acs | Receives the signed response, signs you in |
/api/v2/sso/saml/providers | Workspace owners add and edit providers |
# which provider handles example.com?
curl -s "https://showcase-api.demo.opsapi.org/auth/saml/discover?email=visitor@example.com"
# the demo IdP's metadata (entity ID, sign-in URL, certificate)
curl -s https://saml-idp.demo.opsapi.org/api/saml/metadataTo connect your own IdP, sign in to your workspace, open Workspace → Single sign-on, paste the IdP metadata, then give the IdP the Entity ID and ACS URL shown there.
The five apps
| App | Stack | Signed-in pages | Code |
|---|---|---|---|
| hello-java | Spring Boot | /api/secure/me, /api/secure/resource | source |
| hello-php | PHP 8.3 | /dashboard | source |
| hello-python | Python 3.13, standard library | /dashboard | source |
| hello-rust | Rust, tiny_http + ureq | /dashboard | source |
| hello-go | Go, standard library | /dashboard | source |
All five use the same routes (/login, /auth/callback, /logout), check the token with GET /auth/me, and refuse a forged callback with 400.
Each app keeps a random nonce in its session when you click log in and only accepts the token back if the nonce matches, so a link carrying someone else's token can't sign you in. The full sequence is in the sign-in flow technote, and how the public login is kept safe is in building a safe public demo login.
Try the API directly
# sign in as the demo account (form-encoded)
curl -s -X POST https://identity.demo.opsapi.org/auth/login \
--data-urlencode username=demo@opsapi.org \
--data-urlencode 'password=OpsAPI-Demo-2026!'
# who am I?
curl -s https://identity.demo.opsapi.org/auth/me -H "Authorization: Bearer $TOKEN"
# the showcase team (read access)
curl -s https://identity.demo.opsapi.org/api/v2/namespace/members \
-H "Authorization: Bearer $TOKEN" -H "X-Namespace-Slug: showcase"
# a change is refused (403)
curl -s -X POST https://identity.demo.opsapi.org/api/v2/users -H "Authorization: Bearer $TOKEN" \
-H "X-Namespace-Slug: showcase" --data-urlencode email=new@example.comidentity.demo.opsapi.org runs a minimal core_only build (sign-in, users, workspaces and roles) for the five apps; it has its own dashboard at dashboard.demo.opsapi.org. Every business module, with sample data, is on the showcase.