CodamAIDocs
Topicdone

Reject when in doubt

No default tenant, no default administrator role, required configuration without a default. Why an application would rather not start than start generously.

Variants
no default tenantno default rolesexplicitly empty means nobodyrequired beansstartup refusedrequired field in the request

What this is about

Whoever manages permissions can be wrong in two ways. They keep someone out who should be allowed in. Or they let someone in who should not be. The first mistake is annoying and shows up immediately. The second often does not show up at all, because everything works.

So in every case of doubt, CIAS chooses the first mistake. This is called fail closed. It applies in two places:

  • at startup: if a setting that decides about permissions is missing, the application does not start. The error message names what is missing.
  • on every request: if CIAS cannot answer a question with certainty, it refuses instead of assuming a convenient answer.

Missing, empty or named

A setting like “which roles administer the platform?” can be in three states. CIAS treats them differently, and the difference is deliberate.

Three states of a permission setting
missing
  • Nobody wrote anything down.
  • The application does not start.
  • The message names the missing setting or the missing type.
explicitly empty
  • The installation writes down an empty list.
  • The application starts.
  • The rule applies to nobody.
named
  • The installation names roles.
  • The application starts.
  • The rule applies to everyone who holds one of these roles.

“Empty” and “missing” look similar in code, but they mean different things. An empty list is a decision: “nobody should be able to do this through the API.” A missing setting is an oversight. CIAS wants to see the oversight at startup, not with the first customer.

Empty always means nobody, never everybody. An empty list of platform administrator roles means: nobody is a platform administrator.

What an installation has to state

Some settings are beans. A bean is an object that Spring puts together at startup. If no library supplies it, the installation has to create it itself. Other settings are entries in application.yml. Neither kind has a default value.

SettingWhat it is forWhen it is required
Bean PlatformAdministratorswhich roles administer the platformas soon as an administration module is switched on
Bean RegistrationRoleswhich roles a registration grants: for founders, for members, without a tenantwhen registration is switched on
codamai.cias.tenancy.lookupwhere the tenant gate gets its answers from: local or remotealways, when the CIAS filter chain runs
codamai.cias.tenancy.client.base-url and .tokenwhere CIAS can be reached and with which token it is askedwith lookup: remote
codamai.cias.tenancy.lookup-roleswho may ask the tenant endpoint for serviceswhen codamai.cias.tenancy.lookup-rest is on
codamai.cias.user.lookup-roleswho may read the attribute values per tenant for serviceswhen codamai.cias.user.lookup-rest is on
codamai.cdms.cias.reader-roleswho may read GET /cias/fetch in CDMS, that is the list of all roles and attributesalways in CDMS
codamai.cias.iam-providerwhich identity provider CIAS talks to: keycloak or memoryin the starter
codamai.cias.notification.mailhow mail leaves the process: smtp or logwhen the mail module is switched on
codamai.cias.notification.mail.fromthe sender of the mailswith mail: smtp
cias_clientthe client that module roles live onwhen registration is switched on
public base URL of the registrationwhere the links in the mails point towhen registration is switched on

The reasons are always the same. Two examples:

  • lookup without a default. If local were the default, a CDMS service that forgot the setting would ask a CIAS in its own process that is not there at all. If remote were the default, a standalone CIAS would ask itself over HTTP. Both are worse than a startup that does not succeed in the first place.
  • mail without a default. If log were the default, an installation would report every mail as sent and deliver none. You would only notice when customers complain.

Further checks at startup

Besides missing settings, CIAS catches a few mistakes at startup that would otherwise silently open permissions:

