ICE - identity for the whole product family

One login. One scope catalog.
One root of trust.

ICE is the single sign-in and authorization server for the whole Splenta family. Every product validates its tokens; nobody re-implements identity, tenancy or login.

ICE access tokenone token, checked everywhere
  • issuerice.<customer>who signed it
  • tenantmercy-generalacting in
  • scopehms:patient:read, ice:user:readwhat it may do
  • audiencehms-apiwho it is for

One RS256 token. Every product verifies it against ICE's public key - no shared secret, no callback home.

Abstract streams of code on a dark screen
An abstract security visualization. Photo is illustrative, not a screen from ICE.

What ICE is

One identity plane for the whole family.

Not a bolt-on login for one app - the shared identity and access layer every product is built on. One short line each, no jargon.

Single sign-in

One login, one issuer

Sign in once through ICE. Every other product (SCM, Arin, RMS, WMS, FMS, HMS) trusts the same token - none ships its own login form.

One vocabulary

One scope catalog

129 permissions, defined once as code. Every product reads the same vocabulary; none invents its own.

Tenancy

Multi-tenant IAM

One global identity across many tenants. Switch tenant and the token is re-minted - so no product builds tenancy for itself.

Access model

Roles, permissions, groups

App-roles bundle permissions; groups carry roles to their members. Business roles stay in the product that owns them.

Policy

ABAC policy

A closed, fail-closed condition policy grants or revokes access at sign-in time. Revoke always beats grant.

Migration

Gradual enforcement

Move each product from off to report to enforce on its own schedule - never one irreversible switch for everyone.

The trust chain

One issuer signs. Every product just verifies.

Resource servers never hold a signing key and never call back to ICE to check a token. They verify a signature against a key ICE publishes, confirm the token is meant for them, and decide on their own what its scopes allow. One issuer decides; each product enforces.
  • A person signs in once - no product ships its own login form.
  • Products cache the public key, so a brief ICE blip never stops verification.
  • No shared secret ever leaves ICE, and no token is phoned home to check.
A corridor lined with server racks in a data centerInfrastructure
A data-center corridor. Illustrative photo.

Enforcement modes

Migrate at your own pace.

Flipping identity enforcement on in one step is how you lock people out. ICE has three modes and a per-product override, so a tenant migrates gradually - watch what would be denied, close the gaps, then flip one product to enforce when it is ready, not all of them at once.
  • off - nothing is checked, before a tenant is modelled.
  • report - the default: ICE logs what it would deny, but issues the scopes anyway.
  • enforce - scopes are narrowed to what the permissions allow. HMS runs here today; the rest stay in report.
A close-up of a processor chip on a circuit boardCompute
A processor on a circuit board. Illustrative photo.

Where it fits

Not a broader IdP. A sharper one.

ICE is purpose-built platform identity for the Splenta family, self-hosted per customer - not a general-purpose CIAM you buy to secure your own app. The hosted providers are genuinely good at breadth; ICE trades that for one deployment shape and a shared, code-verified catalog.

ICEAuth0 / OktaKeycloakOryCognitoEntra ID
What it isSelf-hosted OAuth 2.1 server + IAM for one product familyTurnkey hosted consumer identity (CIAM)Open-source IdP with realms and brokeringCertified, headless OAuth2 / OIDCManaged user pools, AWS-nativeEnterprise SSO and conditional access
DeploymentOne dedicated instance per customer, inside your own VPCHosted SaaS (self-host on higher tiers)Self-hosted or managedCloud or self-hostedManaged AWS serviceManaged Microsoft cloud
Shared scope catalogOne code-verified 129-scope catalog across all productsDefine your own APIs and scopesClient scopes per realmOAuth2 scopes you defineResource-server scopes you defineApp roles and scopes you define
MFA / SSO / SAML / SCIMNone today - not a general CIAMExtensive, built inBroad, built inVia the wider Ory stackMFA and SAML / OIDC federationEnterprise-grade, built in
Best fitThe Splenta family's shared identity planeSecuring your own app fastA self-hosted general-purpose IdPCloud-native API authorization at scaleApps already on AWSMicrosoft-centric enterprises

The hosted and open providers keep their real strengths in breadth, MFA, SSO and federation - none of which ICE offers today. ICE trades all of that for a single deployment shape, a code-verified shared scope catalog and a decide-once, enforce-everywhere claim contract. It is pre-revenue with no shared production deploy, which we say plainly. If you need a general CIAM for your own product, one of the others is the better answer.

Under the hood

One issuer, one signature, many enforcers.

The story above is the plain version. For evaluators, here is the mechanism - ICE is built on Spring Authorization Server (Java 17, PostgreSQL), and every other product is a pure resource server that holds no signing key.

