Skip to content
← Documentation

Connections

A connection is configuration, never a session. This page explains the concept first and then the routes.

A connection links one account to one identity provider (IdP) of your customer. The IdP is a SAML 2.0 or OIDC provider. A connection is configuration. It is never a session. Not yet tested against Entra ID, Okta, ADFS or Google.

A realm is the separate sign-in space of one account. RealmSSO creates one entry in the realm for each connection. The entry links the realm to your customer's IdP. RealmSSO creates the entry switched off. It switches the entry on only after every step has finished.

The entry has an alias. The alias is saml-<connection-id> for SAML and oidc-<connection-id> for OIDC. It is opaque. It holds no account slug.

Four words

These four words mean the same on every page about connections. A status change acts on the second word only. The API uses the second word too: the brokering report and the code CONNECTION_NOT_BROKERING. See Connection status.

WordMeaning
ConfigurationThe connection record and its entry in the realm. Create, edit and delete change configuration.
Brokering new loginsThe realm accepts a new login through the connection and passes the sign-in through to your customer’s IdP.
Existing sessionA user signed in through the connection earlier and still holds a session in the realm.
In-flight loginA login that has started and has not finished.

Create a connection

You have four ways to create a connection. The last column says where each way is reached.

WayYou sendReached from
SAML from a metadata URLmetadataUrlAPI, dashboard and Admin Portal
SAML from pasted metadata XMLmetadataXmlAPI only
SAML from three manual valuesidpEntityId, idpSsoUrl and idpCertificateAPI and Admin Portal
OIDC from a discovery URLclientId, clientSecret and discoveryUrlAPI and dashboard

The dashboard form takes a metadata URL for SAML. It has no fields for the three manual values. It also shows optional ACS URL and entity ID fields. Leave the ACS URL field empty. In the Admin Portal an IT admin can set up a SAML connection. The OIDC tab of the portal does not work today. Your team sets up OIDC in the dashboard or the API.

SAML from a metadata URL

Send metadataUrl. The sign-in service reads the metadata at that URL and creates the entry. The URL must point to a public address. RealmSSO does not follow redirects. The metadata must hold a signing certificate. If it holds none, RealmSSO sets up no identity provider from it. The request does not return 400: a create returns 201 with the connection in status error and the reason in metadata.provisionError (see When RealmSSO cannot create the entry).

SAML from pasted metadata XML

Send the XML in metadataXml. API only. No screen takes XML, and there is no file upload. The XML must hold an entity ID, a single sign-on endpoint and a signing certificate. The endpoint must use the HTTP-POST or the HTTP-Redirect binding.

If the XML lacks one of these, the request returns 400. It also returns 400 when the endpoint is not a public address. No connection exists then. A connection made from XML has no metadata URL. The on-demand check does not contact the IdP for it.

SAML from three manual values

Send idpEntityId, idpSsoUrl and idpCertificate. All three are required. They describe your customer's IdP. RealmSSO generates the ACS URL and the SP entity ID. Your customer does not enter them here.

OIDC from a discovery URL

Send clientId, clientSecret and discoveryUrl. The sign-in service reads the endpoints from the discovery document. The URL must point to a public address.

When RealmSSO cannot create the entry

A request can pass validation and still fail at the sign-in service. The response is then 201 and the connection has the status error. The metadata field holds the reason in provisionError. A discovery document without an authorization endpoint or a token endpoint ends this way. Metadata at a metadataUrl with no signing certificate ends this way too. A connection in error follows the error policy of the account. See Connection status.

The three values your customer registers

For a SAML connection, your customer registers three values in their IdP. They are the ACS URL, the SP entity ID and the SP descriptor URL. <sign-in-origin> is the scheme and host of the sign-in service. Per-customer hostnames are switched off in production, so the origin is the shared host of the sign-in service.

ValueShape
ACS URL<sign-in-origin>/realms/kr-<account-id>/broker/saml-<connection-id>/endpoint
SP entity IDThe origin only: <sign-in-origin>
SP descriptor URLThe ACS URL followed by /descriptor

The realm name kr-<account-id> comes from the account id. It holds no customer name. For an OIDC connection your customer registers one redirect URI: <sign-in-origin>/realms/kr-<account-id>/broker/oidc-<connection-id>/endpoint. The sign-in service exchanges the code. The RealmSSO API is not on the login path. The sign-in service also stores the tokens that your customer's IdP returns at sign-in.

You can read the values in four places. The create response holds ssoConfig. The route GET /api/v1/sso/accounts/:id/config returns customerSetup for each connection, with acsUrl, spEntityId and spMetadataUrl. The dashboard shows them on the SSO client card of the account. The Admin Portal shows the ACS URL and entity ID only after the IT admin saves the IdP details. If you set an acsUrl override,ssoConfig and the Admin Portal show that override instead of the real ACS URL.

Check a connection on demand

POST /api/v1/connections/:id/test checks one connection when you call it. No schedule runs it. The check never performs a login. A passing check does not prove that a login works.

ConnectionWhat the check does
Every connectionConfirms that the entry exists in the realm and is switched on.
SAML with a metadata URLReads the URL again and compares its entity ID with the entry. The read uses a public address, follows no redirects and stops after 8 seconds.
SAML from XML or manual valuesReports reachable: true without contact with the IdP.
OIDCReads the discovery URL again and compares its issuer with the entry.

Each check sets healthStatus to healthy or down. It also sets lastHealthCheckAt, and it writes an audit row and an event. RealmSSO never writes degraded. A failed check is a normal 200 response with reachable: false and an error text. You reach the check through the API and the dashboard (Test Connection). The Admin Portal has no connection check.

Certificate expiry

RealmSSO encrypts the SAML signing certificate and the OIDC client secret at rest in its own database. The sign-in service holds its own working copy of each. RealmSSO records the expiry date of the certificate when it creates or changes the entry. The check and the config route show two flags: expired, and expires within 30 days.

RealmSSO sends no alert and runs no scheduled check. A person must run the check or read the config. We have not measured whether the sign-in service picks up a rotated certificate from a metadata URL on its own.

Edit a connection

A new upstream identity drops every user’s link

A PATCH that names an IdP field re-provisions the entry in the realm. For SAML the fields are metadataUrl, metadataXml, idpEntityId, idpSsoUrl, idpCertificate and entityId. For OIDC they are clientId, clientSecret and discoveryUrl. This happens even when the value has not changed.

If the save keeps the same upstream identity, the IdP entity ID for SAML or the issuer for OIDC, RealmSSO updates the entry in place and every user keeps their link, including through a certificate change. A changed SAML signing certificate is also recorded in the audit log as a warning.

If the upstream identity changes, or cannot be read, RealmSSO replaces the entry, and the link between each user and the connection is dropped. A returning user then sees an "Account already exists" error and cannot link the account alone. The response's idpUpdate says which happened: its mode is in_place, replaced, or created (there was no provider to update, for example after a save that removed the old one and could not create the new one). A replacement carries the number of links removed, when it could count them, and a warning. The in-place and the replace paths are tested on the real sign-in service. A change of name or status alone does not re-provision the entry.

If the re-provision fails, the status becomes error, unless it was inactive. A failed re-provision, for example onto metadata with no signing certificate, leaves the existing identity provider and every user's link unchanged. See Connection status.

Delete a connection

Deleting a connection ends the sessions of every user who signed in through it. RealmSSO does this first. If it cannot end the sessions of one user, it logs a warning and goes on. Then it deletes the entry, and last the record. If RealmSSO cannot delete the entry, the delete fails and the record stays. A status change ends no session.

Test login URL

GET /api/v1/connections/:id/initiate is a stable URL for one connection. It needs no credential. It starts a login through that connection and lands the user on the account page of the realm. It does not land the user in your application. An IT admin can put it in the IdP's app tile as the sign-on URL. The config route returns it as idpInitiatedSignOnUrl.

The route answers 302 to the sign-in service. It answers 403 with the code CONNECTION_NOT_BROKERING when the connection does not accept new logins. It answers 404 when the connection or its entry does not exist.

Reference

All routes live under /api/v1/connections. A request with an X-API-Key header is an API key request. Any other request needs a dashboard session. See the API overview for credentials, roles and scopes. An API key must also belong to the account of the connection.

RouteRoleScope
POST /samlmemberconnections:write
POST /oidcmemberconnections:write
GET /viewerconnections:read
GET /:idviewerconnections:read
PATCH /:idmemberconnections:write
DELETE /:idmemberconnections:delete
POST /:id/testmemberconnections:write
GET /:id/configviewerconnections:read
GET /:id/initiatenonenone
POST /:id/offline-accessmemberconnections:write
POST /:id/assertion-grantmemberconnections:write
POST /:id/agent-principalsmemberconnections:write
POST /:id/agent-principals/:userId/revokememberconnections:write

Create a SAML connection

POST/api/v1/connections/saml
FieldTypeNotes
accountIdstringRequired
namestringRequired
metadataUrlstring (URL)Optional. The metadata URL of the IdP. It must be a public address.
metadataXmlstringOptional. The metadata XML of the IdP. API only.
idpEntityIdstringOptional. The entity ID of the IdP. Part of the manual set.
idpSsoUrlstring (URL)Optional. The single sign-on endpoint of the IdP. Part of the manual set.
idpCertificatestringOptional. The signing certificate of the IdP. Part of the manual set.
entityIdstringOptional. The SP entity ID. RealmSSO generates it when you leave it out.
acsUrlstring (URL)Optional. Leave it out. RealmSSO stores and displays it in ssoConfig and the Admin Portal. It does not change where the sign-in service receives assertions.
offlineAccessEnabledbooleanOptional. Offline policy. See the offline access route.
maxOfflineAgeSecondspositive integer or nullOptional. Offline policy.
offlineRequiresDeviceLockbooleanOptional. Offline policy. The default is true.

Send a metadata URL, metadata XML or the three manual values. The response is 201. It holds the connection and its ssoConfig in data.

API=https://api.realmsso.com
curl -X POST "$API/api/v1/connections/saml" \
  -H "X-API-Key: <your-api-key>" \
  -H "Content-Type: application/json" \
  -d '{"accountId":"<account-id>","name":"Acme SAML","metadataUrl":"<idp-metadata-url>"}'
{
  "data": {
    "id": "<connection-id>",
    "accountId": "<account-id>",
    "type": "saml",
    "name": "Acme SAML",
    "status": "active",
    "keycloakIdpAlias": "saml-<connection-id>",
    "idpCertificateSet": true,
    "clientSecretSet": false,
    "ssoConfig": {
      "acsUrl": "<sign-in-origin>/realms/kr-<account-id>/broker/saml-<connection-id>/endpoint",
      "entityId": "<sign-in-origin>",
      "audience": "<sign-in-origin>"
    }
  }
}

Other fields are left out of this example.

Create an OIDC connection

POST/api/v1/connections/oidc
FieldTypeNotes
accountIdstringRequired
namestringRequired
clientIdstringRequired
clientSecretstringRequired, not empty. Encrypted at rest in the RealmSSO database. Never returned. Never written to audit details. The sign-in service keeps its own working copy.
discoveryUrlstring (URL)Required. The OIDC discovery document of the IdP. It must be a public address.
offlineAccessEnabledbooleanOptional. Offline policy. See the offline access route.
maxOfflineAgeSecondspositive integer or nullOptional. Offline policy.
offlineRequiresDeviceLockbooleanOptional. Offline policy. The default is true.

The response is 201. The ssoConfig holds the redirect URI in redirectUris. The OIDC redirect URI is the sign-in service's own endpoint, not a RealmSSO route.

A connection response

FieldMeaning
statuspending, active, inactive or error. See Connection status.
keycloakIdpAliasThe alias of the entry in the realm. It is null until the entry exists.
healthStatushealthy or down, set by the on-demand check. Empty before the first check.
lastHealthCheckAtThe time of the last check.
idpCertificateExpiresAtThe expiry date of the SAML signing certificate.
idpCertificateSet, clientSecretSetTrue or false. The response never holds the certificate or the secret.
metadataHolds provisionError when RealmSSO could not create the entry.
ssoConfigsA list and a get return this array. Each item holds acsUrl, entityId, audience and redirectUris.

