Skip to content

client SDK organizations.getActiveMember(organizationId) sends an organizationId the server ignores — it answers the session's ACTIVE organization, whatever id the caller names #16568

Description

@claude

Found while binding the organizations.* return types for #14314 (card 3 of the #12104 family). Out of that card's ruled scope (type narrowing only), so recorded here rather than fixed there. No assignee, no label — left for triage.

The contract the SDK declares

packages/client/src/index.ts, organizations.getActiveMember, JSDoc at the merge base 9407e1865:

Look up the calling user's membership row in the given organisation. Useful for permission checks on the client without having to scan the full member list.

and the method builds GET /api/v1/auth/organization/get-active-member?organizationId=ENCODED_ID.

What the route does

better-auth 1.7.2, plugins/organization/routes/crud-members.mjs, getActiveMember: the handler reads session.session.activeOrganizationId only and never looks at ctx.query. The query string the SDK sends is dead on arrival.

Measured

Real AuthManager (better-auth 1.7.2, organization plugin, teams enabled) over a real SqlDriver (better-sqlite3), one user who owns two organizations with org1 active:

GET /organization/get-active-member?organizationId=ORG1  -> 200 { organizationId: ORG1, role: 'owner', ... }
GET /organization/get-active-member?organizationId=ORG2  -> 200 { organizationId: ORG1, role: 'owner', ... }   <- same row, the query was ignored
GET /organization/get-active-member?organizationId=x     -> 400 NO_ACTIVE_ORGANIZATION (user with no active organization)

The same three answers came back through the real ObjectStackClient with only the socket stood in for. A caller doing a permission check for organization B while A is active is told about A — the wrong-but-plausible answer, silently.

Class

Declared contract not delivered (the SDK's own JSDoc and its query parameter promise per-organization addressing the runtime does not do). Reproducible with the three calls above.

Options seen, not decided

  • SDK: stop sending the parameter and rename or re-document the method as the ACTIVE-organization lookup it is (#14314's PR already corrects the JSDoc to say the argument is ignored, but keeps the parameter and the request bytes — a body change was out of its scope).
  • SDK: implement per-organization lookup honestly by calling GET /organization/list-members?organizationId=...&filterField=userId&filterValue=SELF and unwrapping the single row, keeping the signature.
  • Server: an ObjectStack before-hook that swaps the session's active organization for the request is the wrong shape (it would mutate session state to answer a read).

Refs: #14314 (where it was measured), #12104 (family head).


Generated by Claude Code

Activity

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

Metadata

Metadata

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions