Mechanisms
UniAuth supports four mechanisms. The distinction that shapes everything is not what they are called but how the user gets to them.
| Mechanism | Kind | How a user reaches it |
|---|---|---|
Internal store |
form-based |
the shared username/password form |
LDAP |
form-based |
the shared username/password form |
OAuth2 / OIDC |
redirect-based |
|
SAML 2.0 |
redirect-based |
|
AuthProviderType carries which half a mechanism belongs to, via its formBased flag.
1. Form-based: one form, several providers
The internal store and LDAP share a single form. They are told apart by the
AuthenticationProvider chain: every AuthenticationProvider bean is registered onto the
chain, and Spring Security tries each until one authenticates.
That fall-through is the point. It is what lets a handful of local break-glass accounts sit beside a directory, so an outage of the directory does not lock every administrator out of the application that depends on it.
2. Redirect-based: their own entry points
OAuth2 and SAML keep their own entry-point URLs, so the chooser page only has to render a link. Nothing in UniAuth proxies the flow — Spring Security’s own filters handle it, and the starter’s job is to have installed them onto the same chain.
3. Asking what is available
AuthProviderRegistry is the single answer to "what can a user sign in with right now". The
chooser page reads it, and so does the JSON endpoint:
curl http://localhost:8080/uniauth/providers
[
{"id":"internal","type":"INTERNAL","brand":"GENERIC","displayName":"Local account",
"loginUrl":null,"oidc":false,"formBased":true},
{"id":"google","type":"OAUTH2","brand":"GOOGLE","displayName":"Google",
"loginUrl":"/oauth2/authorization/google","oidc":true,"formBased":false},
{"id":"github","type":"OAUTH2","brand":"GITHUB","displayName":"GitHub",
"loginUrl":"/oauth2/authorization/github","oidc":false,"formBased":false}
]
An API-first front end can render its own chooser from this without knowing anything about Spring Security.
|
The registry does not read those repositories itself — each redirect-based mechanism
contributes its own entries, which is what lets both jars be optional. A mechanism enumerates
its repository by testing it for |
4. HTTP Basic, for callers that are not browsers
All three login mechanisms assume a browser and a session, which leaves no way in for a
smoke check, a polling worker or a line of curl: none can perform an OAuth redirect, and a
form login means scraping a CSRF token and holding a cookie jar to fetch a health page.
uniauth:
http-basic:
enabled: true # off by default
paths: [ /api/** ] # optional: where the challenge is offered
Basic answers the form-based mechanisms — the internal store and LDAP. The redirect-based ones have no password to present, so the question does not arise for them.
|
A browser never gets the challenge — not when it navigates, and not when a page it is already showing calls in the background. Installed naively, Basic answers an unauthenticated browser with a native credential dialog instead of the login page, which defeats the point of offering OAuth at all; and that dialog opens above the page, so a front end cannot suppress it. Browsers say so themselves. Every current one labels its requests with
|
5. Brand is not a mechanism, and neither is OIDC
Three separate axes, deliberately not merged:
AuthProviderType-
The mechanism — how the provider talks, and therefore which filter installs it. Adding
GOOGLEhere as a peer ofOAUTH2would break the form/redirect split the chain depends on, and the list would grow without bound. AuthProviderBrand-
Presentation only. Icons and vendor-mandated button treatment.
AuthProvider.oidc()-
Capability, derived from the
openidscope. This is the one that changes what your code receives: Google yields anOidcUserwith claims, GitHub yields a bareOAuth2User. Reach for this whenever you mean "does it have claims", never for brand.
That last distinction is why the examples ship Google and GitHub side by side: it is visible rather than asserted.