Skip to content

feat: Add OIDC SSO#84

Open
serendipty01 wants to merge 8 commits intoroostorg:mainfrom
serendipty01:add-oidc-sso
Open

feat: Add OIDC SSO#84
serendipty01 wants to merge 8 commits intoroostorg:mainfrom
serendipty01:add-oidc-sso

Conversation

@serendipty01
Copy link
Copy Markdown
Contributor

@serendipty01 serendipty01 commented Feb 27, 2026

Context & Requests for Reviewers

fixes #335

Currently, Coop only supports SAML for Single Sign-On (SSO). This PR adds OpenID Connect (OIDC) support to enable modern authentication flows and provide better compatibility with identity providers that support OIDC.

This PR is using openid-client library

TODO

  • Lint
  • Test with Okta
  • Test more scenarios
  • Clean up logs and comments
  • Add env variables to .env.example
  • Check whether to use client_secret_basic or client_secret_post
  • Update docs

Tests

Tested locally using Authelia
will add screenshots/video later

(Optional) Rollout Plan

@serendipty01 serendipty01 force-pushed the add-oidc-sso branch 7 times, most recently from 97bba63 to 515762c Compare March 21, 2026 11:10
@serendipty01 serendipty01 force-pushed the add-oidc-sso branch 2 times, most recently from 298aa43 to 3cd5807 Compare March 26, 2026 11:29
@serendipty01 serendipty01 force-pushed the add-oidc-sso branch 2 times, most recently from 6bcf124 to 87e92bd Compare April 2, 2026 13:55
@serendipty01 serendipty01 marked this pull request as ready for review April 2, 2026 13:56
Comment thread server/services/orgSettingsService/orgSettingsService.ts
Comment thread .devops/migrator/src/scripts/api-server-pg/2026.03.02T00.00.00.add-oidc-sso.sql Outdated
Comment thread server/api.ts
Comment thread server/services/orgSettingsService/orgSettingsService.ts
Comment thread server/graphql/modules/org.ts Outdated
Comment thread client/src/webpages/settings/SSOSettings.tsx
Comment thread server/utils/crypto.ts Outdated
Comment thread .devops/migrator/src/scripts/api-server-pg/2026.03.02T00.00.00.add-oidc-sso.sql Outdated
@serendipty01 serendipty01 force-pushed the add-oidc-sso branch 4 times, most recently from 4da4829 to daacab4 Compare April 7, 2026 17:25
Copy link
Copy Markdown
Member

@juanmrad juanmrad left a comment

Choose a reason for hiding this comment

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

a couple more comments. 😅 Thanks for all the hard work.

Comment thread server/api.ts Outdated
Comment thread server/api.ts
Comment thread server/graphql/modules/org.ts Outdated
Comment thread .devops/migrator/src/scripts/api-server-pg/2026.03.02T00.00.00.add-oidc-sso.sql Outdated
Comment thread server/api.ts Outdated
Comment thread server/graphql/modules/org.ts Outdated
Comment thread server/graphql/modules/org.ts Outdated
@juanmrad
Copy link
Copy Markdown
Member

@serendipty01 left you a couple more comments. let me know if you need help, and again thanks for contributing.

@serendipty01 serendipty01 force-pushed the add-oidc-sso branch 5 times, most recently from 658ed70 to a0d56a8 Compare April 25, 2026 10:45

permissions:
pull-requests: write
issues: write
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this is for resolving the failing checklist job.
Should i remove this or raise a separate PR for this ?


export const DateStringArbitrary = fc
.date()
.date({ noInvalidDate: true })
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this change is for fixing a test failure

@serendipty01 serendipty01 force-pushed the add-oidc-sso branch 2 times, most recently from f224b1a to c65e0b8 Compare April 25, 2026 11:23
Copy link
Copy Markdown
Member

@juanmrad juanmrad left a comment

Choose a reason for hiding this comment

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

@serendipty01 Just to doubly confirm you manually tested this change correct?

Copy link
Copy Markdown

@ThisIsMissEm ThisIsMissEm left a comment

Choose a reason for hiding this comment

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

Left some comments that may be worth addressing before merge.

Comment thread server/utils/crypto.ts
Comment thread server/.env.example
Comment thread server/api.ts
Comment thread server/api.ts

// Reconstruct callback URL with code/state from IdP redirect.
// authorizationCodeGrant needs: base = registered redirect_uri, query = code+state from IdP.
const currentUrl = new URL(`${deps.ConfigService.apiUrl}/api/v1/oidc/login/callback`);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

wouldn't new URL(req.url, deps.ConfigService.apiUrl) work here instead of manually copying the search parameters? Or, perhaps better: req.originalUrl

Comment thread server/api.ts
if (!user || user.orgId !== orgId) {
// eslint-disable-next-line no-console
console.error('[OIDC] User not found or org mismatch', { email, orgId, userOrgId: user?.orgId });
return res.redirect(`${deps.ConfigService.uiUrl}/login/sso?error=user_not_found`);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

no email and user_not_found should probably both be the same error as to try to stop credential guessing through oauth

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Use sso_login_failed for both of these.

Comment thread server/api.ts
Comment on lines +356 to +359
if (!user.loginMethods.includes('oidc')) {
user.loginMethods = [...user.loginMethods, 'oidc'];
await user.save();
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This is probably an unsafe upgrade to OIDC for authentication, and should only be done if the user is completing a flow for connecting their local account with an OIDC account; otherwise, it should not be done, as this would allow an attacker gaining control over the OIDC server to then access another internal tool as long as they know someone's email that likely has an account on said tool.

Comment thread server/api.ts
}

const callbackUrl = deps.SSOService.getSSOOidcCallbackUrl();
const issuerUrl = normalizeIssuerUrl(oidcSettings.issuer_url);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

normalize on storage, not on read

Comment thread server/api.ts
Comment thread server/api.ts
Comment on lines +401 to +402
const state = oidcClient.randomState();
redirectTo.searchParams.set('state', state);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

you can move the state generation up to along with codeVerifier and challenge, and then just pass in params as a URLSearchParams({ ... }) with a state key/property. buildAuthorizationUrl should automatically include the state then in the authorization redirect URL

Comment thread server/api.ts
redirectTo.searchParams.set('state', state);

// Store PKCE data + orgId in session for callback
req.session.oidc = { code_verifier: codeVerifier, state, org_id: orgId };
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

double check that this is stored securely, and not in a plaintext cookie or something.

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.

[SSO] Add OIDC SSO authentication

3 participants