Skip to content

feat: add Google Workspace phone sync#11

Open
abdelrahman-zaki wants to merge 6 commits into
kinde-starter-kits:mainfrom
abdelrahman-zaki:feat/google-workspace-sync-user-phone
Open

feat: add Google Workspace phone sync#11
abdelrahman-zaki wants to merge 6 commits into
kinde-starter-kits:mainfrom
abdelrahman-zaki:feat/google-workspace-sync-user-phone

Conversation

@abdelrahman-zaki
Copy link
Copy Markdown
Contributor

@abdelrahman-zaki abdelrahman-zaki commented Aug 15, 2025

Explain your changes

This workflow syncs a user's phone number from a Google Workspace SAML attribute into the phone_number property in Kinde after authentication.

Checklist

🛟 If you need help, consider asking for advice over in the Kinde community.

Summary by CodeRabbit

  • New Features

    • Added a post-authentication workflow that syncs a user’s Google Workspace phone number into their profile when signing in via the designated Google Workspace connection; runs only for that connection and updates the profile if a phone number is present.
  • Documentation

    • Added examples and setup guidance for SAML attribute syncs, including Google Workspace and Okta phone/attribute sync instructions.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Aug 15, 2025

Walkthrough

Adds a PostAuthentication workflow that, for a specific Google Workspace connection, extracts a SAML "phone" attribute and updates the authenticated Kinde user's phone_number property via the Kinde API. Exports workflowSettings and a default handlePostAuth handler.

Changes

Cohort / File(s) Summary
Post-authentication workflow
postUserAuthentication/syncUserPhoneGoogleWorkspaceWorkflow.ts
New PostAuthentication workflow: checks GOOGLE_WORKSPACE_CONNECTION_ID; parses SAML assertion attributes (case-insensitive "phone"); takes first value; creates Kinde API client via createKindeAPI(event) and updates users/{userId}/properties/phone_number?value={urlEncodedPhone}. Exports workflowSettings and default handlePostAuth(event).
Documentation / Examples
README.md
Adds three example entries describing SAML attribute sync workflows including Google Workspace phone sync, generic SAML attribute/group sync, and Okta attribute sync. No deletions.

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
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Suggested reviewers

  • DaveOrDead
  • DanielRivers

Poem

I hop through SAML fields, ears high and keen,
Sniff a “phone” in the grass—ah, shiny and clean!
With a twitch of my nose, to Kinde I scoot,
Patch the user’s burrow with a neat little route.
Connection checked, update done—thump-thump, salute! 🐇📞

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 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 6dab42a and a89e13d.

📒 Files selected for processing (1)
  • README.md (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • README.md
✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbit in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbit in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbit gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbit read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbit help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbit ignore or @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbit summary or @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbit or @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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: 2

🧹 Nitpick comments (2)
postUserAuthentication/syncUserPhoneGoogleWorkspace.ts (2)

37-48: Make the workflow id unique and consider fail-open behavior for reliability.

  • The id should be unique per workflow. "postAuthentication" is generic and risks collisions.
  • With failurePolicy.action: "stop", a transient Management API error will block sign-in. Consider continue unless 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 3d6b6cc and 08f7485.

📒 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 only event.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 updates

Kinde 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.

Comment thread postUserAuthentication/syncUserPhoneGoogleWorkspace.ts Outdated
Comment thread postUserAuthentication/syncUserPhoneGoogleWorkspace.ts Outdated
abdelrahman-zaki and others added 2 commits August 15, 2025 19:54
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
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: 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.

📥 Commits

Reviewing files that changed from the base of the PR and between be1c5f6 and 937d698.

📒 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.

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: 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 937d698 and ae59774.

📒 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: Verify kindeAPI.put supports a request body before refactoring

I wasn’t able to locate a local definition of kindeAPI.put in 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 .put method accepts a body property. 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)

Comment thread postUserAuthentication/syncUserPhoneGoogleWorkspaceWorkflow.ts
Comment thread postUserAuthentication/syncUserPhoneGoogleWorkspaceWorkflow.ts
Comment thread postUserAuthentication/syncUserPhoneGoogleWorkspaceWorkflow.ts Outdated
Comment thread postUserAuthentication/syncUserPhoneGoogleWorkspaceWorkflow.ts
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

♻️ 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.ts already 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.

📥 Commits

Reviewing files that changed from the base of the PR and between ae59774 and 6dab42a.

📒 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.patch isn’t implemented
The kindeAPI client only exposes get, post, and put; no patch calls exist in the codebase.

Likely an incorrect or invalid review comment.


54-58: Verify availability of auth.provider.protocol in the event type — Confirm that event.context.auth.provider.protocol is defined on onPostAuthenticationEvent; if not, extend the type or retrieve the protocol from the correct property before adding the SAML guard.

Comment on lines +64 to +71
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;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ 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.

Suggested change
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.

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