Skip to content

feat: add oidc provider using doorkeeper gem#7668

Open
tachyons wants to merge 5 commits into
masterfrom
feat_circuitverse_oidc_provider
Open

feat: add oidc provider using doorkeeper gem#7668
tachyons wants to merge 5 commits into
masterfrom
feat_circuitverse_oidc_provider

Conversation

@tachyons

@tachyons tachyons commented Jul 19, 2026

Copy link
Copy Markdown
Member

Fixes #

Describe the changes you have made in this PR -

Screenshots of the UI changes (If any) -


Code Understanding and AI Usage

Did you use AI assistance (ChatGPT, Claude, Copilot, etc.) to write any part of this code?

  • No, I wrote all the code myself
  • Yes, I used AI assistance (continue below)

If you used AI assistance:

  • I have reviewed every single line of the AI-generated code
  • I can explain the purpose and logic of each function/component I added
  • I have tested edge cases and understand how the code handles them
  • I have modified the AI output to follow this project's coding standards and conventions

Explain your implementation approach:


Checklist before requesting a review

  • I have added proper PR title and linked to the issue
  • I have performed a self-review of my code
  • I can explain the purpose of every function, class, and logic block I added
  • I understand why my changes work and have tested them thoroughly
  • I have considered potential edge cases and how my code handles them
  • If it is a core feature, I have added thorough tests
  • My code follows the project's style guidelines and conventions

Note: Please check Allow edits from maintainers if you would like us to assist in the PR.

Summary by CodeRabbit

  • New Features
    • Added an OAuth 2.0 / OpenID Connect provider with app management, authorization/consent screens, and PKCE-based authorization-code token exchange.
    • Enabled API V1 access using Bearer tokens, with support for token scopes (public plus optional profile/email) and basic OIDC claim mappings.
    • Added admin-only UI translations for the OAuth/Doorkeeper flows.
  • Security
    • Restricted OAuth application management to admin access.
    • API V1 now enforces authentication and scope checks, returning a 401 with “authentication required” when missing.

@tachyons
tachyons requested a review from ShinichiShi July 19, 2026 14:02

@accesslint accesslint Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Found 1 issue across 1 rule.

<p>This application will be able to:</p>
<ul>
<% @pre_auth.scopes.each do |scope| %>
<li><%= scope %></li>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

WCAG 1.3.1: <li> is not contained in a <ul>, <ol>, or <menu>.

<li> elements must be contained in a <ul>, <ol>, or <menu>.

Details

List items (<li>) only have semantic meaning inside a list container (<ul>, <ol>, or <menu>). Outside of these containers, assistive technologies cannot convey the list relationship. Wrap <li> elements in the appropriate list container.

@github-actions

Copy link
Copy Markdown
Contributor

vet Summary Report

This report is generated by vet

Policy Checks

  • ✅ Vulnerability
  • ✅ Malware
  • ✅ License
  • ✅ Popularity
  • ✅ Maintenance
  • ✅ Security Posture
  • ✅ Threats

Malicious Package Analysis

Active malicious package analysis was disabled. Learn more about enabling active package analysis

Malicious Package Analysis Report
Ecosystem Package Version Status Report
  • ℹ️ 0 packages have been actively analyzed for malicious behaviour.
  • ✅ No malicious packages found.

Note: Only known malicious packages were reported. Consider enabling active package analysis to get more accurate results.

Changed Packages

Changed Packages

  • ✅ [RubyGems] doorkeeper-openid_connect@1.10.5
  • ✅ [RubyGems] doorkeeper@5.9.3

@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (1)
  • Gemfile.lock is excluded by !**/*.lock

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7f539f29-ff23-4c55-990f-1db28a5805b3

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

Adds Doorkeeper OAuth 2 and OpenID Connect support, including dependencies, database tables, foreign keys, provider configuration, routes, locales, administration resources, authorization views, and hybrid API authentication. Adds JWT and OAuth request handling, OAuth application policies, and request specs covering bearer-token API access and PKCE authorization-code exchange.

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding an OpenID Connect provider with Doorkeeper.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat_circuitverse_oidc_provider

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@accesslint accesslint Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Found 1 issue across 1 rule.

<div class="col-md-8">
<div class="card">
<div class="card-header">
<h3 class="mb-0"><%= t("doorkeeper.authorizations.title") %></h3>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Best Practice: Heading is empty. Add text content or remove the heading element.

Headings must have discernible text.

Details

Screen reader users navigate pages by headings, so empty headings create confusing navigation points. Ensure all headings contain visible text or accessible names. If a heading is used purely for visual styling, use CSS instead of heading elements.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
config/initializers/doorkeeper.rb (1)

173-176: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Consider enabling force_pkce for non-confidential clients.

oauth_applications.confidential supports public clients, but force_pkce is left disabled (the generator default). Without it, non-confidential clients aren't required to use PKCE on the authorization_code flow, leaving them exposed to authorization-code interception.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a350fc8a-c621-482b-a376-6ef7c459e94b

📥 Commits

Reviewing files that changed from the base of the PR and between c9d810a and 0310bd9.

⛔ Files ignored due to path filters (1)
  • Gemfile.lock is excluded by !**/*.lock
📒 Files selected for processing (19)
  • .gitignore
  • Gemfile
  • app/avo/resources/doorkeeper_application.rb
  • app/controllers/api/v1/base_controller.rb
  • app/policies/doorkeeper/application_policy.rb
  • app/views/doorkeeper/authorizations/new.html.erb
  • app/views/layouts/doorkeeper.html.erb
  • config/initializers/doorkeeper.rb
  • config/initializers/doorkeeper_openid_connect.rb
  • config/locales/doorkeeper.en.yml
  • config/locales/doorkeeper_openid_connect.en.yml
  • config/routes.rb
  • db/migrate/20260719133550_create_doorkeeper_tables.rb
  • db/migrate/20260719140000_validate_doorkeeper_foreign_keys.rb
  • db/migrate/20260719140001_create_doorkeeper_openid_connect_tables.rb
  • db/migrate/20260719140002_validate_doorkeeper_openid_connect_foreign_keys.rb
  • db/schema.rb
  • spec/requests/api/v1/oauth_access_spec.rb
  • spec/requests/oauth_pkce_flow_spec.rb

Comment on lines +122 to +126
def user_from_doorkeeper
return nil unless defined?(Doorkeeper) && doorkeeper_token&.resource_owner_id

User.find_by(id: doorkeeper_token.resource_owner_id)
end

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Confirm that `doorkeeper_token` does not check for expiration or revocation on its own.

# Inspect Doorkeeper's valid_doorkeeper_token? and doorkeeper_token methods.
# We skip cloning since Doorkeeper source is not in the repo, but we can check if there are any usages locally.
rg "doorkeeper_token" --type rb

Repository: CircuitVerse/CircuitVerse

Length of output: 190


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the relevant controller and inspect the surrounding methods.
wc -l app/controllers/api/v1/base_controller.rb
sed -n '1,220p' app/controllers/api/v1/base_controller.rb

# Search for Doorkeeper-related authentication paths in the repo.
rg -n "doorkeeper_token|doorkeeper_authorize!|authenticate_user!|acceptable\?|accessible\?" app config lib spec test

Repository: CircuitVerse/CircuitVerse

Length of output: 7612


🌐 Web query:

Doorkeeper doorkeeper_token acceptable? accessible? revoked expired helper semantics

💡 Result:

In the Doorkeeper gem, several key methods and helpers manage the lifecycle and validation of access tokens [1][2]. doorkeeper_token This helper method retrieves the current access token from the request [3][1]. It performs authentication by attempting to extract and locate the token based on the methods configured in Doorkeeper (e.g., Authorization header, request parameters) [1][4][2]. accessible? This method indicates whether an access token is valid for use [5]. It returns true if and only if the token is both not expired and not revoked [5]. Internally, it is defined as!expired? &&!revoked? [5]. acceptable?(scopes) This method determines if the token is valid for specific requested scopes [6]. It returns true only if the token is accessible? (not expired, not revoked) AND includes the required scopes [7][6]. Revoked and Expired Doorkeeper tracks revocation via a revoked_at timestamp column [8]. A token is considered revoked if revoked_at is present [5][8]. Expiration is determined by comparing the current time against the token's expiration time [8]. Note that expired tokens may still technically exist in the database unless cleaned up, and there have been discussions in the community regarding the specific behavior of revocation endpoints when applied to tokens that are already expired [9]. Summary of Semantics Helper: doorkeeper_token extracts the token [1]. Validation: accessible? checks basic validity (active/non-expired) [5]. Authorization: acceptable?(scopes) checks both validity and authorization scope compliance [6][1].

