Skip to content

feat: oidc domain registration and verification #7745

Open
n1ru4l wants to merge 32 commits intomainfrom
feat-oidc-registered-domains
Open

feat: oidc domain registration and verification #7745
n1ru4l wants to merge 32 commits intomainfrom
feat-oidc-registered-domains

Conversation

@n1ru4l
Copy link
Contributor

@n1ru4l n1ru4l commented Feb 26, 2026

Background

Closes #7735

Description

This PR addresses the following things:

  • Make the OIDC integration management section look better than before
  • Allow admins to register and verify domains associated with their organization via a DNS challenge (by querying google and cloudflare for a TXT record)
  • If the admins verified a domain, the users attempting to log in via OIDC do not need to verify their email
  • Alter the end2end tests to be more like cloud Hive Console by requiring email verification
  • Add end2end test to verify the register domain and user flow by using a hard-coded DNS record on buzzcheck.dev

A full run through of the PR can be found on on this internal Slack video: https://guild-oss.slack.com/archives/CAY2119MX/p1772221874651489?thread_ts=1772218438.245559&cid=CAY2119MX

Originally I wanted to have #7763 as part of the scope of this PR. But it got a bit bigger, so I will do that in a follow up PR.

Here are some screenshots of the functionality, but I recommend to watch the video instead.

image image image

Rollout Strategy

This change is fully backwards-compatible. I will probably manually create records for our known and trusted enterprise users to spare them some time running through the DNS challenge. In case we would need to rollback the pre-existing functionality continues to work. The only regression would be that some people need to verify their email again.

Checklist

  • Input validation
  • Output encoding
  • Authentication management
  • Session management
  • Access control
  • Error handling and logging
  • Testing

@github-actions
Copy link
Contributor

github-actions bot commented Feb 26, 2026

🚀 Snapshot Release (alpha)

The latest changes of this PR are available as alpha on npm (based on the declared changesets):

Package Version Info
hive 9.5.0-alpha-20260303140958-91ca07dc2aa085132ddc66eb07d7f8380183ba9a npm ↗︎ unpkg ↗︎
@hive/pubsub 0.0.1 npm ↗︎ unpkg ↗︎

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @n1ru4l, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a significant feature for OIDC integrations, enabling organizations to register and verify domains. This enhancement aims to improve the user experience by allowing automatic user provisioning and bypassing email verification for users from verified domains. It involves comprehensive changes across the backend, including database schema updates, new API endpoints, and a dedicated data store, as well as a complete overhaul of the frontend SSO settings interface to expose these new capabilities.

Highlights

  • OIDC Domain Management: Introduced the ability to register, delete, and verify domains for OIDC integrations, allowing for streamlined user onboarding by skipping email confirmation for verified domains.
  • New Database Schema: Added a new database table oidc_integration_domains to store registered domain information, including verification status and timestamps.
  • GraphQL API Extensions: Extended the GraphQL API with new mutations (registerOIDCDomain, deleteOIDCDomain, verifyOIDCDomainChallenge) and a registeredDomains field on the OIDCIntegration type to support domain management.
  • Dedicated OIDC Integration Store: Implemented a new OIDCIntegrationStore service to encapsulate all data access logic for OIDC domains and manage domain verification challenges using Redis.
  • Enhanced UI for SSO Settings: Revamped the Single Sign-On settings page in the web application to include a dedicated section for managing registered domains, along with improved OIDC provider configuration and access settings.
Changelog
  • packages/migrations/src/actions/2026.02.25T00-00-00.oidc-integration-domains.ts
    • Added a new migration to create the oidc_integration_domains table for storing OIDC domain registration and verification data.
  • packages/migrations/src/run-pg-migrations.ts
    • Updated the migration runner to include the new OIDC domain integration migration.
  • packages/services/api/src/modules/oidc-integrations/index.ts
    • Imported and provided the new OIDCIntegrationStore to the OIDC integrations module.
  • packages/services/api/src/modules/oidc-integrations/module.graphql.ts
    • Added registeredDomains field to the OIDCIntegration type.
    • Defined new GraphQL mutations for registerOIDCDomain, deleteOIDCDomain, and verifyOIDCDomainChallenge.
    • Introduced new input and result types for OIDC domain mutations, along with the OIDCIntergrationDomain type.
  • packages/services/api/src/modules/oidc-integrations/providers/oidc-integration.store.ts
    • Added a new OIDCIntegrationStore provider to manage OIDC domain data in the database and handle domain verification challenges using Redis.
  • packages/services/api/src/modules/oidc-integrations/providers/oidc-integrations.provider.ts
    • Imported dns for domain resolution and OIDCIntegrationStore.
    • Injected OIDCIntegrationStore into OIDCIntegrationsProvider.
    • Implemented registerDomain, verifyChallenge, deleteDomain, and getRegisteredDomainsForOIDCIntegration methods to handle OIDC domain logic.
    • Added FQDNModel for validating fully qualified domain names.
  • packages/services/api/src/modules/oidc-integrations/resolvers/Mutation/deleteOIDCDomain.ts
    • Added a new GraphQL resolver for the deleteOIDCDomain mutation.
  • packages/services/api/src/modules/oidc-integrations/resolvers/Mutation/registerOIDCDomain.ts
    • Added a new GraphQL resolver for the registerOIDCDomain mutation.
  • packages/services/api/src/modules/oidc-integrations/resolvers/Mutation/verifyOIDCDomainChallenge.ts
    • Added a new GraphQL resolver for the verifyOIDCDomainChallenge mutation.
  • packages/services/api/src/modules/oidc-integrations/resolvers/OIDCIntegration.ts
    • Added a resolver for the registeredDomains field on the OIDCIntegration type.
  • packages/web/app/src/components/organization/settings/single-sign-on/connect-single-sign-on-provider-sheet.tsx
    • Added a new React component for connecting OIDC providers, supporting both discovery document fetching and manual endpoint configuration.
  • packages/web/app/src/components/organization/settings/single-sign-on/oidc-default-resource-selector.tsx
    • Added a new React component for selecting and updating default resource assignments for OIDC integrations.
  • packages/web/app/src/components/organization/settings/single-sign-on/oidc-default-role-selector.tsx
    • Added a new React component for selecting and updating the default member role for OIDC integrations.
  • packages/web/app/src/components/organization/settings/single-sign-on/oidc-integration-configuration.tsx
    • Added a new React component to display and manage OIDC integration settings, including overview, configuration, access settings, and the new domain configuration.
  • packages/web/app/src/components/organization/settings/single-sign-on/single-sign-on-subpage.tsx
    • Added a new subpage component for Single Sign-On settings, integrating the OIDC configuration and provider connection sheets.
  • packages/web/app/src/components/ui/copy-icon-button.tsx
    • Added a new reusable CopyIconButton component for copying text to the clipboard.
  • packages/web/app/src/pages/organization-settings.tsx
    • Updated the organization settings page to include the new 'sso' subpage in the navigation.
    • Removed the old OIDCIntegrationSection and integrated the new SingleSignOnSubpage.
  • packages/web/app/src/pages/target-trace.tsx
    • Removed the local CopyIcon import.
    • Updated the CopyIconButton import to use the new shared UI component.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 26, 2026

💻 Website Preview

The latest changes are available as preview in: https://pr-7745.hive-landing-page.pages.dev

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces OIDC domain registration and verification, a significant new feature. However, critical flaws exist in the authorization logic, where OIDC integration IDs are incorrectly used instead of organization IDs for permission checks, potentially breaking the feature for legitimate users. Additionally, a database integrity issue was identified in the domain creation logic due to a missing required column in an INSERT statement. Other issues include a broken GraphQL query, typos, and a faulty regex for domain validation. Please review the detailed comments for suggestions on how to fix these issues.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 27, 2026

🐋 This PR was built and pushed to the following Docker images:

Targets: build

Platforms: linux/amd64

Image Tag: e91943c12d09e1f214bddcc57d382c4b080c8998

@n1ru4l n1ru4l force-pushed the feat-oidc-registered-domains branch from aa30811 to b8eda49 Compare February 27, 2026 10:26
@n1ru4l n1ru4l force-pushed the feat-oidc-registered-domains branch from c2835ae to 65286f5 Compare February 27, 2026 10:33
@n1ru4l n1ru4l force-pushed the feat-oidc-registered-domains branch 2 times, most recently from 310c1c9 to b36594d Compare February 27, 2026 15:39
@n1ru4l n1ru4l force-pushed the feat-oidc-registered-domains branch from fc23191 to 427e602 Compare March 2, 2026 12:36
Comment on lines -41 to -47
- name: disable rate limiting
uses: mikefarah/yq@4839dbbf80445070a31c7a9c1055da527db2d5ee # v4.44.6
with:
cmd:
yq -i '.services.server.environment.SUPERTOKENS_RATE_LIMIT = "0"'
docker/docker-compose.community.yml

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This value now provided via docker/docker-compose.end2end.yml


cy.get('div[role="dialog"]').find('button[type="submit"]').last().click();
Cypress.Commands.add('createOIDCIntegration', () => {
const isLocal = Cypress.env('RUN_AGAINST_LOCAL_SERVICES') == '1';
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This now works correctly on both CI and local

Comment on lines -99 to +120
cy.contains('Create Organization');
cy.contains('Verify your email address');

const email = user.email;
return cy.task('getEmailConfirmationLink', email).then((url: string) => {
cy.visit(url);
cy.contains('Success!');
cy.get('[data-button-verify-email-continue]').click();
cy.contains('Create Organization');
});
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The end2end tests now include email verification (when needed).

Comment on lines +35 to +51
},
{
"SubjectId": "3",
"Username": "test-user-3",
"Password": "password",
"Claims": [
{
"Type": "name",
"Value": "Hive Bro",
"ValueType": "string"
},
{
"Type": "email",
"Value": "hive.bro@buzzcheck.dev",
"ValueType": "string"
}
]
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added a user with the @buzzcheck.dev email, so we can test the full flow including DNS querying from our dev environment in a e2e test

Comment on lines +44 to +57
workflows:
ports:
- '3014:3014'
environment:
EMAIL_PROVIDER: mock

redis:
ports:
- '6379:6379'

server:
environment:
SUPERTOKENS_RATE_LIMIT: '0'
AUTH_REQUIRE_EMAIL_VERIFICATION: '1'
Copy link
Contributor Author

Choose a reason for hiding this comment

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

these are needed for e2e tests and fixtures/tasks to work.

}

return {
async purgeOIDCDomains() {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

simple cleanup helper to run before specific test cases

TRUNCATE "oidc_integration_domains"
`);
},
async forgeOIDCDNSChallenge(orgSlug: string) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This one overwrites a DNS challenge record to a fixed value, so we can test the verification flow.

"""
List of domains registered with this OIDC integration.
"""
registeredDomains: [OIDCIntegrationDomain!]!
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Did not make this a Connection as the list will be 1 domain per org in 99.999999% of cases.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Makes sense.

Should we make the OIDCIntegrationDomain nullable to support partial query resolution in case of error?

});

if (oidcConfig?.requireInvitation) {
if (oidcConfig) {
Copy link
Contributor Author

@n1ru4l n1ru4l Mar 3, 2026

Choose a reason for hiding this comment

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

This previously was ONLY assigning the invitation role, if the requireInvitation flag was set. I think that is a bit confusing, so I changed it to always assign the invitation role if it exists.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added a way to filter the emails by date, so it is easier to only get the emails of the current test run (e2e)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Here I exposed the seed functions to the cypress test as tasks. The API is cumbersome, but it works...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

A lot of noise in these files...

Most important things:

  • We use the seed to speed up tests a bit and not do a type/button click sign up every time
  • Added a new test for verifying the domain registration flow.

@n1ru4l n1ru4l assigned dotansimha and unassigned dotansimha Mar 3, 2026
@n1ru4l n1ru4l requested review from dotansimha and jdolle March 3, 2026 15:15
@n1ru4l n1ru4l marked this pull request as ready for review March 3, 2026 15:15
@n1ru4l n1ru4l changed the title feat: oidc domain registration/verification feat: oidc domain registration and verification Mar 3, 2026
Copy link
Collaborator

@jdolle jdolle left a comment

Choose a reason for hiding this comment

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

Just minor comments on this functionality.

We discussed possible future improvements already via slack so I've not included those comments again here.

if (rawChallenge === null) {
return null;
}
return ChallengePayloadModel.parse(JSON.parse(rawChallenge));
Copy link
Collaborator

Choose a reason for hiding this comment

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

Minor: Consider handling the unlikely case of if the rawChallenge is corrupt and isnt valid JSON


const key = `hive:oidcDomainChallenge:${domainId}`;
await this.redis.set(key, JSON.stringify(challenge));
await this.redis.expire(key, 60 * 60 * 24);
Copy link
Collaborator

Choose a reason for hiding this comment

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

You can use the ex option on set to combine these two commands.

await redis.set(key, JSON.stringify(challenge), 'EX', 60 * 60 * 24);

"""
List of domains registered with this OIDC integration.
"""
registeredDomains: [OIDCIntegrationDomain!]!
Copy link
Collaborator

Choose a reason for hiding this comment

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

Makes sense.

Should we make the OIDCIntegrationDomain nullable to support partial query resolution in case of error?

)}
{stepper.switch({
'step-1-general': () => (
<Form {...form}>
Copy link
Collaborator

Choose a reason for hiding this comment

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

minor -- to help keep the component logic easy to follow, it'd be helpful to move each step into its own component.

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

Labels

None yet

Development

Successfully merging this pull request may close these issues.

allow proofing OIDC domain ownership

3 participants