For the complete documentation index, see llms.txt. This page is also available as Markdown.

Login form

Simplest auth mode — username/password credentials via auth.login-form-credentials. All login-form users receive ADMIN privileges; not suitable for production deployments.

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.

auth:
    type: LOGIN_FORM
    login-form-credentials: susan:susan_password,dave:dave_password
AUTH_TYPE=LOGIN_FORM
AUTH_LOGIN_FORM_CREDENTIALS=susan:susan_password,dave:dave_password

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.

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 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 — 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 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 page.

Last updated