A response also holds the other stored fields of the connection.

List connections

GET/api/v1/connections

The accountId query parameter is required. Without it the route returns 400. The optional filters are type (saml or oidc) and status. The list is paginated with page and limit (maximum 100). Each result includes its ssoConfigs.

Get, update and delete a connection

GET/api/v1/connections/:id
PATCH/api/v1/connections/:id
DELETE/api/v1/connections/:id

PATCH takes a partial body. Every field is optional: name, status (active, inactive or error), the SAML and OIDC fields above, acsUrl and config. All of them accept null to clear the value, except name, status and config. RealmSSO encrypts a new clientSecret and never writes it to the audit log.

The PATCH response holds the connection in data and a brokering report. If RealmSSO cannot tell the sign-in service to stop new logins, the route answers 502. See Connection status.

DELETE answers 204. The response carries two headers, x-offline-access-outcome and x-offline-access-effective. They report what the delete did to the offline access of the account.

Check a connection

POST/api/v1/connections/:id/test

The route returns 400 when the connection has no entry yet. Otherwise it returns the result in data.

{
  "data": {
    "reachable": true,
    "latencyMs": 84,
    "certificateExpiresAt": "2027-03-01T00:00:00.000Z",
    "certificateExpired": false,
    "certificateExpiringSoon": false
  }
}

Read the display values

GET/api/v1/connections/:id/config

The route returns type, acsUrl, entityId, issuer and metadataUrl. For OIDC it also returns clientId, discoveryUrl and redirectUris. For SAML it also returns idpCertificateExpiresAt, idpCertificateExpired and idpCertificateExpiringSoon.

It returns idpInitiatedSignOnUrl, offlineAccess and the name and slug of the account. It never returns the client secret. The acsUrl and entityId fields hold only a value that you set yourself, at create time or with a PATCH. They are usually null. Use the routes in the section about the three values to read the real ones. The Admin Portal does not use this route.

Set the offline policy

POST/api/v1/connections/:id/offline-access

Live now · API only. The body holds enabled (required), maxOfflineAgeSeconds (a positive integer, or null) and requiresDeviceLock. The value null means offline is not configured. It never means no limit. RealmSSO stores the policy. No device checks maxOfflineAgeSeconds or requiresDeviceLock today, because the device SDK is not released yet.

A window longer than the realm allows returns 400. enabled: true returns 400 while any connection of the account has the assertion grant on. The route also returns 400 when the account has neither an SSO client nor a mobile client yet. Offline continuity on a device: Building now. See Offline operation.

Assertion grant and agent principals

Three more routes live under /api/v1/connections. They belong to agent identity. See Agent Identity API.

RouteWhat it does
POST /api/v1/connections/:id/assertion-grantTurns the assertion grant on or off for one OIDC connection.
POST /api/v1/connections/:id/agent-principalsMarks a user of the connection as an agent principal.
POST /api/v1/connections/:id/agent-principals/:userId/revokeRemoves the agent mark from a user.

Related routes

RouteWhat it does
POST /api/v1/accounts/:id/connection-error-policySets what a connection in error does about new logins. The role is admin. See Connection status.
GET /api/v1/sso/accounts/:id/configIntegration details for your application. The scope is connections:read.

Errors

StatusCodeWhen
400VALIDATION_ERRORThe body failed validation. The XML cannot be read. A URL is not a public address. The list has no accountId. The connection has no entry yet.
401UNAUTHORIZED, INVALID_API_KEYNo valid credential. See the API overview.
403FORBIDDENThe role is too low. The API key belongs to another account or lacks the scope.
403CONNECTION_NOT_BROKERINGThe initiate route met a connection that does not accept new logins.
404NOT_FOUNDThe connection does not exist.
409IDP_STATE_BUSYAnother change to the IdP settings of the account is in progress. Send the request again.
502CONNECTION_STATUS_ENFORCEMENT_INCOMPLETEA PATCH could not stop new logins in the sign-in service.
502EXTERNAL_SERVICE_ERRORA call to the sign-in service failed.