> For the complete documentation index, see [llms.txt](https://docs.opendatadiscovery.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.opendatadiscovery.org/developer-guides/architecture-decision-log/adr-0074-pluggable-auth-modes.md).

# ADR-0074: Authentication is a pluggable mode selected by auth.type, defaulting to DISABLED

## 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.type` to `LOGIN_FORM`, `OAUTH2`, or `LDAP`** — leaving it at the default on a reachable network exposes the whole API. The [security-configuration guide](/configuration-and-deployment/enable-security.md) 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 `ADMIN` authority, so LOGIN\_FORM fits a small trusted team rather than a deployment that needs role separation; `OAUTH2` and `LDAP` are 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); `:81` builds each credential with `getAuthorities(true)`, which `auth/mapper/GrantedAuthorityExtractor.java:12-16` resolves to the `ADMIN` authority.
* `odd-platform-api/.../config/OAuthSecurityConfiguration.java:71` and `.../config/LDAPSecurityConfiguration.java:51` — the `havingValue="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 adds `s2sAuthenticationFilter` only when `auth.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](/developer-guides/architecture-decision-log/adr-0002-centralised-path-matcher-authorization.md) — the permission rules the authenticated modes enforce, and which DISABLED leaves unwired.
* [ADR-0003 — The catalog is read-collaborative](/developer-guides/architecture-decision-log/adr-0003-read-collaborative-authorization.md) — what an authenticated user can read; that posture assumes a real auth mode is enabled, which is this decision.
* [Enable security](/configuration-and-deployment/enable-security.md) — the operator guide to choosing and configuring an authentication mode.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.opendatadiscovery.org/developer-guides/architecture-decision-log/adr-0074-pluggable-auth-modes.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