The signed claim contract

Access decisions travel inside the token as claims. ICE mints an RS256 JWT; the audience is derived from the scope prefixes, never configured per client. The public JWKS on the right is the only thing a product needs to verify it.

decoded access token :: ICE claim contractRS256
{
"iss": "https://ice.<customer>",
"sub": "usr_9f3c1a7e",
"token_type": "access",
"principal_type": "user",
"tenant": "mercy-general",
"app_roles": ["hms:hms-admin"],
"scope": ["hms:patient:read", "ice:user:read"],
"aud": ["hms-api"]
}
// aud derived from scope prefixes, never configured per client
GET /oauth2/jwks :: public verification keyno shared secrets
{ "keys": [ {
"kty": "RSA",
"kid": "ice-rs256",
"alg": "RS256",
"use": "sig",
"n": "0vx7ag...Kd9GY",
"e": "AQAB"
} ] }

Verify, then authorize

Resource servers never hold a secret from ICE and never call back to check a token. They verify a signature against a published key, confirm the token is for them, then decide on their own what its scopes allow.

sign in

A person signs in

authorization_code + PKCE, once

PDP - decides

ICE mints a token

RS256 JWT of resolved claims

public key

Product reads the JWKS

Cached from /oauth2/jwks

PEP - enforces

Validate + enforce scopes

Per request, holds no signing key

resource server :: validate before authorizeholds no signing key
// four checks, then per-request scope enforcement
1iss== https://ice.<customer>
2sigverified against /oauth2/jwks (RS256)
3audcontains this server's audience
4token_type== "access"
// then, per request:
require SCOPE_hms:patient:read
tenant of record = the tenant claim

One scope catalog - 129 scopes

Every scope in the family is defined once, as code, in a single catalog ICE owns. It exists as both a document and a source file, held in strict parity by a test - so what a product reads is exactly what ICE issues. Authority reads the same everywhere: domain:resource:action, and the audience comes from the prefix.

iceaud=ice-api

ICE - identity + IAM

  • ice:user:read
  • ice:tenant:write
  • ice:policy:write
scmaud=scm-api

SCM - procurement

  • scm:po:approve
arinaud=arin-api

Arin - agent layer

  • arin:agent-action:write
rmsaud=rms-api

RMS - retail

  • rms:order:write
wmsaud=wms-api

WMS - warehouse

  • wms:stock:write
fmsaud=fms-api

FMS - finance

  • fms:gl:write
hmsaud=hms-api

HMS - hospital

  • hms:patient:read

129 total

Across all seven domains. The names above are representative, not the whole set - the catalog is the source of truth, parity-gated in CI.

The seeded client roster

Two grant types, by design, and no others - authorization_code + PKCE for human workbenches, client_credentials for agents and services. The clients below are real and seeded. A Clients admin UI is designed but not built yet.

Human clients

authorization_code + PKCE - public - 15-min access / 7-day refresh

  • ice-workbench
  • scm-workbench
  • arin-workbench
  • rms-merchant
  • rms-admin
  • wms-workbench
  • hms-workbench

Machine clients

client_credentials - agent_id-tagged - identity-neutral claims

  • arin-scm-client
  • scm-service
  • wms-service
  • arin-rms-client
  • arin-wms-client
  • arin-hms-client
  • ice-mcp-dev

Audience routing, live

Mint one token, then watch its audience recompute from the scope prefixes as independent resource servers accept or ignore it. One signature, checked against the published JWKS - each server honours only the scopes prefixed with its own domain.

POST /oauth2/tokenissuing
Principal

grant: client_credentials

Scopes granted

Toggle scopes. The audience is derived from their prefixes - never configured per client.

Minted access token

{
  alg: "RS256", kid: "ice-rs256"
  iss: "https://ice.splenta.internal"
  sub: "arin-scm-client"
  principal_type: "CLIENT"
  aud: ["arin-api", "scm-api"]
}

signed RS256 - verifiable against /oauth2/jwks

Verified independently by

  • SCM aud=scm-apiaccepts
  • Arin aud=arin-apiaccepts
  • RMS aud=rms-apino authority
  • HMS aud=hms-apino authority

One signature, checked against the published JWKS - no shared secrets ever leave ICE. A token can target several products at once; each server honors only the scopes prefixed with its own domain.

Family-internal platform identity

One issuer for the family. Yours to host.

ICE is the identity backbone the rest of the Splenta family is built on - not a product you buy to secure your own app. If you are evaluating the family, or want to walk the trust chain and the claim contract in detail, talk to us and we will show you the architecture.