Skip to content

Commit 6f9a16d

Browse files
yamitzkyclaude
andauthored
fix(scim-users): use SCIM filter path when updating email (#15)
Slack's SCIM PATCH rejects `replace` with path=emails and an array value containing a primary entry: SCIM API error (400): Multi-valued attributes can not have more than one primary element RFC 7644 §3.5.2 specifies that `replace` on a multi-valued attribute replaces the whole array, but Slack's implementation appears to treat it as an add/merge. Using the filter path `emails[primary eq true].value` updates only the primary email's value and is accepted. Verified against a live Enterprise Grid workspace. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent d9358a2 commit 6f9a16d

2 files changed

Lines changed: 11 additions & 1 deletion

File tree

src/commands/scim-users/update.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export async function executeScimUsersUpdate(
2020

2121
if (opts.active !== undefined) operations.push({ op: "replace", path: "active", value: opts.active });
2222
if (opts.userName !== undefined) operations.push({ op: "replace", path: "userName", value: opts.userName });
23-
if (opts.email !== undefined) operations.push({ op: "replace", path: "emails", value: [{ value: opts.email, primary: true }] });
23+
if (opts.email !== undefined) operations.push({ op: "replace", path: 'emails[primary eq true].value', value: opts.email });
2424
if (opts.givenName !== undefined) operations.push({ op: "replace", path: "name.givenName", value: opts.givenName });
2525
if (opts.familyName !== undefined) operations.push({ op: "replace", path: "name.familyName", value: opts.familyName });
2626
if (opts.displayName !== undefined) operations.push({ op: "replace", path: "displayName", value: opts.displayName });

tests/commands/scim-users/update.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@ describe("scim-users update", () => {
2424
]);
2525
});
2626

27+
test("updates email using SCIM filter path for primary address", async () => {
28+
const updated = { id: "U001", emails: [{ value: "new@example.com", primary: true }] };
29+
const mockUpdate = mock(() => Promise.resolve(updated));
30+
const client = { users: { update: mockUpdate } } as any;
31+
await executeScimUsersUpdate(client, { id: "U001", email: "new@example.com" });
32+
expect(mockUpdate).toHaveBeenCalledWith("U001", [
33+
{ op: "replace", path: 'emails[primary eq true].value', value: "new@example.com" },
34+
]);
35+
});
36+
2737
test("throws when no fields provided", async () => {
2838
const client = { users: { update: mock(() => Promise.resolve({})) } } as any;
2939
await expect(executeScimUsersUpdate(client, { id: "U001" })).rejects.toThrow(

0 commit comments

Comments
 (0)