Skip to content

fix(backend): expose externalAccountId on ExternalAccount resource#8982

Open
VihaanAgarwal wants to merge 1 commit into
clerk:mainfrom
VihaanAgarwal:fix/backend-external-account-id
Open

fix(backend): expose externalAccountId on ExternalAccount resource#8982
VihaanAgarwal wants to merge 1 commit into
clerk:mainfrom
VihaanAgarwal:fix/backend-external-account-id

Conversation

@VihaanAgarwal

@VihaanAgarwal VihaanAgarwal commented Jun 24, 2026

Copy link
Copy Markdown

Description

The backend ExternalAccount resource maps the API's id field, which holds an idn_-prefixed identification id, and never reads external_account_id (the eac_-prefixed resource id) from the response. The eac_ id is therefore unreachable from the SDK.

This bites anyone deleting an external account. users.deleteUserExternalAccount() builds its path from the eac_ id, so passing getUser().externalAccounts[0].id (an idn_ value) returns 404 external_account_not_found. The only workaround today is a raw fetch to read external_account_id off the JSON.

The raw user response already contains both ids on each external account:

{
  "id": "idn_...",
  "external_account_id": "eac_...",
  "identification_id": "idn_...",
  "provider": "oauth_google"
}

This PR maps external_account_id to a new externalAccountId property on ExternalAccount. id keeps its current value, so the change is additive and backwards compatible. Callers can now do:

const user = await clerk.users.getUser(userId);
await clerk.users.deleteUserExternalAccount({
  userId,
  externalAccountId: user.externalAccounts[0].externalAccountId,
});

Fixes #7936
Fixes #7584

How to test

ExternalAccount.fromJSON is covered by a new unit test in packages/backend/src/api/resources/__tests__/ExternalAccount.test.ts that asserts external_account_id maps to externalAccountId while id and identificationId stay on the idn_ value. The full @clerk/backend suite passes.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

Summary by CodeRabbit

  • New Features

    • External accounts now include an additional identifier for the account itself, alongside the existing identification ID.
    • Data returned from user lookups now exposes both identifiers for clearer account handling.
  • Bug Fixes

    • Deleting external accounts retrieved from user data should now work correctly with the expected account identifier instead of failing with a not-found error.
  • Tests

    • Added coverage to verify external account data is mapped correctly and both identifiers are preserved.

The backend ExternalAccount mapped the API's `id` field, which holds an
`idn_` identification id, and never read `external_account_id` (the `eac_`
resource id). Passing `getUser().externalAccounts[].id` to
`deleteUserExternalAccount()` then returned a 404 because that method
expects the `eac_` id.

Map `external_account_id` to a new `externalAccountId` property so the
resource id is reachable without a raw API call. `id` keeps its existing
value to stay backwards compatible.
@changeset-bot

changeset-bot Bot commented Jun 24, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 8097c8a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 10 packages
Name Type
@clerk/backend Patch
@clerk/astro Patch
@clerk/express Patch
@clerk/fastify Patch
@clerk/hono Patch
@clerk/nextjs Patch
@clerk/nuxt Patch
@clerk/react-router Patch
@clerk/tanstack-react-start Patch
@clerk/testing Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Jun 24, 2026

Copy link
Copy Markdown

@VihaanAgarwal is attempting to deploy a commit to the Clerk Production Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

ExternalAccount now accepts and stores externalAccountId from API JSON, the backend JSON type includes external_account_id, and a new test verifies the mapping and ID preservation. A changeset documents the backend patch release note.

Changes

External account ID mapping

Layer / File(s) Summary
JSON contract and resource mapping
packages/backend/src/api/resources/JSON.ts, packages/backend/src/api/resources/ExternalAccount.ts
ExternalAccountJSON now requires external_account_id, and ExternalAccount stores it as externalAccountId when built from JSON.
Mapping test and release note
packages/backend/src/api/resources/__tests__/ExternalAccount.test.ts, .changeset/external-account-id.md
A new test checks the external_account_id mapping and identification-id preservation, and the changeset records the backend patch note.

Sequence Diagram(s)

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 I hopped through IDs, both old and new,
The eac_ one now comes shining through.
idn_ stays put where it ought to be,
So delete can hop off happily.

🚥 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 clearly describes the main change: exposing externalAccountId on ExternalAccount.
Linked Issues check ✅ Passed The PR exposes external_account_id as externalAccountId while preserving id, matching the issues' requested fix.
Out of Scope Changes check ✅ Passed All changes support the ID-mapping fix and its test; no unrelated code changes are present.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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


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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/backend/src/api/resources/ExternalAccount.ts (1)

10-19: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

New required constructor arg can break existing consumers

Adding externalAccountId as a required positional constructor parameter is not additive for callers that do new ExternalAccount(...); it introduces a source-level breaking change. Consider making it optional with a default, or moving to an options object constructor to preserve compatibility.

🤖 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 `@packages/backend/src/api/resources/ExternalAccount.ts` around lines 10 - 19,
The ExternalAccount constructor change is breaking existing callers because
externalAccountId is now a required positional argument. Update ExternalAccount
to preserve backward compatibility by making externalAccountId optional with a
safe default, or switch the constructor to accept an options object while
keeping existing new ExternalAccount(...) call sites working. Make sure the
change is localized in the ExternalAccount class and any related usages that
rely on its constructor signature.
🤖 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.

Outside diff comments:
In `@packages/backend/src/api/resources/ExternalAccount.ts`:
- Around line 10-19: The ExternalAccount constructor change is breaking existing
callers because externalAccountId is now a required positional argument. Update
ExternalAccount to preserve backward compatibility by making externalAccountId
optional with a safe default, or switch the constructor to accept an options
object while keeping existing new ExternalAccount(...) call sites working. Make
sure the change is localized in the ExternalAccount class and any related usages
that rely on its constructor signature.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: b7af75fd-ef91-4426-a550-a55759eab66f

📥 Commits

Reviewing files that changed from the base of the PR and between f0b0773 and 8097c8a.

📒 Files selected for processing (4)
  • .changeset/external-account-id.md
  • packages/backend/src/api/resources/ExternalAccount.ts
  • packages/backend/src/api/resources/JSON.ts
  • packages/backend/src/api/resources/__tests__/ExternalAccount.test.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant