Products and product grants API
Register a product once, grant it to accounts, hand its values to the product team, and rotate its secret. Platform operators call these routes.
Live now · Platform operator only
Every route on this page needs a platform operator. A platform operator is RealmSSO staff. Your own API key cannot call these routes. One read, the handout, can also be made by a product-team role, for one named account.
Only a platform operator can register a product or grant it to an account. To have a product granted to your account, ask RealmSSO. No API grants platform-operator authority.
What it is
A product is an application that a platform operator registers once, so that RealmSSO can grant it to accounts. A product grant lets one account use one product. The product grant is the permission. Registering a product provisions nothing.
A realm is the separate sign-in space that RealmSSO makes for each account. It has its own users, clients, identity-provider links and signing keys. The sign-in service keeps one client for each product grant, in the realm of the account that holds it. Everything below the realm is shared between accounts.
- Default deny. A product's client exists only in the realms of accounts that hold a product grant. A new realm receives only the product grants of its own account. Revoking a product grant removes the client from that realm.
- One client for each product grant. Provisioning a product grant gives the product a SAML client or an OIDC client of its own in that realm. The OIDC client ID is
product-<slug>. No client is shared between accounts. - One binding. The API accepts the
redirectbinding only. - SAML audience. On the SAML leg, the assertion audience is the requesting product only. An automated test covers this. The test runs against the sign-in service, not against a real product.
No screen exists for customers or for product teams. This page is that surface. Platform operators also have the connector board. For the idea behind products, see Identity Gateway. For how accounts and realms relate, see Accounts and isolation.
Who can call it
A platform operator is RealmSSO staff with authority across all accounts. RealmSSO grants this authority outside the API. No API grants it, and no API key can hold it. RealmSSO checks it again on every request, so removing it takes effect on the next call.
| Caller | Result |
|---|---|
| An API key, with any scope | 401 on every route on this page, because the routes accept a dashboard session only. This includes your own keys. |
| A dashboard user who is not a platform operator | 403 on every route on this page. The handout read has one exception, in the next row. |
| A dashboard user with a product-team role | Allowed on GET /api/v1/products/:id/handout for one named account, and nowhere else. |
| A platform operator | Allowed on every route on this page. |
A product-team role belongs to one person and one product. The API calls it idp-admins. It lets that person read the handout for one named account. A platform operator grants and revokes it. A person who is a member of any account cannot hold it, and RealmSSO checks this on every request. No screen manages the role.
The routes
The product routes live under /api/v1/products. The two platform reads live under /api/v1/platform. Every route needs a dashboard session. No route accepts an API key. There are 17 product routes and 2 platform reads.
| Route | Who may call it | What it does |
|---|---|---|
POST /api/v1/products | Platform operator | Registers a product and returns 201. It provisions nothing. Body: name, slug, protocol (oidc, saml or both), and spEntityId, which is required for saml and both. |
GET /api/v1/products | Platform operator | Lists the products. |
GET /api/v1/products/:id | Platform operator | Reads one product. |
PATCH /api/v1/products/:id | Platform operator | Changes name or status (active or inactive). The fields slug, protocol and spEntityId cannot change after registration. |
DELETE /api/v1/products/:id | Platform operator | Removes the product's client from every granted realm, then deletes the product and its product grants. Returns 204. If a client cannot be removed, the product and its product grants stay, and the route returns 502 with DEPROVISION_FAILED. Clients already removed from other realms stay removed. |
POST /api/v1/products/:id/grants | Platform operator | Grants the product to one account. Returns 201 with meta.provisionRequired: true. A product grant does not provision. A provision call creates the client. |
GET /api/v1/products/:id/grants | Platform operator | Lists the product grants of one product. |
PATCH /api/v1/products/:id/grants/:grantId | Platform operator | Changes placement (a label, checked only against consumer), binding, nameIdFormat or acsOrRedirectUris. The fields accountId, protocol and consumer cannot change. meta.reprovisionRequired is true after a change to the URLs or the NameID format. |
DELETE /api/v1/products/:id/grants/:grantId | Platform operator | Revokes a product grant. It removes the client from the account's realm first, then deletes the product grant. Returns 204. If the client cannot be removed, the product grant stays and the route returns 502 with DEPROVISION_FAILED. Retry later. |
POST /api/v1/products/:id/provision | Platform operator | Creates or updates the product's client in every granted realm. Returns 200, or 207 when some realms failed. The body names each realm's outcome. |
POST /api/v1/products/:id/grants/:grantId/provision | Platform operator | Does the same for one product grant. Returns 200, 400 with GRANT_NOT_PROVISIONABLE, or 502 with PROVISION_FAILED. An inactive product, or an account with no realm yet, gets 400. |
GET /api/v1/products/:id/handout | Platform operator, or a product-team role with ?accountId= | Reads the handout. See The handout. |
POST /api/v1/products/:id/handout/secrets | Platform operator | Reads the OIDC client secrets, one for each product grant. See Secrets. |
POST /api/v1/products/:id/grants/:grantId/rotate-secret | Platform operator | Rotates the OIDC secret of one product grant. See Rotating a secret. |
POST /api/v1/products/:id/idp-admins | Platform operator | Gives the product-team role to one person for this product. Body: userId, and an optional note. Returns 409 if the person is a member of an account. |
GET /api/v1/products/:id/idp-admins | Platform operator | Lists the holders of the role. |
DELETE /api/v1/products/:id/idp-admins/:userId | Platform operator | Removes the role. It takes effect on the next request. Returns 204. |
GET /api/v1/platform/accounts | Platform operator | Lists every account with its id, name and slug, and a summary of its connection health. RealmSSO writes one platform audit row for each call. |
GET /api/v1/platform/grants | Platform operator | Reads the product-grant matrix of all accounts, or of one account with ?accountId=. RealmSSO writes one platform audit row for each call. |
Register a product
/api/v1/products{
"name": "Reports",
"slug": "reports",
"protocol": "oidc"
}The slug uses lowercase letters, digits and single hyphens. It becomes part of the OIDC client ID. A slug that exists already returns 409. A SAML client ID is the product's spEntityId, so two products cannot share one.
Grant a product to an account
/api/v1/products/:id/grants{
"accountId": "clx8e1b3c0002efgh",
"protocol": "oidc",
"consumer": "product_idp",
"placement": "private",
"binding": "redirect",
"acsOrRedirectUris": ["<absolute callback URL on the product's sign-in system>"]
}| Field | Notes |
|---|---|
accountId | The account that receives the product grant. One account holds at most one product grant for one product. |
protocol | oidc or saml. It must match the product, unless the product is both. |
consumer | product_idp: the product's own sign-in system trusts the realm and passes the login on. app: the product application trusts the realm directly. |
placement | private, published_login, mesh or direct. A label. The API checks it only against consumer. See Placement. |
binding | Must be redirect. The API accepts no other binding today. |
nameIdFormat | SAML product grants only: persistent (the default) or email. An OIDC product grant that sets it is refused. |
acsOrRedirectUris | One or more absolute URLs: ACS URLs for SAML, redirect URIs for OIDC. A wildcard and embedded credentials are refused. A product_idp OIDC product grant needs at least one callback URL on the product's own sign-in system, with a path shaped like /realms/<realm>/broker/<alias>/endpoint. |
A product that is inactive cannot receive a new product grant, and RealmSSO will not provision it. Its existing product grants stay editable.
Placement
placement is a label. The API checks one thing: whether the value fits consumer. A product_idp consumer fits private, published_login and mesh. An app consumer fits direct only. A pair that does not fit gets 400.
Beyond that check, nothing acts on the value. No setting in the sign-in service changes with it, and RealmSSO does not test which network a browser can reach. The value appears in the product grant, in the handout and in the audit row. Treat it as a note for people, not as a control.
The handout
/api/v1/products/:id/handoutThe handout holds the non-secret values that one product team copies for one account. It returns one entry for each granted account. A platform operator can read all entries, or one entry with ?accountId=. A product-team role must name one account with ?accountId=. A mistyped query parameter gets 400.
| Field | What it holds |
|---|---|
accountId | With grantId and realm: the account, the product grant and the realm name. |
consumer | With binding and placement: copied from the product grant. placement is a label, checked only against consumer. |
status | provisioned, absent or unreachable. RealmSSO asks the sign-in service at the time of the call. This is a live read, not a stored state. unreachable means the sign-in service did not answer. |
registeredUris | The URLs on the product grant. Each has a kind: acs, app_redirect or broker_callback. |
brokerAlias | product_idp product grants only. The alias found in the callback URL. If the URLs hold more than one alias, the entry has brokerAliasCandidates instead. |
discoveryUrl | OIDC product grants. With clientId: the values that the product team copies. |
descriptorUrl | SAML product grants. With idpEntityId and nameIdFormat: the descriptor URL that the product polls, the realm's entity ID, and the NameID format. |
secretRotatedAt | When a platform operator last rotated the secret of this product grant. It is null if no rotation has run. |
The handout holds no secret. For an OIDC product, the client secret is a separate read that only a platform operator can make.
Every handout read writes an audit row in the audit log of each account it lists. A read that names one account with no product grant also writes a row, so an empty read leaves a record. The row says which authority admitted the call. Audit writes are best effort. See the Audit log page.
| Refusal | Code |
|---|---|
| The caller holds neither authority | 403 PRODUCT_HANDOUT_FORBIDDEN |
| The caller holds the product-team role and is also a member of an account | 403 PRODUCT_IDP_ADMIN_MEMBERSHIP_CONFLICT |
| A product-team role named no account | 400 PRODUCT_HANDOUT_ACCOUNT_REQUIRED |
Secrets
/api/v1/products/:id/handout/secretsOnly a platform operator can call this route. It returns one client secret for each OIDC product grant, keyed by grantId and realm. A product has no single secret, because each granted realm has its own client. Add ?accountId= to read one account. A SAML product has no secret, so the route refuses it with 400.
The route is a POST on purpose. The response holds live credentials, and a GET could put them in browser history and proxy logs. RealmSSO stores no product secret. The sign-in service holds each secret, and RealmSSO reads it from there on each call. An entry that cannot be read has clientSecret: null and an error.
Every secret read writes an audit row with severity warn in the account's audit log. The row never holds the secret.
Rotating a secret
/api/v1/products/:id/grants/:grantId/rotate-secret{ "note": "CHG-1042: planned rotation with the product team" }- Who. Only a platform operator. A product-team role cannot rotate.
- Scope. One product grant, in one account and one realm. No product-wide rotation exists.
- Protocol. OIDC product grants only. A SAML product grant has no secret, and the route refuses it with
400. - The note.
noteis required and has 1 to 500 characters. Use a change reference. Never write a secret in it. RealmSSO records it as written in the audit row. - Cutover. The rotation is designed as an immediate cutover with no overlap. The old secret is designed to stop working when the sign-in service accepts the rotation. This is reasoned from the sign-in service's documentation. It is not measured.
- Effect. New logins to that product in that account are designed to fail until the product team installs the new secret. Agree a time with the product team first.
- No rollback. The old secret cannot be recovered. A platform operator can read the current secret again through the secrets route. A lost new value needs no second rotation.
- Record. RealmSSO writes an audit row with severity
warnthat holds the note and the time. It setssecretRotatedAton the product grant. The response returns the newclientSecret, and RealmSSO does not store it.
The connector board
The connector board is a dashboard page for platform operators. It shows accounts against products. A platform operator can use it to register a product and to grant a product to an account. It can also provision, revoke, and show the handout values of one product grant. It can rename, deactivate or delete a product.
The board does not rotate a secret, show a secret, or manage the product-team role. Use the routes above for those. A customer has no screen, and a product team has no screen.
What is not built
| Item | Today | Detail |
|---|---|---|
| Signed SAML requests from products | Building now | RealmSSO is designed to require signed requests from a product before any SAML pilot runs. Today the sign-in service accepts unsigned SAML requests from a granted product. |
| Device credentials for mesh devices | Building now | RealmSSO is designed to issue a device credential for mesh devices. Today mesh is only a label value of placement. |
The authenticator_spi binding | Building now | It is designed and is not built, so the API refuses it. Use the redirect binding. |
| A screen for customers or product teams | None exists. | This page and the connector board are the whole surface. |
| Self-service for your own staff | None exists. | Your staff cannot register a product, grant it or read its secrets. Only a platform operator can. |
| A login carried end to end | None yet. | No login has yet been carried end to end through the gateway. |