Skip to content

Commit d651e9a

Browse files
authored
Fix authflow misconfiguration errors and messaging #5866
ref DEV-3759
2 parents 65f8f6d + 5d45512 commit d651e9a

29 files changed

Lines changed: 218 additions & 0 deletions
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
name: Authflow misconfiguration shows fatal error page for AuthenticationFlowInvalidFlowConfig
2+
# BUG: select_destination requires the user to have already been identified
3+
# (it needs MilestoneDoUseAccountRecoveryIdentity to be set). Placing it as
4+
# the first step means there is no identity to enumerate destinations for.
5+
# The server fires AuthenticationFlowInvalidFlowConfig immediately when the
6+
# flow is created, before any user input.
7+
# NavigateNonRecoverableError redirects the browser to /v2/errors/error and
8+
# the fatal error page shows "misconfigured".
9+
#
10+
# Fix: add an identify step before select_destination:
11+
# - type: identify
12+
# one_of:
13+
# - identification: email
14+
authgear.yaml:
15+
override: |
16+
authentication:
17+
identities:
18+
- login_id
19+
primary_authenticators:
20+
- password
21+
identity:
22+
login_id:
23+
keys:
24+
- key: email
25+
type: email
26+
authentication_flow:
27+
account_recovery_flows:
28+
- name: default
29+
steps:
30+
- type: select_destination
31+
enumerate_destinations: true
32+
- type: verify_account_recovery_code
33+
- type: reset_password
34+
steps:
35+
# Step 1: visiting the forgot-password page triggers flow creation, which
36+
# immediately fails with AuthenticationFlowInvalidFlowConfig. The server
37+
# returns a 302 redirect to /v2/errors/error?q_error=...
38+
- name: get_forgot_password_page
39+
action: http_request
40+
http_request_method: GET
41+
http_request_url: http://127.0.0.1:4000/authflow/v2/forgot_password
42+
http_request_follow_redirects: false
43+
http_output:
44+
http_status: 302
45+
# Step 2: follow the redirect to the fatal error page and confirm the
46+
# user-facing message contains "misconfigured".
47+
- name: get_error_page
48+
action: http_request
49+
http_request_method: GET
50+
http_request_url: "http://127.0.0.1:4000{{ .steps.get_forgot_password_page.result.http_response_headers.location }}"
51+
http_output:
52+
http_status: 200
53+
redirect_path: /v2/errors/error
54+
html_text_contains:
55+
- misconfigured
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
name: Authflow misconfiguration shows error for AuthenticationFlowInvalidTargetStep
2+
# The verify step's target_step points to "create_auth" (a create_authenticator step).
3+
# verify requires its target_step to implement IntentSignupFlowStepVerifyTarget, which
4+
# only identify steps implement. create_authenticator does NOT implement it.
5+
#
6+
# After the user submits a password (POST), the flow engine advances to the verify step
7+
# automatically. verify fires InvalidTargetStep because create_auth is the wrong type.
8+
# On POST, NavigateNonRecoverableError keeps the current URL and redirects back to the
9+
# create_password page. On the subsequent GET to that page, the flow engine tries to
10+
# advance to verify again, triggering InvalidTargetStep on a GET request.
11+
# NavigateNonRecoverableError then redirects to /v2/errors/error (fatal error page).
12+
authgear.yaml:
13+
override: |
14+
authentication:
15+
identities:
16+
- login_id
17+
primary_authenticators:
18+
- password
19+
identity:
20+
login_id:
21+
keys:
22+
- key: email
23+
type: email
24+
authentication_flow:
25+
signup_flows:
26+
- name: default
27+
steps:
28+
- type: identify
29+
one_of:
30+
- identification: email
31+
- type: create_authenticator
32+
name: create_auth
33+
one_of:
34+
- authentication: primary_password
35+
- type: verify
36+
target_step: create_auth
37+
steps:
38+
- name: oauth_setup
39+
action: oauth_setup
40+
- name: get_signup_page
41+
action: http_request
42+
http_request_method: GET
43+
http_request_url: "http://127.0.0.1:4000/signup?{{ .steps.oauth_setup.result.query }}&q_login_id_key=email&q_login_id_input_type=email"
44+
http_output:
45+
http_status: 200
46+
redirect_path: /signup
47+
- name: submit_email
48+
action: http_request
49+
http_request_method: POST
50+
http_request_url: "{{ .steps.get_signup_page.result.http_final_url }}"
51+
http_request_form_urlencoded_body:
52+
q_login_id_key: email
53+
q_login_id: test-invalid-target-step@example.com
54+
x_action: login_id
55+
http_output:
56+
http_status: 200
57+
redirect_path: /authflow/v2/create_password
58+
- name: submit_password
59+
action: http_request
60+
http_request_method: POST
61+
http_request_url: "{{ .steps.submit_email.result.http_final_url }}"
62+
http_request_form_urlencoded_body:
63+
x_password: testpassword123
64+
x_confirm_password: testpassword123
65+
http_output:
66+
http_status: 200
67+
html_text_contains:
68+
- misconfigured
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
name: Authflow misconfiguration shows fatal error page for AuthenticationFlowStepNotFound
2+
# BUG: the verify step's target_step references "nonexistent", which does not
3+
# exist in this flow. Because verify is the first step, FindTargetStep is called
4+
# immediately during flow creation (GET request) before any user input.
5+
# NavigateNonRecoverableError redirects the browser to /v2/errors/error and
6+
# the fatal error page shows "misconfigured".
7+
#
8+
# Fix: add a preceding identify step with a matching name, e.g.:
9+
# - type: identify
10+
# name: identify_email
11+
# one_of:
12+
# - identification: email
13+
# - type: verify
14+
# target_step: identify_email
15+
# or remove target_step entirely if the default step ordering is sufficient.
16+
authgear.yaml:
17+
override: |
18+
authentication:
19+
identities:
20+
- login_id
21+
primary_authenticators:
22+
- password
23+
identity:
24+
login_id:
25+
keys:
26+
- key: email
27+
type: email
28+
authentication_flow:
29+
signup_flows:
30+
- name: default
31+
steps:
32+
- type: verify
33+
target_step: nonexistent
34+
steps:
35+
# Step 1: visiting the signup page triggers flow creation, which immediately
36+
# fails with AuthenticationFlowStepNotFound. The server returns a 302 redirect
37+
# to /v2/errors/error?q_error=...
38+
- name: get_signup_page
39+
action: http_request
40+
http_request_method: GET
41+
http_request_url: http://127.0.0.1:4000/signup
42+
http_request_follow_redirects: false
43+
http_output:
44+
http_status: 302
45+
# Step 2: follow the redirect to the fatal error page and confirm the
46+
# user-facing message contains "misconfigured".
47+
- name: get_error_page
48+
action: http_request
49+
http_request_method: GET
50+
http_request_url: "http://127.0.0.1:4000{{ .steps.get_signup_page.result.http_response_headers.location }}"
51+
http_output:
52+
http_status: 200
53+
redirect_path: /v2/errors/error
54+
html_text_contains:
55+
- misconfigured

