CVE-2026-65633 Overview
CVE-2026-65633 is an improper authentication vulnerability [CWE-287] in the team-alembic ash_authentication library for Elixir. The bearer-token helper AshAuthentication.Plug.Helpers.retrieve_from_bearer/3 verifies JWT signatures and rejects tokens containing an act claim, but does not check that the token's purpose claim equals user at the bearer boundary. Purpose-limited JWTs, such as the sign_in token issued by WebAuthn and the Password strategy, can be replayed directly in an Authorization: Bearer header and resolve to a full current_user assignment. The flaw affects ash_authentication from 3.10.5 before 4.14.2 and from 5.0.0-rc.0 before 5.0.0-rc.13.
Critical Impact
An attacker who obtains a not-yet-exchanged sign_in token can authenticate as the target subject, bypassing the library's one-time-use and revocation contract.
Affected Products
- team-alembic ash_authentication 3.10.5 through versions before 4.14.2
- team-alembic ash_authentication 5.0.0-rc.0 through versions before 5.0.0-rc.13
- Applications using WebAuthn or the Password strategy with sign_in_tokens_enabled?: true and require_token_presence_for_authentication?: false
Discovery Timeline
- 2026-08-25 - CVE-2026-65633 published to NVD
- 2026-08-25 - Last updated in NVD database
Technical Details for CVE-2026-65633
Vulnerability Analysis
The ash_authentication library issues purpose-scoped JWTs for narrow flows. The sign_in token, emitted by WebAuthn and the Password strategy, is intended to be exchanged exactly once through a preparation that validates the purpose claim and revokes the token. The stateless bearer path never enforces this contract.
retrieve_from_bearer/3 calls Jwt.verify/3 and rejects tokens carrying an act claim, but performs no purpose check. When the resource is configured with require_token_presence_for_authentication?: false (the DSL default), the follow-on validate_token/3 returns {:ok, nil} without consulting the stored token record. No downstream code enforces purpose == "user", so a sign_in token is accepted as a general-purpose bearer credential.
Root Cause
The root cause is missing purpose-scope validation at the bearer authentication boundary. The library treats a valid JWT signature and absence of the act claim as sufficient for bearer authentication, conflating narrow, purpose-limited tokens with full user-session credentials. The session-based path authenticate_resource_from_session/4 and configurations with require_token_presence_for_authentication?: true correctly enforce the purpose claim against the token resource and are not affected.
Attack Vector
An attacker must obtain a still-valid, not-yet-exchanged sign_in token for the target subject. Realistic exposure paths include referrer or log leakage of magic-link URLs, interception of magic-link delivery channels, or a partially compromised intermediary. The attacker submits the captured token as Authorization: Bearer <token> to any route wired to retrieve_from_bearer/3, and the request resolves as the target user.
|> Enum.reduce(conn, fn token, conn ->
with {:ok, %{"sub" => subject, "jti" => jti} = claims, resource}
when not is_map_key(claims, "act") <- Jwt.verify(token, otp_app, opts),
+ true <- usable_as_bearer_token?(claims),
{:ok, token_record} <-
validate_token(resource, jti, opts),
{:ok, user} <-
Source: GitHub Commit 124eddd1. The patch inserts a usable_as_bearer_token?/1 guard that rejects tokens whose purpose claim is not user before validate_token/3 is called.
Detection Methods for CVE-2026-65633
Indicators of Compromise
- Successful authenticated requests where the presented JWT carries a purpose claim other than user (for example sign_in).
- Repeated presentation of the same jti value across multiple bearer requests, indicating replay of a one-time token.
- Bearer authentications for a subject that lack a corresponding session-establishment event or token-exchange record.
Detection Strategies
- Decode inbound JWTs at the reverse proxy or application logger and alert on any purpose value other than user reaching bearer-protected routes.
- Correlate jti values in application logs against the stored token resource to detect tokens presented after they should have been revoked.
- Audit application routing to enumerate every plug that invokes retrieve_from_bearer/3 and confirm the associated resource configuration.
Monitoring Recommendations
- Log full JWT claim sets (excluding signatures) for bearer authentications during the remediation window.
- Alert on magic-link URLs appearing in HTTP referrer headers, web server access logs, or outbound proxy logs.
- Monitor for unexpected access from IP addresses or user agents that do not match the target user's normal session pattern.
How to Mitigate CVE-2026-65633
Immediate Actions Required
- Upgrade ash_authentication to 4.14.2 or 5.0.0-rc.13 or later.
- Set require_token_presence_for_authentication?: true on any resource exposed through retrieve_from_bearer/3 until the upgrade is complete.
- Revoke any outstanding sign_in tokens by clearing the token resource, forcing re-authentication.
- Rotate JWT signing secrets if sign_in tokens may have been exposed through logs, referrers, or intercepted delivery channels.
Patch Information
The fix is delivered in ash_authentication versions 4.14.2 and 5.0.0-rc.13. The commits 124eddd1 and 8cf8b2d4 add a usable_as_bearer_token?/1 guard in lib/ash_authentication/plug/helpers.ex that rejects any JWT whose purpose claim is not user before the bearer path returns a current_user. See the GitHub Security Advisory GHSA-6vcj-3h59-rrc3 and the CNA advisory for the full disclosure.
Workarounds
- Set require_token_presence_for_authentication?: true on affected resources so the token resource is consulted and purpose == "user" is enforced.
- Prefer the session-based path authenticate_resource_from_session/4, which is not affected.
- Disable sign_in_tokens_enabled? on the Password strategy where it is not required, and restrict WebAuthn flows to trusted transport channels.
# Configuration example - enforce token presence on affected resources
# In your Ash resource module:
#
# authentication do
# tokens do
# enabled? true
# require_token_presence_for_authentication? true
# end
# end
#
# Then upgrade the dependency in mix.exs:
# {:ash_authentication, "~> 4.14.2"}
mix deps.update ash_authentication
mix compile --force
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

