Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
b9d0da0
doc: Add profile filling spec
tung2744 Sep 29, 2026
5874394
doc: Validate form submissions with authentication.form.post_submitted
tung2744 Oct 6, 2026
ec6cc80
doc: List all blocking events and show forms in trigger point diagrams
tung2744 Oct 6, 2026
78facaf
doc: Restrict profile filling forms to custom flows
tung2744 Oct 7, 2026
8fcb94f
doc: Allow forms after identification in login flows
tung2744 Oct 9, 2026
8c25418
doc: Document the risk of a form before authentication in login flows
tung2744 Oct 12, 2026
6370d7e
doc: Count marks and zero width joiners as the kind of the character …
tung2744 Oct 12, 2026
051e181
doc: Exclude never-supported config from breaking changes
tung2744 Oct 12, 2026
20134aa
doc: Add profile filling implementation plan
tung2744 Oct 9, 2026
6c3f3a5
[Portal] Keep custom attributes of unknown types on user save
tung2744 Oct 9, 2026
e2214d2
Add boolean custom attribute type
tung2744 Oct 9, 2026
ebfa6f6
Add string constraints to custom and standard attributes config
tung2744 Oct 9, 2026
f5d61d1
Check string constraints on attribute writes
tung2744 Oct 9, 2026
b85c787
Add standard attribute label translations
tung2744 Oct 9, 2026
7b07d10
[Settings] Support boolean attributes and string constraints
tung2744 Oct 9, 2026
10f9454
chore: Generate translations for the profile attribute strings
tung2744 Oct 9, 2026
97f82aa
[Portal] Edit boolean custom attributes and show allowedCharacters er…
tung2744 Oct 9, 2026
6ed6420
Add authentication flow form config types and validation
tung2744 Oct 9, 2026
2dc230f
Add authentication.form.post_submitted event payload and hook response
tung2744 Oct 9, 2026
e7bc4fa
Add fill_form step to authentication flows
tung2744 Oct 9, 2026
0335254
Support forms in the identify step of signup and promote flows
tung2744 Oct 9, 2026
4c48bb3
[Auth UI] Restore checkbox state after a form error
tung2744 Oct 9, 2026
0038aa0
[Auth UI] Add fill_form page
tung2744 Oct 9, 2026
6617f50
[Auth UI] Show identify step form fields on the signup page
tung2744 Oct 9, 2026
cff6487
chore: Generate translations for the fill_form page
tung2744 Oct 9, 2026
bf69eff
Add e2e tests for profile filling
tung2744 Oct 9, 2026
5efe9ec
Add e2e tests for authentication.form.post_submitted
tung2744 Oct 9, 2026
d32243d
chore: Update vettedpositions
tung2744 Oct 9, 2026
e461f3b
chore: Apply go fix and regenerate the icon subset
tung2744 Oct 9, 2026
df31fc2
chore: Remove an unused test helper
tung2744 Oct 9, 2026
dd04288
Fix form errors shown on the wrong fields when a required field is mi…
tung2744 Oct 12, 2026
2124cdf
Fix a checked checkbox submitted as false by turbo-form
tung2744 Oct 12, 2026
709b40c
[Auth UI] Polish the form fields UI
tung2744 Oct 12, 2026
2c9ae75
chore: Reduce the complexity of NewFormViewModel
tung2744 Oct 12, 2026
efcbd8e
chore: Update the translation keys lint expectation
tung2744 Oct 12, 2026
7324093
[Auth UI] Add form container classes for custom layout
tung2744 Oct 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/skills/review-pr/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ When a finding could be two of these, file it under the more user-facing one (Bu
- Go: new/changed exported struct fields, function signatures in `pkg/lib/`
- HTTP/config: new endpoints, changed request/response shapes, `authgear.yaml` schema changes
- Note whether each change is additive (safe) or a rename/removal (breaking)
- Breaking changes are not only interface renames/removals: a new default that refuses input previously accepted, a changed default, stricter validation, or a newly required config field all stop a working deployment. Any of those needs an entry in `docs/BREAKING-CHANGES.md` under `## [Unreleased]`, added in the same commit. A missing entry is a finding — report it under this heading.
- Breaking changes are not only interface renames/removals: a new default that refuses input previously accepted, a changed default, stricter validation, or a newly required config field all stop a working deployment. Any of those needs an entry in `docs/BREAKING-CHANGES.md` under `## [Unreleased]`, added in the same commit. A missing entry is a finding — report it under this heading. Apply the exclusions in the "Breaking changes" section of `AGENTS.md` first: a never-supported config key that is now rejected or now takes effect is not a finding, and neither is an entry missing for one. An entry the diff adds for such a key is the finding instead.
- OAuth error codes: if the diff adds a new `error` code, removes one, or adds a new cause for an existing one (`protocol.NewError` / `NewErrorStatusCode` in `pkg/lib/oauth/`, `pkg/lib/dpop/`, `pkg/auth/handler/oauth/`), the public list at https://docs.authgear.com/reference/apis/oauth-2.0-and-openid-connect-oidc/error-codes (`reference/apis/oauth-2.0-and-openid-connect-oidc/error-codes.md` in `authgear/docs`) needs a matching update. Report it under this heading as a docs follow-up. A reworded `error_description` alone does not need one; the page tells developers not to match on it.

3. **Code quality issues** — look specifically for:
Expand Down
45 changes: 33 additions & 12 deletions .make-lint-translation-keys-expect
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,20 @@ resources/authgear/templates/en/web/authflowv2/__country_input.html:10:21: trans
resources/authgear/templates/en/web/authflowv2/__csrf_error_page_layout.html:29:18: template translation is forbidden: `page-content`
resources/authgear/templates/en/web/authflowv2/__dialog.html:28:14: template translation is forbidden: `dialog-attr`
resources/authgear/templates/en/web/authflowv2/__direct_access_disable_page_frame.html:7:16: template translation is forbidden: `page-content`
resources/authgear/templates/en/web/authflowv2/__error.html:143:18: template translation is forbidden: `authflowv2/__error_account`
resources/authgear/templates/en/web/authflowv2/__error.html:192:20: template translation is forbidden: `authflowv2/__error_account`
resources/authgear/templates/en/web/authflowv2/__error.html:123:49: translation key not defined: "%s%s"
resources/authgear/templates/en/web/authflowv2/__error.html:124:28: translation key not defined: "custom-attribute-pattern-error-%s"
resources/authgear/templates/en/web/authflowv2/__error.html:125:30: translation key not defined: "standard-attribute-pattern-error-%s"
resources/authgear/templates/en/web/authflowv2/__error.html:127:39: invalid translation key: "$custom_key"
resources/authgear/templates/en/web/authflowv2/__error.html:128:17: invalid translation key: "$custom_key"
resources/authgear/templates/en/web/authflowv2/__error.html:129:44: invalid translation key: "$standard_key"
resources/authgear/templates/en/web/authflowv2/__error.html:130:17: invalid translation key: "$standard_key"
resources/authgear/templates/en/web/authflowv2/__error.html:168:18: template translation is forbidden: `authflowv2/__error_account`
resources/authgear/templates/en/web/authflowv2/__error.html:217:20: template translation is forbidden: `authflowv2/__error_account`
resources/authgear/templates/en/web/authflowv2/__form_fields.html:25:22: translation key not defined: "form-field-label-%s"
resources/authgear/templates/en/web/authflowv2/__form_fields.html:29:60: translation key not defined: "%s %s"
resources/authgear/templates/en/web/authflowv2/__form_fields.html:130:35: translation key not defined: "aria-labelledby="%s"
resources/authgear/templates/en/web/authflowv2/__form_fields.html:139:30: translation key not defined: "aria-labelledby="%s" %s"
resources/authgear/templates/en/web/authflowv2/__form_fields.html:186:24: translation key not defined: "%s/country"
resources/authgear/templates/en/web/authflowv2/__locale_input.html:6:16: invalid translation key: "$labelKey"
resources/authgear/templates/en/web/authflowv2/__locale_input.html:10:21: translation key not defined: "%s %s"
resources/authgear/templates/en/web/authflowv2/__new_password_field.html:54:33: translation key not defined: "%s %s"
Expand All @@ -23,6 +35,10 @@ resources/authgear/templates/en/web/authflowv2/__settings_page_frame.html:27:18:
resources/authgear/templates/en/web/authflowv2/__settings_radio.html:16:16: translation key not defined: "radio-%s-%s"
resources/authgear/templates/en/web/authflowv2/__timezone_input.html:8:21: translation key not defined: "%s %s"
resources/authgear/templates/en/web/authflowv2/account_linking.html:89:33: translation key not defined: "%s-icon"
resources/authgear/templates/en/web/authflowv2/fill_form.html:9:10: invalid translation key: "$named_title_key"
resources/authgear/templates/en/web/authflowv2/fill_form.html:13:10: invalid translation key: "$named_description_key"
resources/authgear/templates/en/web/authflowv2/fill_form.html:23:11: invalid translation key: "$title_key"
resources/authgear/templates/en/web/authflowv2/fill_form.html:25:35: invalid translation key: "$description_key"
resources/authgear/templates/en/web/authflowv2/forgot_password.html:102:30: template translation is forbidden: `__forgot_password_alternative`
resources/authgear/templates/en/web/authflowv2/forgot_password.html:157:26: template translation is forbidden: `__forgot_password_alternative`
resources/authgear/templates/en/web/authflowv2/layout.html:5:14: template translation is forbidden: `widget`
Expand All @@ -38,17 +54,22 @@ resources/authgear/templates/en/web/authflowv2/settings_mfa.html:69:44: translat
resources/authgear/templates/en/web/authflowv2/settings_oob_otp.html:78:31: translation key not defined: "/settings/mfa/create_oob_otp_%s"
resources/authgear/templates/en/web/authflowv2/settings_profile.html:140:22: translation key not defined: "custom-attribute-label-%s"
resources/authgear/templates/en/web/authflowv2/settings_profile.html:141:20: invalid translation key: "$labelKey"
resources/authgear/templates/en/web/authflowv2/settings_profile.html:145:21: invalid translation key: "$labelKey"
resources/authgear/templates/en/web/authflowv2/settings_profile.html:209:22: invalid translation key: "$label"
resources/authgear/templates/en/web/authflowv2/settings_profile.html:147:36: invalid translation key: "$labelKey"
resources/authgear/templates/en/web/authflowv2/settings_profile.html:149:23: invalid translation key: "$labelKey"
resources/authgear/templates/en/web/authflowv2/settings_profile.html:223:22: invalid translation key: "$label"
resources/authgear/templates/en/web/authflowv2/settings_profile_edit_custom.html:6:18: translation key not defined: "custom-attribute-label-%s"
resources/authgear/templates/en/web/authflowv2/settings_profile_edit_custom.html:7:16: invalid translation key: "$labelKey"
resources/authgear/templates/en/web/authflowv2/settings_profile_edit_custom.html:11:17: invalid translation key: "$labelKey"
resources/authgear/templates/en/web/authflowv2/settings_profile_edit_custom.html:103:30: translation key not defined: "custom-attribute-enum-label-%s-%s"
resources/authgear/templates/en/web/authflowv2/settings_profile_edit_custom.html:104:28: invalid translation key: "$enum_label_key"
resources/authgear/templates/en/web/authflowv2/settings_profile_edit_custom.html:107:28: invalid translation key: "$enum_label_key"
resources/authgear/templates/en/web/authflowv2/settings_profile_edit_custom.html:13:32: invalid translation key: "$labelKey"
resources/authgear/templates/en/web/authflowv2/settings_profile_edit_custom.html:15:19: invalid translation key: "$labelKey"
resources/authgear/templates/en/web/authflowv2/settings_profile_edit_custom.html:67:31: translation key not defined: "custom-attribute-label-%s"
resources/authgear/templates/en/web/authflowv2/settings_profile_edit_custom.html:68:12: invalid translation key: "$boolean_label_key"
resources/authgear/templates/en/web/authflowv2/settings_profile_edit_custom.html:69:29: invalid translation key: "$boolean_label_key"
resources/authgear/templates/en/web/authflowv2/settings_profile_edit_custom.html:135:30: translation key not defined: "custom-attribute-enum-label-%s-%s"
resources/authgear/templates/en/web/authflowv2/settings_profile_edit_custom.html:136:28: invalid translation key: "$enum_label_key"
resources/authgear/templates/en/web/authflowv2/settings_profile_edit_custom.html:139:28: invalid translation key: "$enum_label_key"
resources/authgear/templates/en/web/authflowv2/settings_sessions.html:47:29: translation key not defined: "__settings_session_item_description"
resources/authgear/templates/en/web/authflowv2/signup.html:97:26: translation key not defined: "v2.page.signup-login.continue.subtitle-%v"
resources/authgear/templates/en/web/authflowv2/signup.html:106:26: translation key not defined: "v2.page.signup.continue.subtitle-%v"
resources/authgear/templates/en/web/authflowv2/signup.html:282:35: translation key not defined: "%s-icon"
52 errors found
resources/authgear/templates/en/web/authflowv2/signup.html:103:26: translation key not defined: "v2.page.signup-login.continue.subtitle-%v"
resources/authgear/templates/en/web/authflowv2/signup.html:112:26: translation key not defined: "v2.page.signup.continue.subtitle-%v"
resources/authgear/templates/en/web/authflowv2/signup.html:303:35: translation key not defined: "%s-icon"
73 errors found
exit status 1
9 changes: 5 additions & 4 deletions .vettedpositions
Original file line number Diff line number Diff line change
Expand Up @@ -97,16 +97,17 @@
/pkg/auth/handler/webapp/authflowv2/enter_recovery_code.go:113:26: requestcontext
/pkg/auth/handler/webapp/authflowv2/enter_totp.go:132:26: requestcontext
/pkg/auth/handler/webapp/authflowv2/error.go:65:36: requestcontext
/pkg/auth/handler/webapp/authflowv2/fill_form.go:81:26: requestcontext
/pkg/auth/handler/webapp/authflowv2/finish_flow.go:36:56: requestcontext
/pkg/auth/handler/webapp/authflowv2/forgot_password.go:286:33: requestcontext
/pkg/auth/handler/webapp/authflowv2/forgot_password_link_sent.go:86:26: requestcontext
/pkg/auth/handler/webapp/authflowv2/forgot_password_otp.go:205:26: requestcontext
/pkg/auth/handler/webapp/authflowv2/internal_signup_login.go:241:33: requestcontext
/pkg/auth/handler/webapp/authflowv2/internal_signup_login.go:254:33: requestcontext
/pkg/auth/handler/webapp/authflowv2/ldap_login.go:109:26: requestcontext
/pkg/auth/handler/webapp/authflowv2/login.go:242:33: requestcontext
/pkg/auth/handler/webapp/authflowv2/oauth_provider_demo_credential.go:65:26: requestcontext
/pkg/auth/handler/webapp/authflowv2/oob_otp_link.go:170:26: requestcontext
/pkg/auth/handler/webapp/authflowv2/promote.go:136:33: requestcontext
/pkg/auth/handler/webapp/authflowv2/promote.go:145:33: requestcontext
/pkg/auth/handler/webapp/authflowv2/prompt_create_passkey.go:142:26: requestcontext
/pkg/auth/handler/webapp/authflowv2/reauth.go:42:33: requestcontext
/pkg/auth/handler/webapp/authflowv2/reset_password.go:169:35: requestcontext
Expand Down Expand Up @@ -257,8 +258,8 @@
/pkg/auth/handler/webapp/verify_identity.go:161:27: requestcontext
/pkg/auth/handler/webapp/verify_identity_success.go:42:27: requestcontext
/pkg/auth/handler/webapp/verify_login_link.go:92:27: requestcontext
/pkg/auth/handler/webapp/viewmodels/base.go:212:9: requestcontext
/pkg/auth/handler/webapp/viewmodels/base.go:430:9: requestcontext
/pkg/auth/handler/webapp/viewmodels/base.go:215:9: requestcontext
/pkg/auth/handler/webapp/viewmodels/base.go:433:9: requestcontext
/pkg/auth/handler/webapp/websocket.go:42:25: requestcontext
/pkg/auth/handler/webapp/websocket.go:53:25: requestcontext
/pkg/auth/handler/webapp/websocket.go:77:30: requestcontext
Expand Down
5 changes: 5 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,11 @@ For more targets, browse the root `Makefile` and `portal/package.json` / `authui
- stricter validation of existing config or requests
- a behaviour change a deployment could be relying on

"Works today" means it uses only what Authgear officially supports. These do **not** belong there:

- A config key, value, or request input that was never supported, even when a permissive schema accepted and ignored it. It is not expected to exist, so rejecting it or giving it a meaning breaks nothing.
- A risk only during a mixed-version rollout or a rollback, when the upgrade needs no manual step. Mention it in the commit message or PR description instead.

Rules:

- Add the entry under `## [Unreleased]`, **in the same commit as the change itself** — not as a follow-up.
Expand Down
1 change: 1 addition & 0 deletions authui/src/authflowv2/components.css
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
@import "./components/divider.css";
@import "./components/body-text.css";
@import "./components/input.css";
@import "./components/form.css";
@import "./components/alert-message.css";
@import "./components/otp-input.css";
@import "./components/password-strength-meter.css";
Expand Down
4 changes: 4 additions & 0 deletions authui/src/authflowv2/components/checkbox.css
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@
}
}

.checkbox--error .checkbox__label {
@apply ring-[color:var(--color-error)];
}

.checkbox input {
&:checked + .checkbox__label {
@apply bg-[color:var(--checkbox\_\_background-color--checked)];
Expand Down
12 changes: 12 additions & 0 deletions authui/src/authflowv2/components/form.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@layer components {
:root {
--form__container-gap: 1.25rem;
}

/* A named form also has .form__container--<name>, for custom CSS to set the grid template. */
.form__container {
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: var(--form__container-gap);
}
}
10 changes: 10 additions & 0 deletions authui/src/authflowv2/components/input.css
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,16 @@
font-weight: var(--input__error-message-font-weight);
}

/* A label sits right above its input, so it is always start-aligned, unlike
.body-text--md, which follows the project's alignment-content setting. */
.input__label {
@apply body-text--md text-start;
}

:root[alignment-content] .input__label {
@apply text-start;
}

.input.input__password {
@apply ltr:pr-[var(--input\_\_password-padding-end)] rtl:pl-[var(--input\_\_password-padding-end)];
}
Expand Down
4 changes: 4 additions & 0 deletions authui/src/authflowv2/components/select-input.css
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@
ring-[color:var(--select-input\_\_trigger-ring-color--focus)];
}

.select-input--error & {
@apply ring-[color:var(--select-input\_\_trigger-ring-color--error)];
}

