Skip to content

fix(authentik): two flow defects that only a real browser sign-in could reveal - #68

Open
ggfto wants to merge 2 commits into
mainfrom
fix/authentik-recovery-flow-2025
Open

fix(authentik): two flow defects that only a real browser sign-in could reveal#68
ggfto wants to merge 2 commits into
mainfrom
fix/authentik-recovery-flow-2025

Conversation

@ggfto

@ggfto ggfto commented Sep 8, 2026

Copy link
Copy Markdown
Owner

Both were found migrating a live production deployment to Authentik 2025.2.4, and both had survived every check the project makes — including a full live run against a real instance. Neither is reachable from the API surface: the provider reads as correctly configured, discovery and JWKS answer 200, and bootstrap.sh is idempotent. They fail only when a person tries to use them.

1. The provider's authorization_flow had the wrong designation

It was set to default-authentication-flow, whose designation is authentication. That field wants a flow whose designation is authorization — the consent step that runs once the user is already signed in.

Authentik accepts the wrong designation without complaint. The first browser sign-in fails with:

Request has been denied. Flow does not apply to current user.

...because an authentication flow is moot for a user who has just authenticated. Now prefers default-provider-authorization-implicit-consent, falls back to explicit consent, then to any authorization-designation flow. On the instance where this was found, that is exactly what its five other providers already used.

2. The recovery flow was created as require_unauthenticated

Authentik 2025.x evaluates a flow's authentication requirement against the session that asks for a recovery link — which is the gateway's own API call, authenticated as the token's user. So POST /core/users/{pk}/recovery/ answered 400 "Recovery flow not applicable to user" and no link was ever issued, meaning adding a teammate would fail on a provider that looked healthy. This worked on 2024.8, which is why the earlier live run passed.

none is safe here rather than merely permissive: the flow's write stage, default-password-change-write, ships with user_creation_mode=never_create, so with no pending user it dead-ends instead of minting an account for whoever opens the URL. The only way in stays the one-time flow_token in the link.

Verified

Against the live 2025.2.4 instance: the recovery endpoint now returns 200 with a flow_token link, the provider's authorization_flow matches the instance's other providers, and a real browser sign-in completes and resolves to the expected user.

What this says about the earlier testing

The previous round added AuthentikLiveIT precisely so version drift would be caught by a test rather than a person. It did catch three 2025.x API changes. It could not catch either of these, because both live in the browser-facing half of the flow and every API-level assertion passes. Worth remembering before trusting "verified against a live instance" to mean "works".

https://claude.ai/code/session_01VRJxBb5fb9RUe2UW5FS4mK

…e links

The flow was created with authentication: require_unauthenticated. Authentik
2025.x evaluates that requirement against the session that ASKS for a recovery
link — which is the gateway's own API call, authenticated as the token's user —
so the endpoint answered 400 "Recovery flow not applicable to user" and no link
was ever issued. Adding a teammate would have failed on a provider that looked
correctly configured. It worked on 2024.8, which is why the mocked tests and the
earlier live run against that version both passed.

`none` is safe here rather than merely permissive: the flow's write stage,
default-password-change-write, ships with user_creation_mode=never_create, so
with no pending user it dead-ends instead of minting an account for whoever
opens the URL. The only way in remains the one-time flow_token in the link —
verified against the live instance, which now returns 200 with a flow_token
link, and rejects the flow for an anonymous visitor.

Claude-Session: https://claude.ai/code/session_01VRJxBb5fb9RUe2UW5FS4mK
…ation_flow

The provider was created with authorization_flow pointing at
default-authentication-flow, whose designation is `authentication`. That field
wants a flow whose designation is `authorization` — the consent step that runs
once the user is already signed in.

Authentik accepts the wrong designation without complaint. The provider reads as
correctly configured through the API, discovery and JWKS both answer 200, and
every check this script and its tests perform passes. The failure appears only
in a real browser: the authorize endpoint denies with "Request has been denied.
Flow does not apply to current user", because an authentication flow is moot for
a user who has just authenticated.

That is why it survived a live run against 2024.8 and a live run against 2025.2:
neither completed a browser sign-in, only API calls. It broke the first time a
person actually tried to log in.

Now prefers default-provider-authorization-implicit-consent, falls back to the
explicit-consent flow, then to any authorization-designation flow.

Claude-Session: https://claude.ai/code/session_01VRJxBb5fb9RUe2UW5FS4mK
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant