feat: add Google Workspace phone sync#11
Conversation
WalkthroughAdds a PostAuthentication workflow that, for a specific Google Workspace connection, extracts a SAML "phone" attribute and updates the authenticated Kinde user's Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant U as User
participant IdP as Google Workspace (SAML)
participant Auth as Auth System
participant WF as PostAuth Workflow
participant KA as Kinde API
U->>Auth: Authenticate (Google Workspace)
Auth->>IdP: SAML AuthNRequest
IdP-->>Auth: SAML Response (Assertion + Attributes)
Auth-->>WF: Trigger PostAuthentication(event)
WF->>WF: Verify connectionId == GOOGLE_WORKSPACE_CONNECTION_ID
alt No match or missing assertion/phone
WF-->>Auth: Abort (no-op)
else Phone present
WF->>WF: Extract first "phone" attribute (case-insensitive)
WF->>KA: createKindeAPI(event) (M2M)
KA-->>WF: Client ready
WF->>KA: PUT users/{userId}/properties/phone_number?value={encodedPhone}
KA-->>WF: 2xx Updated
WF-->>Auth: Completed
end
note right of WF: Env: KINDE_WF_M2M_CLIENT_ID, KINDE_WF_M2M_CLIENT_SECRET, GOOGLE_WORKSPACE_CONNECTION_ID
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related PRs
Suggested reviewers
Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. 📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 💡 Knowledge Base configuration:
You can enable these sources in your CodeRabbit configuration. 📒 Files selected for processing (1)
✅ Files skipped from review due to trivial changes (1)
✨ Finishing Touches🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
postUserAuthentication/syncUserPhoneGoogleWorkspace.ts (2)
37-48: Make the workflow id unique and consider fail-open behavior for reliability.
- The
idshould be unique per workflow."postAuthentication"is generic and risks collisions.- With
failurePolicy.action: "stop", a transient Management API error will block sign-in. Considercontinueunless you intentionally want hard-fail.export const workflowSettings: WorkflowSettings = { - id: "postAuthentication", + id: "syncUserPhoneGoogleWorkspace", name: "GoogleWorkspacePhoneSync", failurePolicy: { - action: "stop", + action: "continue", }, trigger: WorkflowTrigger.PostAuthentication,
71-73: Optionally normalize the phone value before persisting.Light normalization (e.g., trim spaces and remove formatting chars) can make downstream usage more consistent. Keep the leading “+” if present.
- const phoneValue = phoneAttr?.values?.[0]?.value?.trim() || null; - if (!phoneValue) return; + const rawPhone = phoneAttr?.values?.[0]?.value?.trim() || null; + if (!rawPhone) return; + // Basic normalization: keep digits and a leading '+' + const phoneValue = rawPhone.replace(/(?!^\+)[^\d]/g, ""); + if (!phoneValue) return;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
postUserAuthentication/syncUserPhoneGoogleWorkspace.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-06-19T06:39:44.342Z
Learnt from: ages96
PR: kinde-starter-kits/workflow-examples#6
File: postUserAuthentication/impossibleTravelWorkflow.ts:0-0
Timestamp: 2025-06-19T06:39:44.342Z
Learning: In Kinde's postUserAuthentication workflows, after the OTP authentication step, event.context.user consistently returns null for fields like name and email, with only the user ID being available. Therefore, kindeAPI.get() is required to retrieve the full user details including name and email fields.
Applied to files:
postUserAuthentication/syncUserPhoneGoogleWorkspace.ts
🧬 Code Graph Analysis (1)
postUserAuthentication/syncUserPhoneGoogleWorkspace.ts (4)
postUserAuthentication/syncNewUserToHubspotWorkflow.ts (2)
handlePostAuth(45-123)extractMatchingProperties(69-79)newPassword/securelySyncPasswordWorkflow.ts (1)
Workflow(30-56)postUserAuthentication/checkIPWithAbuseIPDBWorkflow.ts (2)
handlePostAuth(237-394)validateConfiguration(121-166)m2mToken/mapOrgToM2MApplicationWorkflow.ts (1)
Workflow(43-74)
🔇 Additional comments (2)
postUserAuthentication/syncUserPhoneGoogleWorkspace.ts (2)
74-76: LGTM on using onlyevent.context.user.id.This aligns with prior learnings: in post-auth workflows, only the user ID is guaranteed; other fields can be null and should be fetched via the API if needed.
79-81: Confirmed — PUT with ?value= is correct for single-property updatesKinde docs show PUT /api/v1/users/{user_id}/properties/{property_key}?value=... for updating a single property; PATCH with a JSON body is for bulk updates. Your PUT call is valid.
- Location: postUserAuthentication/syncUserPhoneGoogleWorkspace.ts (lines 79–81) — await kindeAPI.put({ endpoint:
users/${userId}/properties/${phonePropertyKey}?value=${encodeURIComponent(phoneValue)}})Notes:
- Boolean properties may require "on"/"off" when sent via the PUT/query method; use PATCH if you need more flexible typing.
- Ensure the Management API token has the update:user_properties scope.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (4)
postUserAuthentication/syncUserPhoneGoogleWorkspace.ts (4)
21-25: Document optional env vars to remove code changes for attribute/property names.Make it clear contributors can configure these without editing code.
Apply this diff to the inline docs:
// * KINDE_WF_M2M_CLIENT_ID // * KINDE_WF_M2M_CLIENT_SECRET - Ensure this is setup with sensitive flag // enabled to prevent accidental sharing // * GOOGLE_WORKSPACE_CONNECTION_ID +// * GOOGLE_PHONE_ATTR_NAME (optional; defaults to "phone") +// * PHONE_PROPERTY_KEY (optional; defaults to "phone_number") //
64-66: Make the Google phone attribute name configurable via env var.Avoid future code edits when teams use a different attribute name.
- const googlePhoneAttributeName = "phone"; + const googlePhoneAttributeName = + getEnvironmentVariable("GOOGLE_PHONE_ATTR_NAME")?.value?.toLowerCase().trim() ?? "phone";
69-71: Optional: normalize/validate phone before persisting for better downstream consistency.Even basic sanitization helps avoid saving accidental spaces, dashes, or parens. If you don’t want to add a dependency (e.g., libphonenumber-js), a light sanitizer works.
Apply this diff in-place:
- const phoneValue = phoneAttr?.values?.[0]?.value?.trim() || null; - if (!phoneValue) return; + const rawPhone = phoneAttr?.values?.[0]?.value ?? ""; + const phoneValue = sanitizePhone(rawPhone.trim()); + if (!phoneValue) return;Add this helper somewhere near the top of the file:
function sanitizePhone(phone: string): string | null { // Keep digits and a single leading plus; drop other punctuation/spaces. const cleaned = phone.replace(/[^\d+]/g, "").replace(/(?!^)\+/g, ""); return cleaned.length ? cleaned : null; }If you do prefer full validation/formatting, consider libphonenumber-js and parse to E.164.
72-80: Wrap the Management API update in try/catch and make the property key configurable.This preserves the stop-on-failure behavior but surfaces actionable logs, and avoids code edits when the property key differs.
- const kindeAPI = await createKindeAPI(event); - const userId = event.context.user.id; - - const phonePropertyKey = "phone_number"; - - await kindeAPI.put({ - endpoint: `users/${userId}/properties/${phonePropertyKey}?value=${encodeURIComponent(phoneValue)}` - }); + const kindeAPI = await createKindeAPI(event); + const userId = event.context.user.id; + const phonePropertyKey = getEnvironmentVariable("PHONE_PROPERTY_KEY")?.value ?? "phone_number"; + try { + await kindeAPI.put({ + endpoint: `users/${userId}/properties/${phonePropertyKey}?value=${encodeURIComponent(phoneValue)}` + }); + } catch (err) { + console.error("Failed to update user phone property", { userId, phonePropertyKey, err }); + throw err; // honor failurePolicy: stop + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
postUserAuthentication/syncUserPhoneGoogleWorkspace.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-06-19T06:39:44.342Z
Learnt from: ages96
PR: kinde-starter-kits/workflow-examples#6
File: postUserAuthentication/impossibleTravelWorkflow.ts:0-0
Timestamp: 2025-06-19T06:39:44.342Z
Learning: In Kinde's postUserAuthentication workflows, after the OTP authentication step, event.context.user consistently returns null for fields like name and email, with only the user ID being available. Therefore, kindeAPI.get() is required to retrieve the full user details including name and email fields.
Applied to files:
postUserAuthentication/syncUserPhoneGoogleWorkspace.ts
🧬 Code Graph Analysis (1)
postUserAuthentication/syncUserPhoneGoogleWorkspace.ts (1)
postUserAuthentication/syncNewUserToHubspotWorkflow.ts (2)
handlePostAuth(45-123)extractMatchingProperties(69-79)
🔇 Additional comments (4)
postUserAuthentication/syncUserPhoneGoogleWorkspace.ts (4)
37-48: Workflow settings look solid (PostAuthentication + stop-on-failure).Bindings are minimal and correct for this workflow. Nothing to change here.
55-57: Good guard: prevents accidental execution when env var is missing or connection doesn’t match.This addresses the earlier feedback around undefined values short-circuiting.
66-69: Safe attribute name comparison (no method calls on undefined).Using (a.name ?? "").toLowerCase().trim() avoids the runtime error noted in earlier feedback. Good.
72-74: Using only userId aligns with post-auth constraints.Based on prior learnings, only userId is reliably present at this step; not relying on name/email is correct.
…orkspaceWorkflow.ts
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (3)
postUserAuthentication/syncUserPhoneGoogleWorkspaceWorkflow.ts (3)
44-47: Drop unused url binding to minimize surface area.You’re not using an outbound URL binding here. Remove it to keep the workflow principle of least privilege.
bindings: { "kinde.env": {}, - url: {}, },
64-69: Support common phone attribute aliases and allow an env override.Google Admins often use aliases like telephoneNumber, mobile, or phoneNumber. Accept a small set and allow an environment override to avoid code edits.
- const googlePhoneAttributeName = "phone"; - - const phoneAttr = attributeStatements - .flatMap((s) => s.attributes ?? []) - .find((a) => (a.name ?? "").toLowerCase().trim() === googlePhoneAttributeName); + const googlePhoneAttributeNames = new Set( + ["phone", "phonenumber", "telephonenumber", "mobile", "mobilephone"].map((n) => n.toLowerCase()) + ); + const envPhoneAttr = getEnvironmentVariable("GOOGLE_WORKSPACE_PHONE_ATTRIBUTE_NAME")?.value; + if (envPhoneAttr) { + googlePhoneAttributeNames.add(envPhoneAttr.toLowerCase().trim()); + } + + const phoneAttr = attributeStatements + .flatMap((s) => s.attributes ?? []) + .find((a) => googlePhoneAttributeNames.has((a.name ?? "").toLowerCase().trim()));
75-76: Make the target user property configurable.Let ops change the destination key without code changes.
- const phonePropertyKey = "phone_number"; + const phonePropertyKey = + getEnvironmentVariable("PHONE_PROPERTY_KEY")?.value ?? "phone_number";
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
postUserAuthentication/syncUserPhoneGoogleWorkspaceWorkflow.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-06-19T06:39:44.342Z
Learnt from: ages96
PR: kinde-starter-kits/workflow-examples#6
File: postUserAuthentication/impossibleTravelWorkflow.ts:0-0
Timestamp: 2025-06-19T06:39:44.342Z
Learning: In Kinde's postUserAuthentication workflows, after the OTP authentication step, event.context.user consistently returns null for fields like name and email, with only the user ID being available. Therefore, kindeAPI.get() is required to retrieve the full user details including name and email fields.
Applied to files:
postUserAuthentication/syncUserPhoneGoogleWorkspaceWorkflow.ts
🧬 Code Graph Analysis (1)
postUserAuthentication/syncUserPhoneGoogleWorkspaceWorkflow.ts (1)
postUserAuthentication/syncNewUserToHubspotWorkflow.ts (2)
handlePostAuth(45-123)extractMatchingProperties(69-79)
🔇 Additional comments (4)
postUserAuthentication/syncUserPhoneGoogleWorkspaceWorkflow.ts (4)
50-53: Types for SAML parsing look good.The minimal optional field modeling is appropriate for parsing loosely-typed provider payloads.
54-58: Good early-return gating by connection.Nice guard to ensure the workflow runs only for the intended Google Workspace connection.
58-61: Verify provider assertion path.Confirm that SAML attributes are consistently at event.context.auth.provider.data.assertion.attributeStatements for Google Workspace; providers sometimes nest under slightly different keys. If this varies, add a defensive fallback or a type guard.
Would you like me to generate a quick probe script to dump a redacted shape of event.context.auth during a dev login to validate the path?
77-79: VerifykindeAPI.putsupports a request body before refactoringI wasn’t able to locate a local definition of
kindeAPI.putin the repo – it’s likely coming from an external package. Please double-check the package’s TypeScript definitions or API docs to confirm that the.putmethod accepts abodyproperty. If it does not, you’ll need to either:
- Switch to using
kindeAPI.fetch(or whatever low-level client method supports bodies), or- Keep the query string approach but take extra care to strip PII from logs/metrics.
Once you’ve confirmed it’s safe, here’s the suggested refactor to avoid embedding the phone number in the URL and to handle failures gracefully:
- await kindeAPI.put({ - endpoint: `users/${userId}/properties/${phonePropertyKey}?value=${encodeURIComponent(phoneValue)}` - }); + try { + await kindeAPI.put({ + endpoint: `users/${userId}/properties/${phonePropertyKey}`, + body: { value: phoneValue }, // avoid PII in URLs + }); + } catch (err: any) { + // Don’t log the raw phone number; only surface safe metadata. + console.warn("GoogleWorkspacePhoneSync: failed to update phone property; continuing login.", { + userId, + reason: err?.message ?? String(err), + }); + }
- Target file:
postUserAuthentication/syncUserPhoneGoogleWorkspaceWorkflow.ts(around lines 77–79)
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
postUserAuthentication/syncUserPhoneGoogleWorkspaceWorkflow.ts (1)
37-48: ID collision resolved and failure policy set to continue — good.
Unique workflow id and non-blocking failure policy align with repo guidance and prior feedback.
🧹 Nitpick comments (6)
postUserAuthentication/syncUserPhoneGoogleWorkspaceWorkflow.ts (6)
44-47: Remove unused binding "url".
Not referenced; drop to reduce noise.bindings: { "kinde.env": {}, - url: {}, },
72-74: Defensive guard for missing user id.
Rare, but avoids throwing if user is absent.- const kindeAPI = await createKindeAPI(event); - const userId = event.context.user.id; + const kindeAPI = await createKindeAPI(event); + const userId = event.context.user?.id; + if (!userId) return;
75-76: Allow overriding the property key via env var.
Avoids code edits for different installations.- const phonePropertyKey = "phone_number"; + const phonePropertyKey = + getEnvironmentVariable("PHONE_PROPERTY_KEY")?.value || "phone_number";
82-87: Include raw error for better diagnostics.
Helps capture stack/cause without leaking PII.- console.error("Phone sync failed", { - userId, - phonePropertyKey, - message: err instanceof Error ? err.message : String(err), - }); + console.error("Phone sync failed", { + userId, + phonePropertyKey, + message: err instanceof Error ? err.message : String(err), + error: err, + });
58-71: Consider extracting SAML attribute parsing into a shared helper.
syncAttributesSamlWorkflow.tsalready builds a normalized map; reuse to avoid drift.
1-36: Add README entry and configuration table.
Document required env vars (GOOGLE_WORKSPACE_CONNECTION_ID, KINDE_WF_M2M_CLIENT_ID/SECRET, optional DEFAULT_E164_COUNTRY_CODE, PHONE_PROPERTY_KEY, GOOGLE_WORKSPACE_PHONE_ATTR_NAMES) and Google Admin mapping steps.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
postUserAuthentication/syncUserPhoneGoogleWorkspaceWorkflow.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-06-19T06:39:44.342Z
Learnt from: ages96
PR: kinde-starter-kits/workflow-examples#6
File: postUserAuthentication/impossibleTravelWorkflow.ts:0-0
Timestamp: 2025-06-19T06:39:44.342Z
Learning: In Kinde's postUserAuthentication workflows, after the OTP authentication step, event.context.user consistently returns null for fields like name and email, with only the user ID being available. Therefore, kindeAPI.get() is required to retrieve the full user details including name and email fields.
Applied to files:
postUserAuthentication/syncUserPhoneGoogleWorkspaceWorkflow.ts
🧬 Code graph analysis (1)
postUserAuthentication/syncUserPhoneGoogleWorkspaceWorkflow.ts (2)
postUserAuthentication/syncNewUserToHubspotWorkflow.ts (2)
handlePostAuth(45-123)extractMatchingProperties(69-79)postUserAuthentication/syncAttributesSamlWorkflow.ts (2)
handlePostAuth(73-127)acc(84-95)
🔇 Additional comments (2)
postUserAuthentication/syncUserPhoneGoogleWorkspaceWorkflow.ts (2)
77-81: Discard PATCH suggestion—kindeAPI.patchisn’t implemented
ThekindeAPIclient only exposesget,post, andput; nopatchcalls exist in the codebase.Likely an incorrect or invalid review comment.
54-58: Verify availability ofauth.provider.protocolin the event type — Confirm thatevent.context.auth.provider.protocolis defined ononPostAuthenticationEvent; if not, extend the type or retrieve the protocol from the correct property before adding the SAML guard.
| const googlePhoneAttributeName = "phone"; | ||
|
|
||
| const phoneAttr = attributeStatements | ||
| .flatMap((s) => s.attributes ?? []) | ||
| .find((a) => (a.name ?? "").toLowerCase().trim() === googlePhoneAttributeName); | ||
| const phoneValue = phoneAttr?.values?.[0]?.value?.trim() || null; | ||
| if (!phoneValue) return; | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
Make attribute name configurable and normalize phone to E.164-ish.
Accept common SAML names via env var and lightly sanitize the value.
- const googlePhoneAttributeName = "phone";
-
- const phoneAttr = attributeStatements
- .flatMap((s) => s.attributes ?? [])
- .find((a) => (a.name ?? "").toLowerCase().trim() === googlePhoneAttributeName);
- const phoneValue = phoneAttr?.values?.[0]?.value?.trim() || null;
- if (!phoneValue) return;
+ const configuredNames = getEnvironmentVariable("GOOGLE_WORKSPACE_PHONE_ATTR_NAMES")?.value;
+ const phoneAttrNames = (configuredNames ? configuredNames.split(",") : ["phone", "telephonenumber", "urn:oid:2.5.4.20"])
+ .map((n) => n.toLowerCase().trim());
+
+ const phoneAttr = attributeStatements
+ .flatMap((s) => s.attributes ?? [])
+ .find((a) => phoneAttrNames.includes((a.name ?? "").toLowerCase().trim()));
+
+ const phoneValueRaw =
+ phoneAttr?.values?.map((v) => v.value?.trim()).filter((v): v is string => !!v)?.[0] ?? null;
+ if (!phoneValueRaw) return;
+
+ // Minimal sanitization toward E.164; optionally prepend default country code (e.g., "1" for US)
+ const defaultCountry = getEnvironmentVariable("DEFAULT_E164_COUNTRY_CODE")?.value; // optional
+ const digitsPlus = phoneValueRaw.replace(/[^\d+]/g, "");
+ const phoneToStore = digitsPlus.startsWith("+")
+ ? digitsPlus
+ : defaultCountry && /^\d{7,15}$/.test(digitsPlus)
+ ? `+${defaultCountry}${digitsPlus}`
+ : digitsPlus;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const googlePhoneAttributeName = "phone"; | |
| const phoneAttr = attributeStatements | |
| .flatMap((s) => s.attributes ?? []) | |
| .find((a) => (a.name ?? "").toLowerCase().trim() === googlePhoneAttributeName); | |
| const phoneValue = phoneAttr?.values?.[0]?.value?.trim() || null; | |
| if (!phoneValue) return; | |
| // Allow overriding the list of SAML attribute names via env; fallback to common defaults | |
| const configuredNames = getEnvironmentVariable("GOOGLE_WORKSPACE_PHONE_ATTR_NAMES")?.value; | |
| const phoneAttrNames = (configuredNames | |
| ? configuredNames.split(",") | |
| : ["phone", "telephonenumber", "urn:oid:2.5.4.20"] | |
| ) | |
| .map((n) => n.toLowerCase().trim()); | |
| const phoneAttr = attributeStatements | |
| .flatMap((s) => s.attributes ?? []) | |
| .find((a) => phoneAttrNames.includes((a.name ?? "").toLowerCase().trim())); | |
| const phoneValueRaw = | |
| phoneAttr | |
| ?.values | |
| ?.map((v) => v.value?.trim()) | |
| .filter((v): v is string => !!v) | |
| ?.[0] ?? null; | |
| if (!phoneValueRaw) return; | |
| // Minimal sanitization toward E.164; optionally prepend default country code (e.g., "1" for US) | |
| const defaultCountry = getEnvironmentVariable("DEFAULT_E164_COUNTRY_CODE")?.value; | |
| const digitsPlus = phoneValueRaw.replace(/[^\d+]/g, ""); | |
| const phoneToStore = digitsPlus.startsWith("+") | |
| ? digitsPlus | |
| : defaultCountry && /^\d{7,15}$/.test(digitsPlus) | |
| ? `+${defaultCountry}${digitsPlus}` | |
| : digitsPlus; |
🤖 Prompt for AI Agents
In postUserAuthentication/syncUserPhoneGoogleWorkspaceWorkflow.ts around lines
64 to 71, make the phone attribute name configurable and normalize the extracted
phone into an E.164-ish form: read a comma-separated env var (e.g.
GOOGLE_PHONE_ATTRIBUTE_NAMES) defaulting to common names like
"phone,telephoneNumber,mobile,phoneNumber" and match attribute.name by
lowercasing and trimming against any of those values; when extracting
phoneValue, lightly sanitize by trimming, removing common
separators/parentheses/labels and non-digit characters (preserve a leading + if
present), strip extensions (e.g. after "ext" or "x"), then if the result is
digits add a '+' prefix to produce a normalized value, otherwise treat as null;
ensure the code uses the first matching attribute and returns early only if the
normalized phone is null/empty.
Explain your changes
This workflow syncs a user's phone number from a Google Workspace SAML attribute into the
phone_numberproperty in Kinde after authentication.Checklist
🛟 If you need help, consider asking for advice over in the Kinde community.
Summary by CodeRabbit
New Features
Documentation