&::after {
@apply material-icons;
content: "arrow_forward_ios";
Expand Down
Binary file modified authui/src/authflowv2/icons/material-symbols-outlined-subset.ttf
Binary file not shown.
Binary file not shown.
3 changes: 2 additions & 1 deletion authui/src/authflowv2/turboForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ export class TurboFormController extends Controller {
const params = new URLSearchParams();
formData.forEach((value, name) => {
if (typeof value === "string") {
params.set(name, value);
// append keeps both values of a checkbox and its hidden false; the server reads the first.
params.append(name, value);
} else {
console.error("ignoring non-string value: ", name);
}
Expand Down
25 changes: 23 additions & 2 deletions authui/src/form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,9 +192,30 @@ export class RestoreFormController extends Controller {
elem instanceof HTMLTextAreaElement
) {
const value = formDataJSON[elem.name];
if (value != null) {
elem.value = value;
if (value == null) {
continue;
}
if (
elem instanceof HTMLInputElement &&
(elem.type === "checkbox" || elem.type === "radio")
) {
// Never change the value of a checkbox or a radio button.
// Only the form value of the one that was selected is restored.
elem.checked = value === elem.value;
continue;
}
if (
elem instanceof HTMLInputElement &&
elem.type === "hidden" &&
form.querySelector(
`input[type="checkbox"][name="${CSS.escape(elem.name)}"]`
) != null
) {
// A hidden input paired with a checkbox carries the value of an unchecked checkbox.
// The form value was taken from the first of the two, so it does not belong to this input.
continue;
}
elem.value = value;
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion docs/BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Changes that can break a **working deployment** when you upgrade. Newest first.

This is not a changelog. A change is listed here only if an upgrade can stop something that used to work, and only with what to do about it.
This is not a changelog. A change is listed here only if an upgrade can stop something that used to work, and only with what to do about it. Something that used to work means something Authgear officially supports; a config key or input that was never supported is not listed when it starts being rejected or starts to take effect.

## How to use this file

Expand Down
Loading
Loading