CVE-2026-82758 Overview
CVE-2026-82758 is an improper authentication vulnerability [CWE-287] in the ash-project/ash_authentication_oauth2_server Elixir library. The flaw allows an unauthenticated attacker to register OAuth clients through the Dynamic Client Registration (DCR) endpoint even when registration is gated by an initial_access_token. The resolve_secret/3 function in AshAuthentication.Oauth2Server fails open when a configured secret provider returns nil, false, or an empty string. That behavior converts a closed registration configuration into an open one and also affects other resolved secrets such as signing_secret. The issue impacts versions from 0.1.0 before 0.3.1.
Critical Impact
Attackers can register arbitrary OAuth clients against a server operator's authorization service without presenting a valid bearer token, defeating the DCR access-token gate.
Affected Products
- ash_authentication_oauth2_server versions 0.1.0 through 0.3.0
- Elixir applications using the Ash Framework OAuth2 server with a configured initial_access_token provider
- Deployments relying on resolve_secret/3 for any secret value, including signing_secret
Discovery Timeline
- 2026-09-07 - CVE-2026-82758 published to NVD
- 2026-09-08 - Last updated in NVD database
Technical Details for CVE-2026-82758
Vulnerability Analysis
The vulnerability resides in resolve_secret/3 within AshAuthentication.Oauth2Server, reached through __resolve_secret__!. The function accepts secret providers configured as either a {module, function, args} tuple or a 2-arity function. It matched on {:ok, value} and :error return shapes but treated every other return as a valid secret by wrapping it in {:ok, value}.
When a provider returned nil, false, or "", the resolver produced {:ok, nil}, {:ok, false}, or {:ok, ""} instead of failing. POST /oauth/register then compared the client-supplied bearer token against this empty value. The comparison succeeded when no token was supplied, opening registration to unauthenticated callers.
Root Cause
The secret resolver did not enforce a type or non-empty invariant on provider return values. Any truthy or falsy non-tuple result was accepted as a legitimate secret, violating the fail-closed principle expected of authentication gates.
Attack Vector
An unauthenticated remote attacker sends a POST /oauth/register request with no Authorization header. If the deployment's initial_access_token provider resolves to an empty value under any runtime condition (missing environment variable, unloaded config), the bearer-token comparison passes and the attacker registers an arbitrary OAuth client.
// Patch excerpt - lib/ash_authentication/oauth2_server.ex
defp resolve_secret({mod, fun, args}, module, path, _context)
when is_atom(mod) and is_atom(fun) and is_list(args) do
- case apply(mod, fun, [path, module | args]) do
- {:ok, value} -> {:ok, value}
- :error -> :error
- other -> {:ok, other}
- end
+ mod |> apply(fun, [path, module | args]) |> normalize_resolved_secret()
end
defp resolve_secret(fun, module, path, _context) when is_function(fun, 2) do
- case fun.(path, module) do
- {:ok, value} -> {:ok, value}
- :error -> :error
- other -> {:ok, other}
- end
+ fun.(path, module) |> normalize_resolved_secret()
end
+ # A resolved secret must be a non-empty binary. Treat nil, false, "",
+ # {:error, _}, {:ok, nil} and any non-binary as a resolution failure.
+ defp normalize_resolved_secret({:ok, value}) when is_binary(value) and value != "",
+ do: {:ok, value}
Source: GitHub Commit 30a8710
Detect Identity Attacks the Moment Authentication Fails
Singularity™ Identity surfaces credential misuse, lateral movement, and privilege escalation in real time, so you can contain the breach before it spreads.
Stop Identity AttacksDetection Methods for CVE-2026-82758
Indicators of Compromise
- Successful POST /oauth/register requests originating from unexpected IPs without an Authorization: Bearer header
- Newly registered OAuth clients that do not correspond to any provisioning workflow or ticket
- Application logs showing initial_access_token resolving to nil, false, or empty string values
Detection Strategies
- Audit the OAuth client registration table for entries created outside of expected change windows or without an associated operator identity
- Instrument the secret provider callback to log and alert on any non-binary or empty return value
- Correlate DCR endpoint access logs with authenticated administrative sessions to identify anomalous unauthenticated registrations
Monitoring Recommendations
- Enable structured request logging on /oauth/register including presented bearer token presence and client metadata
- Alert on registration bursts or registrations from IP ranges outside internal automation
- Monitor Elixir application telemetry for AshAuthentication.Oauth2Server secret-resolution failures after upgrade
How to Mitigate CVE-2026-82758
Immediate Actions Required
- Upgrade ash_authentication_oauth2_server to version 0.3.1 or later
- Review all OAuth clients registered since the vulnerable version was deployed and revoke any that are not attributable to legitimate operators
- Rotate signing_secret and any other secrets resolved through the affected provider path, since they may have silently resolved to empty values
Patch Information
The fix is delivered in commit 30a8710 and released in version 0.3.1. The patch introduces normalize_resolved_secret/1, which requires a non-empty binary and raises rather than silently accepting nil, false, "", or non-binary values. See the GitHub Security Advisory GHSA-fxc6-vp68-87pw and CNA CVE-2026-82758 Details for authoritative references.
Workarounds
- Disable Dynamic Client Registration entirely if an upgrade cannot be applied immediately
- Add a reverse-proxy rule requiring a non-empty Authorization: Bearer header on /oauth/register
- Wrap the initial_access_token provider so it raises when the underlying source returns nil, false, or an empty string
# Update mix.exs to require the patched version
# {:ash_authentication_oauth2_server, "~> 0.3.1"}
mix deps.update ash_authentication_oauth2_server
mix deps.get
mix compile --force
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

