Skip to content
← Documentation

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 redirect binding 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.

CallerResult
An API key, with any scope401 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 operator403 on every route on this page. The handout read has one exception, in the next row.
A dashboard user with a product-team roleAllowed on GET /api/v1/products/:id/handout for one named account, and nowhere else.
A platform operatorAllowed 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.

RouteWho may call itWhat it does
POST /api/v1/productsPlatform operatorRegisters 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/productsPlatform operatorLists the products.
GET /api/v1/products/:idPlatform operatorReads one product.
PATCH /api/v1/products/:idPlatform operatorChanges name or status (active or inactive). The fields slug, protocol and spEntityId cannot change after registration.
DELETE /api/v1/products/:idPlatform operatorRemoves 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/grantsPlatform operatorGrants 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/grantsPlatform operatorLists the product grants of one product.
PATCH /api/v1/products/:id/grants/:grantIdPlatform operatorChanges 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/:grantIdPlatform operatorRevokes 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/provisionPlatform operatorCreates 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/provisionPlatform operatorDoes 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/handoutPlatform operator, or a product-team role with ?accountId=Reads the handout. See The handout.
POST /api/v1/products/:id/handout/secretsPlatform operatorReads the OIDC client secrets, one for each product grant. See Secrets.
POST /api/v1/products/:id/grants/:grantId/rotate-secretPlatform operatorRotates the OIDC secret of one product grant. See Rotating a secret.
POST /api/v1/products/:id/idp-adminsPlatform operatorGives 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-adminsPlatform operatorLists the holders of the role.
DELETE /api/v1/products/:id/idp-admins/:userIdPlatform operatorRemoves the role. It takes effect on the next request. Returns 204.
GET /api/v1/platform/accountsPlatform operatorLists 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/grantsPlatform operatorReads 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

POST/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

POST/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>"]
}
FieldNotes
accountIdThe account that receives the product grant. One account holds at most one product grant for one product.
protocoloidc or saml. It must match the product, unless the product is both.
consumerproduct_idp: the product's own sign-in system trusts the realm and passes the login on. app: the product application trusts the realm directly.
placementprivate, published_login, mesh or direct. A label. The API checks it only against consumer. See Placement.
bindingMust be redirect. The API accepts no other binding today.
nameIdFormatSAML product grants only: persistent (the default) or email. An OIDC product grant that sets it is refused.
acsOrRedirectUrisOne 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

GET/api/v1/products/:id/handout

The 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.

FieldWhat it holds
accountIdWith grantId and realm: the account, the product grant and the realm name.
consumerWith binding and placement: copied from the product grant. placement is a label, checked only against consumer.
statusprovisioned, 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.
registeredUrisThe URLs on the product grant. Each has a kind: acs, app_redirect or broker_callback.
brokerAliasproduct_idp product grants only. The alias found in the callback URL. If the URLs hold more than one alias, the entry has brokerAliasCandidates instead.
discoveryUrlOIDC product grants. With clientId: the values that the product team copies.
descriptorUrlSAML product grants. With idpEntityId and nameIdFormat: the descriptor URL that the product polls, the realm's entity ID, and the NameID format.
secretRotatedAtWhen 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.

RefusalCode
The caller holds neither authority403 PRODUCT_HANDOUT_FORBIDDEN
The caller holds the product-team role and is also a member of an account403 PRODUCT_IDP_ADMIN_MEMBERSHIP_CONFLICT
A product-team role named no account400 PRODUCT_HANDOUT_ACCOUNT_REQUIRED

Secrets

POST/api/v1/products/:id/handout/secrets

Only 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

POST/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. note is 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 warn that holds the note and the time. It sets secretRotatedAt on the product grant. The response returns the new clientSecret, 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

ItemTodayDetail
Signed SAML requests from productsBuilding nowRealmSSO 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 devicesBuilding nowRealmSSO is designed to issue a device credential for mesh devices. Today mesh is only a label value of placement.
The authenticator_spi bindingBuilding nowIt is designed and is not built, so the API refuses it. Use the redirect binding.
A screen for customers or product teamsNone exists.This page and the connector board are the whole surface.
Self-service for your own staffNone exists.Your staff cannot register a product, grant it or read its secrets. Only a platform operator can.
A login carried end to endNone yet.No login has yet been carried end to end through the gateway.