Skip to content

fix(theme): pass Zitadel given_name + family_name to auto-provisioned WP Subscriber#178

Open
JohnRDOrazio wants to merge 1 commit into
mainfrom
fix/bearer-pass-name-claims
Open

fix(theme): pass Zitadel given_name + family_name to auto-provisioned WP Subscriber#178
JohnRDOrazio wants to merge 1 commit into
mainfrom
fix/bearer-pass-name-claims

Conversation

@JohnRDOrazio
Copy link
Copy Markdown
Member

@JohnRDOrazio JohnRDOrazio commented Jun 6, 2026

Summary

  • Zitadel bearer validator's auto-provision flow now passes OIDC `given_name` → WP `first_name` and `family_name` → `last_name` to `wp_insert_user`, in addition to the existing `name` → `display_name` mapping.
  • Resolution + auto-provision signatures changed from `string $display_name` to `array $profile` (display_name + first_name + last_name) so the call sites express intent and future profile-claim additions stay diff-friendly.
  • Empty given/family fields are omitted from the `wp_insert_user` args (not passed as `''`), so a partial-profile sign-up doesn't blank out WP defaults.

Why

Reported on staging after PR #176 + the AUTH_ZITADEL_ORG_ID Org-scoping (PR #177): registering a new user successfully landed them in the CDCF Org and auto-provisioned a WP Subscriber, but the WP user row had empty First Name + Last Name (`display_name` had fallen back to the email). The OIDC standard claims `given_name` + `family_name` were being discarded.

Tests

  • Existing `test_auto_provision_uses_zitadel_name_for_display_name` extended to also assert `first_name` / `last_name`.
  • Existing `test_auto_provision_falls_back_to_email_for_display_name_when_name_missing` extended to assert `first_name` + `last_name` keys are omitted (not passed as empty strings) when the claims are absent.
  • New `test_auto_provision_passes_given_and_family_name_to_wp_insert_user` covers the given+family-without-aggregate-name sign-up shape (the actual case the operator hit).

Theme suite: 458 / 458 (+1), 1063 assertions.

Deploy

WP theme change. After merge needs `gh workflow run deploy.yml -f environment=production` so the new shape reaches live WP. Independent of PR #177 (no overlap in files).

Test plan

  • Merge + deploy to production.
  • Register a new user via Zitadel on staging.
  • WP admin → Users → the new Subscriber row shows First Name, Last Name, Display Name populated from the Zitadel registration form values.
  • Repeat with a Zitadel account whose `name` claim is empty but `given_name` + `family_name` are set — confirm first_name/last_name populate, display_name falls back to email.

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Improved handling of user name information during authentication and automatic account creation, with intelligent fallback handling when name data is incomplete.
  • Tests

    • Expanded test coverage for name field population during automatic account provisioning.

… WP Subscriber

Phase 5 auto-provision only read the OIDC `name` claim and only mapped
it to `display_name`. A new sign-up where Zitadel returned `given_name`
+ `family_name` (but no aggregate `name`) produced a WP user row whose
WP-admin profile showed empty First/Last Name fields and display_name
falling back to the email.

Resolution + auto-provision now take a `$profile` array carrying
`display_name` (OIDC `name`), `first_name` (OIDC `given_name`), and
`last_name` (OIDC `family_name`). Empty given/family fields are
*omitted* from the wp_insert_user args rather than passed as '' so
partial-profile sign-ups don't actively blank-out the WP fields
(WP keeps its own defaults).

Tests: existing display_name happy/fallback paths extended to assert
first_name/last_name behaviour; +1 new test for the given_name +
family_name (no aggregate name) sign-up shape. Theme suite: 458/458,
1063 assertions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 6, 2026

PR changed again? Review this PR in Change Stack to compare snapshots and stay oriented.

Review Change Stack

📝 Walkthrough

Walkthrough

Zitadel authentication now passes a structured profile array containing display_name, first_name, and last_name fields through user resolution and auto-provisioning. Auto-provisioning conditionally populates WordPress user first_name and last_name when present, with display_name computed from profile or falling back to email.

Changes

Zitadel Bearer Profile Handling