When CIAS refuses to start
What is noticed at startupConsequence
A module wants to publish a public path like /** or /Startup refused. Such a path would switch off authentication for the whole application.
A module declares an attribute per tenant, but nothing can read the valuesStartup refused. Otherwise the value from the token would silently apply, the same in every tenant.
A time is negative, a cache smaller than 1, a timeout 0Startup refused
A throttling limit names a counter that does not exist (typo)Startup refused. Otherwise the limit would silently have no effect.
Two modules in the declaration list have the same name, or one lacks a name or clientStartup refused
An address for the first platform administrator is set, but no administrator role, or Keycloak does not know the addressStartup refused

Every application with CIAS also checks at startup whether development settings were left behind. This applies to standalone CIAS just as to every application that embeds CIAS. If one of these settings is present, the application does not start and names each of them:

SettingWhy it is dangerous in front of customers
codamai.cias.iam-provider: memoryan in-memory identity provider that forgets every account on restart
codamai.cias.notification.mail: log, when mail is switched onmails end up in the log instead of with the recipient
codamai.cias.migration.enabled: falsenothing creates or upgrades the CIAS tables
spring.jpa.hibernate.ddl-auto not validate (standalone CIAS only)Hibernate changes the schema itself instead of reporting differences

Whether a finding stops the start is decided by one setting: codamai.safety.mode (as an environment variable CODAMAI_SAFETY_MODE).

ValueWhat happensFor
enforce (applies when nothing is set)start refused, every setting is namedevery installation in front of customers: stage, production, blue/green
warnthe start goes ahead, every setting is logged as a warningonly a developer’s machine and tests

The value says what happens, not where the application runs. That is why no environment needs a value of its own. Standalone CIAS sets warn only in its profile local.

At runtime: reject when in doubt

On every single request the same applies: what CIAS does not know for sure is refused. The table shows the most important places.

SituationWhat CIAS does not doWhat happens
Operating mode MULTI, the token names no tenantpick a default tenant403 cias.authentication.tenant-required
The person belongs to several organizations, nothing says which one is meanttake the first one403 cias.authentication.tenant-unresolved
The tenant gate does not know the tenant, or CIAS does not answer and nothing is rememberedlet it through403 cias.authentication.tenant-not-served
The person’s attribute values in this tenant cannot be readcarry on with empty values403 cias.authentication.tenant-not-served
Keycloak returns no token during the token exchangeassume an identitythe request continues without an identity, every role check refuses
A caller without roles, or the list of administrator roles is emptytreat them as an administratorthey are not a platform administrator
A role has no delegationlet everyone grant itonly a platform administrator grants it
POST /cias/admin/roles without scopeassume “platform wide” or “in the tenant”400, the request is invalid
POST /cias/admin/tenants without typeassume “dynamic” or “static”400, the request is invalid
A registration flow is not configureduse a generous default flowthe flow does not exist, the call is refused

Why does CIAS refuse when it cannot read the attribute values, instead of simply assuming “no values”? Because in CDMS “no values” is dangerously close to “all values”: a single * switches an attribute filter off entirely. So empty values would not be a safe assumption, but a bet.

When the request is refused, the filter chain writes nothing into the RequestContext. Even code that overlooked the refusal would find no tenant and no roles there.

Where there are defaults

Not everything in CIAS has to be configured. There are defaults for everything that grants no permissions:

SettingDefault
codamai.cias.tenant-gate.ttl: how long the tenant gate remembers an answer30 seconds
codamai.cias.attribute-lookup.ttl: how long attribute values per tenant are remembered30 seconds
codamai.cias.token-exchange.ttl: how long an exchanged token is reused5 minutes
Timeout when asking a standalone CIAS2 seconds each for connecting and for the answer
Size of the caches10,000 entries

These times are security relevant too, because a suspension can take effect that much later. But a wrong value only makes the application slower or chattier, it does not open anything.

And some things are deliberately not allowed to stop the startup:

  • A module declaration that CIAS cannot accept refuses only that one module. Its roles are then not written. The application still starts, because otherwise a single faulty module could bring down the whole platform.
  • A role reconciliation at startup that does not complete is in the log with all details. The application starts. Otherwise it would not come up just because another service happens to be restarting.
  • Missing metrics change nothing about the behavior. The tenant gate works the same way without measurements.

Pitfalls

Next

Sources in the code and the knowledge base
  • CIAS/CLAUDE.md – §12.3 Fail Closed, §32 Security (no default admin role, no silent tenant fallbacks)
  • CIAS/cias-kernel – PlatformAdministrators (no default, empty set = nobody, isPlatformAdministrator), TenantRequirement
  • CIAS/cias-authentication – CiasTokenConfiguration (TenantLookupPort required), TenantBoundAttributeWiringCheck, SessionConfig.collect (OPENS_EVERYTHING), TenantGateProperties, AttributeLookupProperties, TokenExchangeProperties, TokenParser (tokenParser, admit), JwtSessionFilter
  • CIAS/cias-tenancy – CiasTenancyConfiguration (lookup without default, AdministrationConfiguration, LookupRestConfiguration), TenantLookupRoles, TenantAdministrationService.requireAdministrator, TenantRestDtos.CreateTenantRequest (type @NotNull)
  • CIAS/cias-tenancy-client – CiasTenancyClientProperties (base-url), CiasTenancyClientConfiguration (token)
  • CIAS/cias-registration – RegistrationRoles (three sets, no default), CiasRegistrationConfiguration (requireClient, RegistrationRoles via ObjectProvider), RegistrationLinks, RateLimiterPort.bucket
  • CIAS/cias-notification – CiasNotificationConfiguration.ciasMailSender (smtp or log, no default), SmtpConfiguration (mail.from)
  • CIAS/cias-user – CiasUserConfiguration (lookup-roles), AttributeLookupRoles
  • CIAS/cias-authorization – CiasAuthorizationConfiguration (PlatformAdministrators, declaration sources), AuthorizationRestDtos.DefineRoleRequest (scope @NotNull), RoleReconciliationService (rejected declaration)
  • CIAS/cias-spring-boot-starter – CiasAutoConfiguration (RegistrationRoles not supplied, reconciliation at startup), CiasIamAutoConfiguration (iam-provider), CiasDeclarationAutoConfiguration, CiasBootstrap, CiasProperties
  • CIAS/cias-spring-boot-starter – CiasSafetyCheck, SafetyMode (codamai.safety.mode)
  • CIAS/cias-runtime – CiasLocalProfileNotice, CiasPlatformAdministratorConfiguration, CiasTenantLookupRoleConfiguration, CiasRegistrationRoleConfiguration, CiasMailTemplateEditConfiguration
  • CDMS/cdms-authorization – CiasReaderRoles (codamai.cdms.cias.reader-roles without default), CiasApi
  • CIAS/cias-authorization/docs/adr – ADR-028; CIAS/cias-authentication/docs/adr – ADR-021, ADR-042; CIAS/cias-kernel/docs/adr – ADR-022
Search