[GH-ISSUE #9133] SSO: registerSSOProvider missing role check allows any org member to register providers #19914

Open
opened 2026-04-15 19:16:59 -05:00 by GiteaMirror · 1 comment
Owner

Originally created by @robingenz on GitHub (Apr 12, 2026).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/9133

Is this suited for github?

  • Yes, this is suited for github

Reproduction

  1. Set up Better Auth with the SSO and organization plugins
  2. Create an organization and add a user with the member role (not owner or admin)
  3. As that member, call POST /sso/register with an organizationId in the body
  4. The provider is created successfully despite the user only being a regular member

Relevant code in packages/sso/src/routes/sso.ts (line 631–649): the endpoint only checks whether the user is a member of the organization, but does not verify the member's role.

Current vs. Expected behavior

Current: Any organization member (including regular members) can register SSO providers for the organization. The check only verifies membership, not role:

const organization = await ctx.context.adapter.findOne({
    model: "member",
    where: [
        { field: "userId", value: user.id },
        { field: "organizationId", value: ctx.body.organizationId },
    ],
});
if (!organization) {
    throw new APIError("BAD_REQUEST", {
        message: "You are not a member of the organization",
    });
}

Expected: Only organization owner or admin roles should be allowed to register SSO providers, consistent with the other provider endpoints (getSSOProvider, updateSSOProvider, deleteSSOProvider) which all use checkProviderAccess and require owner or admin role via isOrgAdmin.

What version of Better Auth are you using?

1.6.2

System info

N/A

Which area(s) are affected? (Select all that apply)

Backend

Auth config (if applicable)

N/A — the issue is in the SSO plugin's internal route logic, not user configuration.

Additional context

The other three provider CRUD endpoints all go through checkProviderAccess() (packages/sso/src/routes/providers.ts:240) which calls isOrgAdmin() and restricts access to ["owner", "admin"] roles. The registerSSOProvider endpoint is the only one missing this role check.

Originally created by @robingenz on GitHub (Apr 12, 2026). Original GitHub issue: https://github.com/better-auth/better-auth/issues/9133 ### Is this suited for github? - [x] Yes, this is suited for github ### Reproduction 1. Set up Better Auth with the SSO and organization plugins 2. Create an organization and add a user with the `member` role (not `owner` or `admin`) 3. As that member, call `POST /sso/register` with an `organizationId` in the body 4. The provider is created successfully despite the user only being a regular member Relevant code in `packages/sso/src/routes/sso.ts` (line 631–649): the endpoint only checks whether the user is a member of the organization, but does not verify the member's role. ### Current vs. Expected behavior **Current:** Any organization member (including regular members) can register SSO providers for the organization. The check only verifies membership, not role: ```ts const organization = await ctx.context.adapter.findOne({ model: "member", where: [ { field: "userId", value: user.id }, { field: "organizationId", value: ctx.body.organizationId }, ], }); if (!organization) { throw new APIError("BAD_REQUEST", { message: "You are not a member of the organization", }); } ``` **Expected:** Only organization `owner` or `admin` roles should be allowed to register SSO providers, consistent with the other provider endpoints (`getSSOProvider`, `updateSSOProvider`, `deleteSSOProvider`) which all use `checkProviderAccess` and require `owner` or `admin` role via `isOrgAdmin`. ### What version of Better Auth are you using? 1.6.2 ### System info N/A ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) N/A — the issue is in the SSO plugin's internal route logic, not user configuration. ### Additional context The other three provider CRUD endpoints all go through `checkProviderAccess()` (`packages/sso/src/routes/providers.ts:240`) which calls `isOrgAdmin()` and restricts access to `["owner", "admin"]` roles. The `registerSSOProvider` endpoint is the only one missing this role check.
GiteaMirror added the security label 2026-04-15 19:16:59 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Apr 12, 2026):

Confirmed — this is a valid missing role check. The registerSSOProvider endpoint only verifies organization membership without checking the member's role [1], while the other three provider endpoints (getSSOProvider, updateSSOProvider, deleteSSOProvider) all go through checkProviderAccessisOrgAdmin, which restricts access to ["owner", "admin"] roles [2].

The fix should add an isOrgAdmin check [3] after verifying membership in registerSSOProvider, something like:

const member = await ctx.context.adapter.findOne<Member>({
    model: "member",
    where: [
        { field: "userId", value: session.user.id },
        { field: "organizationId", value: body.organizationId },
    ],
});
if (!member) {
    throw new APIError("BAD_REQUEST", {
        message: "You are not a member of the organization",
    });
}
// Add role check to match other provider endpoints
const hasAccess = await isOrgAdmin(ctx, session.user.id, body.organizationId);
if (!hasAccess) {
    throw new APIError("FORBIDDEN", {
        message: "You must be an organization owner or admin to register SSO providers",
    });
}

This would make registerSSOProvider consistent with the other CRUD endpoints.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other
Chat with Dosu Open in Cursor Join Discord Share on X