pkg/auth/handler/webapp/error_renderer.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/authgear/authgear-server/pkg/auth/handler/webapp/viewmodels"
1414
"github.com/authgear/authgear-server/pkg/auth/webapp"
1515
authflow "github.com/authgear/authgear-server/pkg/lib/authenticationflow"
16+
"github.com/authgear/authgear-server/pkg/lib/authenticationflow/declarative"
1617
"github.com/authgear/authgear-server/pkg/lib/authn/user"
1718
"github.com/authgear/authgear-server/pkg/lib/config"
1819
"github.com/authgear/authgear-server/pkg/util/httputil"
@@ -99,6 +100,12 @@ func (s *ErrorRenderer) MakeAuthflowErrorResult(ctx context.Context, w http.Resp
99100
logger.WithError(err).Error(ctx, "unexpected error")
100101
}
101102

103+
if errors.Is(err, authflow.ErrStepNotFound) ||
104+
apierrors.IsKind(err, declarative.InvalidTargetStep) ||
105+
apierrors.IsKind(err, declarative.InvalidFlowConfig) {
106+
logger.WithError(err).Error(ctx, "flow misconfiguration error")
107+
}
108+
102109
recoverable := func() *webapp.Result {
103110
cookie, err := s.ErrorService.SetRecoverableError(ctx, r, apierr)
104111
if err != nil {
@@ -123,6 +130,12 @@ func (s *ErrorRenderer) MakeAuthflowErrorResult(ctx context.Context, w http.Resp
123130
fallthrough
124131
case errors.Is(err, authflow.ErrFlowNotFound):
125132
fallthrough
133+
case errors.Is(err, authflow.ErrStepNotFound):
134+
fallthrough
135+
case apierrors.IsKind(err, declarative.InvalidTargetStep):
136+
fallthrough
137+
case apierrors.IsKind(err, declarative.InvalidFlowConfig):
138+
fallthrough
126139
case user.IsAccountStatusError(err):
127140
fallthrough
128141
case errors.Is(err, api.ErrNoAuthenticator):

resources/authgear/templates/de/translation.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,7 @@
467467
"v2.error.email-required-github": "Bitte fügen Sie eine \"Öffentliche E-Mail\" zu Ihrem GitHub-Profil hinzu. Verwalten Sie Ihr Profil hier: <a class=\"link\" target=\"_blank\" href=\"https://github.com/settings/profile\">https://github.com/settings/profile</a>",
468468
"v2.error.error-account-not-found-login-id-name": "{ LoginIDType, select, email {E-Mail-Adresse} phone {Telefonnummer} username {Benutzername} other {{ LoginIDKey, select, email {E-Mail-Adresse} phone {Telefonnummer} username {Benutzername} other {Identität} }} }",
469469
"v2.error.error-account-not-found-oauth-provider-name": "{ OAuthProviderType, select, google {Google} facebook {Facebook} github {GitHub} linkedin {LinkedIn} azureadv2 {Entra ID} azureadb2c {Azure AD B2C} adfs {AD FS} apple {Apple} wechat {Wechat} other {OAuth} }",
470+
"v2.error.flow-misconfigured": "[Entwickler] Dieser Authentication Flow ist falsch konfiguriert und kann nicht abgeschlossen werden. Überprüfen Sie Ihre Konfiguration im Portal.",
470471
"v2.error.hook-delivery-failed": "Der Vorgang ist nicht zulässig, da die Webhook-Zustellung zu einem unbekannten Fehler geführt hat.",
471472
"v2.error.hook-delivery-timeout": "Der Vorgang ist nicht zulässig, da die Webhook-Übermittlung zu einem Timeout geführt hat.",
472473
"v2.error.hook-disallowed": "Der Vorgang ist nicht zulässig",

resources/authgear/templates/el/translation.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,7 @@
467467
"v2.error.email-required-github": "Παρακαλούμε προσθέστε μια \"Δημόσια Διεύθυνση Email\" στο προφίλ σας στο GitHub. Διαχειριστείτε το προφίλ σας εδώ: <a class=\"link\" target=\"_blank\" href=\"https://github.com/settings/profile\">https://github.com/settings/profile</a>",
468468
"v2.error.error-account-not-found-login-id-name": "{ LoginIDType, select, email {email} phone {αριθμός τηλεφώνου} username {όνομα χρήστη} other {{ LoginIDKey, select, email {email} phone {αριθμός τηλεφώνου} username {όνομα χρήστη} other {ταυτότητα} }} }",
469469
"v2.error.error-account-not-found-oauth-provider-name": "{ OAuthProviderType, select, google {Google} facebook {Facebook} github {GitHub} linkedin {LinkedIn} azureadv2 {Entra ID} azureadb2c {Azure AD B2C} adfs {AD FS} apple {Apple} wechat {Wechat} other {OAuth} }",
470+
"v2.error.flow-misconfigured": "[Προγραμματιστές] Αυτό το Authentication Flow έχει εσφαλμένη διαμόρφωση και δεν μπορεί να ολοκληρωθεί. Ελέγξτε τη διαμόρφωσή σας στην Πύλη.",
470471
"v2.error.hook-delivery-failed": "Η λειτουργία δεν επιτρέπεται επειδή η παράδοση του webhook είχε ως αποτέλεσμα άγνωστο σφάλμα.",
471472
"v2.error.hook-delivery-timeout": "Η λειτουργία δεν επιτρέπεται επειδή η παράδοση του webhook είχε ως αποτέλεσμα το timeout.",
472473
"v2.error.hook-disallowed": "Η λειτουργία δεν επιτρέπεται",

resources/authgear/templates/en/translation.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,7 @@
467467
"v2.error.email-required-github": "Please add a \"Public Email\" to your GitHub profile. Manage your profile here: <a class=\"link\" target=\"_blank\" href=\"https://github.com/settings/profile\">https://github.com/settings/profile</a>",
468468
"v2.error.error-account-not-found-login-id-name": "{ LoginIDType, select, email {email} phone {phone number} username {username} other {{ LoginIDKey, select, email {email} phone {phone number} username {username} other {identity} }} }",
469469
"v2.error.error-account-not-found-oauth-provider-name": "{ OAuthProviderType, select, google {Google} facebook {Facebook} github {GitHub} linkedin {LinkedIn} azureadv2 {Entra ID} azureadb2c {Azure AD B2C} adfs {AD FS} apple {Apple} wechat {Wechat} other {OAuth} }",
470+
"v2.error.flow-misconfigured": "[Developers] This Authentication Flow is misconfigured and can''t be completed. Check your configuration in the portal.",
470471
"v2.error.hook-delivery-failed": "Operation is disallowed because the webhook delivery resulted in unknown error.",
471472
"v2.error.hook-delivery-timeout": "Operation is disallowed because the webhook delivery resulted in timeout.",
472473
"v2.error.hook-disallowed": "Operation is disallowed",

resources/authgear/templates/en/web/authflowv2/__error.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,8 @@
307307
{{ else }}
308308
<span>{{ include "v2.error.oauth-error" (dict "error" $error_code "error_description" .Error.info.error_description) }}</span>
309309
{{ end }}
310+
{{ else if or (eq .Error.reason "AuthenticationFlowStepNotFound") (eq .Error.reason "AuthenticationFlowInvalidTargetStep") (eq .Error.reason "AuthenticationFlowInvalidFlowConfig") }}
311+
<span>{{ include "v2.error.flow-misconfigured" nil }}</span>
310312
{{ else }}
311313
<span>{{ include "v2.error.unknown" (dict "TrackingID" .TrackingID "HasTrackingID" (not (empty .TrackingID))) }}</span>
312314
{{ end }}

resources/authgear/templates/en/web/authflowv2/fatal_error.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@
6666
{{ $error_message = include "v2.error.hook-delivery-timeout" nil }}
6767
{{ else if eq .Error.reason "HookDeliveryUnknownFailure" }}
6868
{{ $error_message = include "v2.error.hook-delivery-failed" nil }}
69+
{{ else if or (eq .Error.reason "AuthenticationFlowStepNotFound") (eq .Error.reason "AuthenticationFlowInvalidTargetStep") (eq .Error.reason "AuthenticationFlowInvalidFlowConfig") }}
70+
{{ $error_message = include "v2.error.flow-misconfigured" nil }}
6971
{{ end }}
7072
{{ end }}
7173

resources/authgear/templates/es-419/translation.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,7 @@
467467
"v2.error.email-required-github": "Agrega un \"Correo electrónico público\" a tu perfil de GitHub. Administra tu perfil aquí: <a class=\"link\" target=\"_blank\" href=\"https://github.com/settings/profile\">https://github.com/settings/profile</a>",
468468
"v2.error.error-account-not-found-login-id-name": "{ LoginIDType, select, email {correo electrónico} phone {número de teléfono} username {nombre de usuario} other {{ LoginIDKey, select, email {correo electrónico} phone {número de teléfono} username {nombre de usuario} other {identidad} }} }",
469469
"v2.error.error-account-not-found-oauth-provider-name": "{ OAuthProviderType, select, google {Google} facebook {Facebook} github {GitHub} linkedin {LinkedIn} azureadv2 {Entra ID} azureadb2c {Azure AD B2C} adfs {AD FS} apple {Apple} wechat {Wechat} other {OAuth} }",
470+
"v2.error.flow-misconfigured": "[Desarrolladores] Este Authentication Flow está mal configurado y no se puede completar. Revisa tu configuración en el Portal.",
470471
"v2.error.hook-delivery-failed": "La operación no está permitida porque la entrega del webhook resultó en un error desconocido.",
471472
"v2.error.hook-delivery-timeout": "Operación no permitida porque la entrega del webhook resultó en un tiempo de espera.",
472473
"v2.error.hook-disallowed": "Operación no permitida",

0 commit comments

Comments
 (0)