> 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/configuration-and-deployment/enable-security/authentication/login-form.md).

# Login form

This is the simplest form of authentication provided by ODD Platform.

User credentials are set by defining the `auth.login-form-credentials` property in the format `username1:password1,username2:password2,...`. All users authenticated through this method receive ADMIN privileges in the platform.

{% hint style="danger" %}
**The shipped configuration contains the default credentials `admin:admin,root:root`.** If you enable `LOGIN_FORM` authentication without overriding `auth.login-form-credentials`, the platform will accept logins from `admin/admin` and `root/root` — and both accounts have full ADMIN privileges. Change these defaults before any non-local deployment.

To override, either set `auth.login-form-credentials` explicitly (YAML or the `AUTH_LOGIN_FORM_CREDENTIALS` environment variable, shown below) or switch to `OAUTH2` or `LDAP` authentication. For any production deployment, prefer OAUTH2 or LDAP — `LOGIN_FORM` stores credentials in plain text in the platform configuration and does not support rotation, session revocation, or MFA.
{% endhint %}

{% tabs %}
{% tab title="YAML" %}

```yaml
auth:
    type: LOGIN_FORM
    login-form-credentials: susan:susan_password,dave:dave_password
```

{% endtab %}

{% tab title="Environment variables" %}

```
AUTH_TYPE=LOGIN_FORM
AUTH_LOGIN_FORM_CREDENTIALS=susan:susan_password,dave:dave_password
```

{% endtab %}
{% endtabs %}

## Authorization posture under `LOGIN_FORM`

`LOGIN_FORM` is dev/demo-oriented authentication. Three platform-side defaults diverge from the production modes (`OAUTH2`, `LDAP`) in ways that materially change what an operator should expect from a `LOGIN_FORM` deployment — none of them is a bug, but each is load-bearing on operator decisions. For the cross-mode comparison of how every auth mode grants ADMIN (including LOGIN\_FORM's "every user is ADMIN" stance), see [Admin promotion across providers](/configuration-and-deployment/enable-security/admin-promotion.md).

### Policy and Role tables are not consulted

Every user authenticated through `LOGIN_FORM` is granted `ADMIN` authority at login. The Spring Security chain in this mode does not wire the `AuthorizationCustomizer` that translates the `SecurityConstants.SECURITY_RULES` table into per-request authorization checks; under `LOGIN_FORM`, the chain is `.pathMatchers("/**").authenticated()` and nothing more. The Policies, Roles, and Permission grants an operator authors via the [Management → Policies / Roles UI](/configuration-and-deployment/enable-security/authorization/policies.md) are stored in the database but never gate the live auth chain — they take effect only after switching `auth.type` to `OAUTH2` or `LDAP`.

**Operator consequence:** evaluating a non-ADMIN Policy in `LOGIN_FORM` mode does not test what will happen in `OAUTH2` / `LDAP`. Do Policy / Role testing on the same auth mode you will run in production.

### CSRF protection is disabled

The `LOGIN_FORM` filter chain unconditionally disables CSRF (`http.csrf(...::disable)`). A signed-in operator visiting an attacker-controlled page can have state-changing `POST` / `PUT` / `DELETE` requests issued to the platform on their behalf, because the browser automatically attaches the session cookie. The platform also does not stamp `SameSite=Strict` or `Secure` cookie attributes on its default session cookies — see [Configure ODD Platform → Session and cookie configuration](/configuration-and-deployment/odd-platform.md) — so the protections that would normally compensate for missing CSRF defence are also absent in the default deployment.

**Operator consequence:** run `LOGIN_FORM` only behind a TLS-terminating reverse proxy that stamps `Secure` and `SameSite=Strict` externally, or restrict the deployment to a trusted network. Either is acceptable for dev/demo; neither is production-grade.

### `auth.login-form-redirect` is operator-trusted

After a successful sign-in, the platform sends the user to the URL configured in `auth.login-form-redirect` (or to `/` when the property is empty). The redirect target is parsed via `URI.create(...)` with no scheme allowlist, no host allowlist, and no platform-base-URL validation. An operator who templates this value from a less-trusted upstream pipeline (a CI variable, a downstream Helm value derived from arbitrary env-var input) must ensure the provenance of the input.

**Operator consequence:** treat `auth.login-form-redirect` as a high-trust string. Either set it to a fixed value at the deployment manifest, or apply your own allowlist in the configuration pipeline that produces the platform's environment variables.

## Cross-mode user-name collision (activity feed read paths)

The platform's [activity-feed](/features/active-platform-features/activity-feed.md) read paths join the `USER_OWNER_MAPPING` table on `OIDC_USERNAME` only — the join does **not** discriminate by auth provider. If your deployment has historically used multiple auth modes (for example, started on `LOGIN_FORM` with seed users, then enabled `LDAP` or `OAUTH2`), a user named `alice` who signed in via `LOGIN_FORM` and a user named `alice` who signed in via `LDAP` can resolve to the same `OwnerPojo` when activity-feed rows are rendered. The most recent `USER_OWNER_MAPPING` row for the literal username wins each lookup; results are not deterministic across replicas.

**Operator mitigation today:** never reuse usernames across auth modes when migrating. If you migrate from `LOGIN_FORM` to `LDAP` or `OAUTH2`, delete the `USER_OWNER_MAPPING` rows for the `LOGIN_FORM` usernames before introducing same-named users in the new mode.

**Forensic note:** do not rely on activity-feed Owner attribution for incident response in multi-mode deployments without verifying the auth mode of the actor through the underlying `activity.created_by` value.

A platform-side fix that discriminates the join by `PROVIDER` is tracked upstream. The same caveat applies to deployments running `LDAP` on top of an earlier `LOGIN_FORM` history — see the matching note on the [LDAP](/configuration-and-deployment/enable-security/authentication/ldap.md#cross-mode-user-name-collision-activity-feed-read-paths) page.