<!-- gh-comment-id:4231342835 --> @dosubot[bot] commented on GitHub (Apr 12, 2026): <!-- Answer --> Confirmed — this is a valid missing role check. The `registerSSOProvider` endpoint only verifies organization membership without checking the member's role [[1]](https://github.com/better-auth/better-auth/blob/ed2c18df3150ab3a024f7d14a5b5fa58719332f2/packages/sso/src/routes/sso.ts#L631-L650), while the other three provider endpoints (`getSSOProvider`, `updateSSOProvider`, `deleteSSOProvider`) all go through `checkProviderAccess` → `isOrgAdmin`, which restricts access to `["owner", "admin"]` roles [[2]](https://github.com/better-auth/better-auth/blob/ed2c18df3150ab3a024f7d14a5b5fa58719332f2/packages/sso/src/routes/providers.ts#L240-L279). The fix should add an `isOrgAdmin` check [[3]](https://github.com/better-auth/better-auth/blob/ed2c18df3150ab3a024f7d14a5b5fa58719332f2/packages/sso/src/routes/providers.ts#L28-L52) after verifying membership in `registerSSOProvider`, something like: ```ts const member = await ctx.context.adapter.findOne<Member>({ model: "member", where: [ { field: "userId", value: session.user.id }, { field: "organizationId", value: body.organizationId }, ], }); if (!member) { throw new APIError("BAD_REQUEST", { message: "You are not a member of the organization", }); } // Add role check to match other provider endpoints const hasAccess = await isOrgAdmin(ctx, session.user.id, body.organizationId); if (!hasAccess) { throw new APIError("FORBIDDEN", { message: "You must be an organization owner or admin to register SSO providers", }); } ``` This would make `registerSSOProvider` consistent with the other CRUD endpoints. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=brand-link).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-great_response#message_id=cde7d308-831d-4a63-beab-a63b36a5e2b5&feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-irrelevant_answer#message_id=cde7d308-831d-4a63-beab-a63b36a5e2b5&feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-incorrect_sources#message_id=cde7d308-831d-4a63-beab-a63b36a5e2b5&feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-too_verbose#message_id=cde7d308-831d-4a63-beab-a63b36a5e2b5&feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-hallucination#message_id=cde7d308-831d-4a63-beab-a63b36a5e2b5&feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-bug_report#message_id=cde7d308-831d-4a63-beab-a63b36a5e2b5&feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-other#message_id=cde7d308-831d-4a63-beab-a63b36a5e2b5&feedback_type=other)</sup> [![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=chat-badge)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=Confirmed%20%E2%80%94%20this%20is%20a%20valid%20missing%20role%20check.%20The%20%60registerSSOProvider%60%20endpoint%20only%20verifies%20organization%20membership%20without%20checking%20the%20member%27s%20role%20%5B%5B1%5D%5D%28https%3A//github.com/better-auth/better-auth/blob/ed2c18df3150ab3a024f7d14a5b5fa58719332f2/packages/sso/src/routes/sso.ts%23L631-L650%29%2C%20while%20the%20other%20three%20provider%20endpoints%20%28%60getSSOProvider%60%2C%20%60updateSSOProvider%60%2C%20%60deleteSSOProvider%60%29%20all%20go%20through%20%60checkProviderAccess%60%20%E2%86%92%20%60isOrgAdmin%60%2C%20which%20restricts%20access%20to%20%60%5B%22owner%22%2C%20%22admin%22%5D%60%20roles%20%5B%5B2%5D%5D%28https%3A//github.com/better-auth/better-auth/blob/ed2c18df3150ab3a024f7d14a5b5fa58719332f2/packages/sso/src/routes/providers.ts%23L240-L279%29.%0A%0AThe%20fix%20should%20add%20an%20%60isOrgAdmin%60%20check%20%5B%5B3%5D%5D%28https%3A//github.com/better-auth/better-auth/blob/ed2c18df3150ab3a024f7d14a5b5fa58719332f2/packages/sso/src/routes/providers.ts%23L28-L52%29%20after%20verifying%20membership%20in%20%60registerSSOProvider%60%2C%20something%20like%3A%0A%0A%60%60%60ts%0Aconst%20member%20%3D%20await%20ctx.context.adapter.findOne%3CMember%3E%28%7B%0A%20%20%20%20model%3A%20%22member%22%2C%0A%20%20%20%20where%3A%20%5B%0A%20%20%20%20%20%20%20%20%7B%20field%3A%20%22userId%22%2C%20value%3A%20session.user.id%20%7D%2C%0A%20%20%20%20%20%20%20%20%7B%20field%3A%20%22organizationId%22%2C%20value%3A%20body.organizationId%20%7D%2C%0A%20%20%20%20%5D%2C%0A%7D%29%3B%0Aif%20%28%21member%29%20%7B%0A%20%20%20%20throw%20new%20APIError%28%22BAD_REQUEST%22%2C%20%7B%0A%20%20%20%20%20%20%20%20message%3A%20%22You%20are%20not%20a%20member%20of%20the%20organization%22%2C%0A%20%20%20%20%7D%29%3B%0A%7D%0A//%20Add%20role%20check%20to%20match%20other%20provider%20endpoints%0Aconst%20hasAccess%20%3D%20await%20isOrgAdmin%28ctx%2C%20session.user.id%2C%20body.organizationId%29%3B%0Aif%20%28%21hasAccess%29%20%7B%0A%20%20%20%20throw%20new%20APIError%28%22FORBIDDEN%22%2C%20%7B%0A%20%20%20%20%20%20%20%20message%3A%20%22You%20must%20be%20an%20organization%20owner%20or%20admin%20to%20register%20SSO%20providers%22%2C%0A%20%20%20%20%7D%29%3B%0A%7D%0A%60%60%60%0A%0AThis%20would%20make%20%60registerSSOProvider%60%20consistent%20with%20the%20other%20CRUD%20endpoints.)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=join-discord)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/9133)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#19914