Layer / File(s) Summary
Profile data contract and resolver signature
wordpress/themes/cdcf-headless/includes/auth/zitadel-bearer.php
Zitadel userinfo claims (name, given_name, family_name) are assembled into a $profile array at the authenticate call site. The cdcf_zitadel_bearer_resolve_user() function signature and docblock are updated to accept this array instead of a lone display_name string.
Auto-provision profile consumption and wiring
wordpress/themes/cdcf-headless/includes/auth/zitadel-bearer.php
cdcf_zitadel_bearer_auto_provision_subscriber() accepts $profile and conditionally sets first_name and last_name in WordPress user data when those fields are non-empty; display_name is computed from profile with email fallback. The resolver forwards the profile through to auto-provisioning.
Test coverage for profile-based auto-provisioning
wordpress/themes/cdcf-headless/tests/ZitadelBearerTest.php
Existing test assertions verify first_name and last_name are passed to wp_insert_user when Zitadel provides those claims. The nameless auto-provision test asserts those keys are omitted when claims are absent. A new test covers given_name + family_name without aggregate name, verifying display_name falls back to email.

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly Related PRs

  • CatholicOS/cdcf-website#176: This PR builds on the subscriber auto-provision flow by refactoring the function signatures to accept a richer $profile array structure, enabling first/last name fields to be wired into WordPress user creation where PR #176 previously used a simpler display_name-only approach.

Poem

🐰 A profile takes shape with names so fine,
Given and family claims align,
Display falls back when names run dry,
Empty fields are left untied,
Tests affirm each careful tie.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: passing Zitadel given_name and family_name claims to auto-provisioned WordPress subscribers, which aligns with the core objective of mapping OIDC claims to user profile fields.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/bearer-pass-name-claims

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov-commenter
Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@codacy-production
Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 8 complexity

Metric Results
Complexity 8

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@JohnRDOrazio
Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 6, 2026

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@wordpress/themes/cdcf-headless/includes/auth/zitadel-bearer.php`:
- Around line 286-289: Normalize OIDC name claims by trimming whitespace before
checking for emptiness: for the array keys 'display_name', 'first_name', and
'last_name' in zitadel-bearer.php (the block building the user data passed to
wp_insert_user()), call trim() on the incoming claims (e.g., $userinfo['name'],
$userinfo['given_name'], $userinfo['family_name']) and only use the value if the
trimmed string is non-empty; otherwise omit the key or fall back to the
email/default. Apply the same trim-and-empty check logic to the other similar
block around the 403-410 region so whitespace-only claims won't overwrite WP
defaults.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a119611a-ec31-4c6c-8d4d-2f997ce1cf6c

📥 Commits

Reviewing files that changed from the base of the PR and between 8960ed8 and 4ae5790.

📒 Files selected for processing (2)
  • wordpress/themes/cdcf-headless/includes/auth/zitadel-bearer.php
  • wordpress/themes/cdcf-headless/tests/ZitadelBearerTest.php

Comment on lines +286 to +289
[
'display_name' => is_string($userinfo['name'] ?? null) ? $userinfo['name'] : '',
'first_name' => is_string($userinfo['given_name'] ?? null) ? $userinfo['given_name'] : '',
'last_name' => is_string($userinfo['family_name'] ?? null) ? $userinfo['family_name'] : '',
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Normalize the OIDC name claims before the empty-string checks.

These values come through as raw strings, so whitespace-only claims still pass the !== '' guards here. A token with name = ' ' or given_name = ' ' will write a blank display_name/first_name into wp_insert_user() instead of falling back to the email or omitting the key, which breaks the "don't blank WP defaults" behavior this PR is aiming for.

Proposed fix
         [
-            'display_name' => is_string($userinfo['name'] ?? null) ? $userinfo['name'] : '',
-            'first_name'   => is_string($userinfo['given_name'] ?? null) ? $userinfo['given_name'] : '',
-            'last_name'    => is_string($userinfo['family_name'] ?? null) ? $userinfo['family_name'] : '',
+            'display_name' => is_string($userinfo['name'] ?? null) ? sanitize_text_field($userinfo['name']) : '',
+            'first_name'   => is_string($userinfo['given_name'] ?? null) ? sanitize_text_field($userinfo['given_name']) : '',
+            'last_name'    => is_string($userinfo['family_name'] ?? null) ? sanitize_text_field($userinfo['family_name']) : '',
         ]

Also applies to: 403-410

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@wordpress/themes/cdcf-headless/includes/auth/zitadel-bearer.php` around lines
286 - 289, Normalize OIDC name claims by trimming whitespace before checking for
emptiness: for the array keys 'display_name', 'first_name', and 'last_name' in
zitadel-bearer.php (the block building the user data passed to
wp_insert_user()), call trim() on the incoming claims (e.g., $userinfo['name'],
$userinfo['given_name'], $userinfo['family_name']) and only use the value if the
trimmed string is non-empty; otherwise omit the key or fall back to the
email/default. Apply the same trim-and-empty check logic to the other similar
block around the 403-410 region so whitespace-only claims won't overwrite WP
defaults.

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.

2 participants