Citations:


Reject revoked/expired Doorkeeper tokens in user_from_doorkeeper. doorkeeper_token can still be present after expiry or revocation; gate this path with doorkeeper_token&.accessible? before mapping it to a user so authenticate_user! doesn’t accept invalid OAuth tokens.

Comment thread config/initializers/doorkeeper.rb
Comment thread spec/requests/api/v1/oauth_access_spec.rb

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6fb80fa3-c2b7-44de-8e29-26100ba070bc

📥 Commits

Reviewing files that changed from the base of the PR and between 0310bd9 and fb4c82e.

📒 Files selected for processing (3)
  • app/controllers/api/v1/base_controller.rb
  • app/views/doorkeeper/authorizations/new.html.erb
  • config/locales/en.yml
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/views/doorkeeper/authorizations/new.html.erb
  • app/controllers/api/v1/base_controller.rb

Comment thread config/locales/en.yml
Comment on lines +70 to 74
project:
show_more: "Show More"
options:
users: "Users"
projects: "Projects"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Fix the duplicate key and preserve the search-bar translation contract.

options is declared twice under components.search_bar, and project is empty, so the intended nesting was not applied. This can make locale loading fail or overwrite the earlier mapping. Also, app/components/search/search_bar_component.rb:52-76 still reads components.search_bar.options.users/projects; either preserve that path by removing the duplicate block, or update the consumer together with a correctly nested project.options structure.

🧰 Tools
🪛 YAMLlint (1.37.1)

[error] 72-72: duplication of key "options" in mapping

(key-duplicates)

Source: Linters/SAST tools

@ShinichiShi

Copy link
Copy Markdown
Contributor

checking

@accesslint accesslint Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Found 2 issues across 2 rules (1 WCAG, 1 Best Practice).

<div class="col-md-8">
<div class="card">
<div class="card-header">
<h3 class="mb-0"><%= t("doorkeeper.authorizations.title") %></h3>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Best Practice: Heading is empty. Add text content or remove the heading element.

Headings must have discernible text.

Details

Screen reader users navigate pages by headings, so empty headings create confusing navigation points. Ensure all headings contain visible text or accessible names. If a heading is used purely for visual styling, use CSS instead of heading elements.

<p><%= t("doorkeeper.authorizations.scope_intro") %></p>
<ul>
<% @pre_auth.scopes.each do |scope| %>
<li><%= scope %></li>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

WCAG 1.3.1: <li> is not contained in a <ul>, <ol>, or <menu>.

<li> elements must be contained in a <ul>, <ol>, or <menu>.

Details

List items (<li>) only have semantic meaning inside a list container (<ul>, <ol>, or <menu>). Outside of these containers, assistive technologies cannot convey the list relationship. Wrap <li> elements in the appropriate list container.

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
config/initializers/doorkeeper.rb (1)

239-243: ⚠️ Potential issue | 🟠 Major

Register the required openid scope.

OIDC authorization requests using scope=openid will be rejected because only public, profile, and email are configured. Add :openid to the configured scopes, typically as an optional scope.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ee941781-6b23-40a7-bb51-ed033275f98d

📥 Commits

Reviewing files that changed from the base of the PR and between fb4c82e and 06ee6fb.

📒 Files selected for processing (5)
  • app/views/doorkeeper/authorizations/new.html.erb
  • config/initializers/doorkeeper.rb
  • config/locales/en.yml
  • spec/requests/api/v1/oauth_access_spec.rb
  • spec/requests/oauth_pkce_flow_spec.rb
🚧 Files skipped from review as they are similar to previous changes (3)
  • spec/requests/api/v1/oauth_access_spec.rb
  • config/locales/en.yml
  • spec/requests/oauth_pkce_flow_spec.rb

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.

2 participants