Phase6c: Optional Company contact name
All checks were successful
Build and Push / build (push) Successful in 1m42s
All checks were successful
Build and Push / build (push) Successful in 1m42s
This commit is contained in:
@@ -534,20 +534,24 @@ export async function registerCustomer(params: {
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update a human user's profile (first name + last name). Returns
|
* Update a human user's profile (first name + last name + display
|
||||||
* the new `details.changeDate` from ZITADEL so the caller can
|
* name). Returns the new `details.changeDate` from ZITADEL so the
|
||||||
* confirm the write landed.
|
* caller can confirm the write landed.
|
||||||
*
|
*
|
||||||
* The v2 user service endpoint is technically a PUT but accepts
|
* The v2 user service endpoint is technically a PUT but accepts
|
||||||
* partial bodies — only `profile.givenName` and `profile.familyName`
|
* partial bodies — only the `profile` block is sent. ZITADEL
|
||||||
* are sent. ZITADEL preserves email, password, and other fields
|
* preserves email, password, and other fields across the call
|
||||||
* across the call (verified empirically in stripe-node#7786 and
|
* (verified empirically in zitadel-server#7786 and documented in
|
||||||
* documented in v2.63+ of zitadel-server).
|
* v2.63+ of zitadel-server).
|
||||||
*
|
*
|
||||||
* `displayName` is intentionally NOT sent. ZITADEL recomputes it
|
* `displayName` IS sent explicitly, set to "givenName familyName".
|
||||||
* from givenName + familyName when not provided, which is what we
|
* Empirically (and contra what some docs suggest), ZITADEL does
|
||||||
* want — keeping displayName as a frozen value would let it drift
|
* NOT recompute displayName when only the name parts change — it
|
||||||
* out of sync with the name parts on subsequent edits.
|
* keeps whatever displayName was previously stored, including the
|
||||||
|
* one set at user creation time. That stale displayName is what
|
||||||
|
* ZITADEL surfaces in the OIDC `name` claim, so without this
|
||||||
|
* explicit write the portal session would never see the updated
|
||||||
|
* name (even after sign-out / sign-in).
|
||||||
*
|
*
|
||||||
* Auth: the portal's service-account PAT (ZITADEL_SA_PAT). The PAT
|
* Auth: the portal's service-account PAT (ZITADEL_SA_PAT). The PAT
|
||||||
* must have user-write permission in the user's resource org.
|
* must have user-write permission in the user's resource org.
|
||||||
@@ -556,7 +560,8 @@ export async function registerCustomer(params: {
|
|||||||
*/
|
*/
|
||||||
export interface UpdateHumanUserProfileResult {
|
export interface UpdateHumanUserProfileResult {
|
||||||
changeDate: string;
|
changeDate: string;
|
||||||
/** ZITADEL recomputes this from given+family unless overridden. */
|
/** The displayName ZITADEL stored, which the OIDC `name` claim will
|
||||||
|
* carry on the user's next session. */
|
||||||
displayName: string;
|
displayName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -566,6 +571,11 @@ export async function updateHumanUserProfile(params: {
|
|||||||
familyName: string;
|
familyName: string;
|
||||||
}): Promise<UpdateHumanUserProfileResult> {
|
}): Promise<UpdateHumanUserProfileResult> {
|
||||||
const path = `/v2/users/human/${encodeURIComponent(params.userId)}`;
|
const path = `/v2/users/human/${encodeURIComponent(params.userId)}`;
|
||||||
|
// Compose the displayName ourselves so ZITADEL stores something
|
||||||
|
// sensible. Empty-string fallback only triggers if both name parts
|
||||||
|
// are blank, which the API zod schema prevents anyway.
|
||||||
|
const displayName =
|
||||||
|
`${params.givenName.trim()} ${params.familyName.trim()}`.trim();
|
||||||
type ZitadelUpdateResponse = {
|
type ZitadelUpdateResponse = {
|
||||||
details?: { changeDate?: string };
|
details?: { changeDate?: string };
|
||||||
};
|
};
|
||||||
@@ -573,15 +583,16 @@ export async function updateHumanUserProfile(params: {
|
|||||||
profile: {
|
profile: {
|
||||||
givenName: params.givenName,
|
givenName: params.givenName,
|
||||||
familyName: params.familyName,
|
familyName: params.familyName,
|
||||||
|
displayName,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
// Re-fetch the user so we can return the canonical displayName
|
// Re-fetch the user to read back the canonical displayName ZITADEL
|
||||||
// (ZITADEL computes "Given Family" itself; matching what NextAuth
|
// committed. Should match what we sent, but reading from the source
|
||||||
// sees in the next sign-in claim).
|
// of truth catches any sanitization ZITADEL might apply.
|
||||||
const detail = await getHumanUserDetail(params.userId);
|
const detail = await getHumanUserDetail(params.userId);
|
||||||
return {
|
return {
|
||||||
changeDate: new Date().toISOString(),
|
changeDate: new Date().toISOString(),
|
||||||
displayName: detail.displayName,
|
displayName: detail.displayName || displayName,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user