ADR-0074: Authentication is a pluggable mode selected by auth.type, defaulting to DISABLED
Auth is one of four modes set by auth.type (DISABLED, LOGIN_FORM, OAUTH2, LDAP). It ships DISABLED, which is fully open — enable a real mode for any networked deployment.
Status
Accepted. Reconstructed from the codebase on 2026-05-31; the decision is live in the source today.
Context
ODD runs in very different settings — a developer evaluating it on a laptop, a small team behind a VPN, an enterprise with OAuth/SSO or a corporate LDAP. The platform has to support all of them without compiling in one authentication scheme, and without forcing a first-time evaluator to stand up an identity provider just to start the app. The design questions are: how to make authentication a deployment choice rather than a code property, and what to ship as the default.
Decision
Authentication is a mode selected by the auth.type property, and each mode is a mutually-exclusive, fully-formed SecurityWebFilterChain. Four @Configuration classes each carry @ConditionalOnProperty("auth.type", havingValue=<MODE>), so the value of auth.type decides — by construction — which single filter chain is built at boot. The modes cannot overlap or partially combine; there is always exactly one active chain:
DISABLED —
anyExchange().permitAll(): no authentication, and the permission rules of ADR-0002 are not wired in. Every endpoint is open.LOGIN_FORM — a static username/password list with form login; a whitelist plus
pathMatchers("/**").authenticated().OAUTH2 — OIDC/OAuth2 client login, with group→role mapping.
LDAP — LDAP / Active-Directory bind, with group→role mapping.
auth.type ships as DISABLED.
Service-to-service (S2S) authentication is not a fifth mode — it composes additively. Each of the three real modes layers the S2sAuthenticationFilter onto its chain when auth.s2s.enabled is set; DISABLED, having no chain to protect, does not. CSRF protection is disabled in every mode's chain — a cross-mode convention for this SPA/token-style backend, not a per-mode choice.
Consequences
DISABLED is for local evaluation only. In DISABLED mode the platform performs no authentication and enforces none of the ADR-0002 permission rules — every endpoint, including every mutation, is open to anyone who can reach the port. It exists so the platform runs with zero security configuration on a developer's machine. Any networked or production deployment must set
auth.typetoLOGIN_FORM,OAUTH2, orLDAP— leaving it at the default on a reachable network exposes the whole API. The security-configuration guide is the operator's path to enabling a mode.LOGIN_FORM authenticates but does not authorize per user. Every configured form-login credential is granted the
ADMINauthority, so LOGIN_FORM fits a small trusted team rather than a deployment that needs role separation;OAUTH2andLDAPare the modes that carry real group→role mapping.Adding an auth mode means adding one
@Configuration. A new scheme is a new conditionally-loaded filter-chain bean, not a branch inside an existing one — the modes stay isolated and individually readable, at the cost of some duplicated chain wiring across the four classes.S2S being additive lets a deployment accept both interactive logins (its chosen mode) and service tokens at once, keyed independently — rather than forcing a choice between human and machine callers.
Evidence
odd-platform-api/.../config/DisabledAuthSecurityConfiguration.java:10-18—@ConditionalOnProperty("auth.type", havingValue="DISABLED"); the chain is.csrf(disable).authorizeExchange(anyExchange().permitAll())— open by construction.odd-platform-api/src/main/resources/application.yml:32-34—auth: type: DISABLED, the shipped default.odd-platform-api/.../config/LoginFormSecurityConfiguration.java:31(havingValue="LOGIN_FORM"),:53-59(whitelist +pathMatchers("/**").authenticated()+ form login);:81builds each credential withgetAuthorities(true), whichauth/mapper/GrantedAuthorityExtractor.java:12-16resolves to theADMINauthority.odd-platform-api/.../config/OAuthSecurityConfiguration.java:71and.../config/LDAPSecurityConfiguration.java:51— thehavingValue="OAUTH2"and"LDAP"chains; the four@ConditionalOnProperty("auth.type", …)beans are mutually exclusive..../config/LoginFormSecurityConfiguration.java:61-63,.../config/OAuthSecurityConfiguration.java:109,.../config/LDAPSecurityConfiguration.java:150— each real mode addss2sAuthenticationFilteronly whenauth.s2s.enabled(S2S is additive); DISABLED does not.DisabledAuthSecurityConfiguration.java:15,LoginFormSecurityConfiguration.java:54,OAuthSecurityConfiguration.java:96,LDAPSecurityConfiguration.java:143— CSRF disabled in all four.
See also
ADR-0002 — Centralised path-matcher authorization — the permission rules the authenticated modes enforce, and which DISABLED leaves unwired.
ADR-0003 — The catalog is read-collaborative — what an authenticated user can read; that posture assumes a real auth mode is enabled, which is this decision.
Enable security — the operator guide to choosing and configuring an authentication mode.
Last updated