Skip to content

[OPIK-6666] [BE] feat: MCP OAuth 2.1 foundations (PR 1/7) — schema, config, domain, persistence, service#6979

Merged
LifeXplorer merged 39 commits into
mainfrom
avinahradau/OPIK-6666-oauth-as-chunked-1
Jun 9, 2026
Merged

[OPIK-6666] [BE] feat: MCP OAuth 2.1 foundations (PR 1/7) — schema, config, domain, persistence, service#6979
LifeXplorer merged 39 commits into
mainfrom
avinahradau/OPIK-6666-oauth-as-chunked-1

Conversation

@LifeXplorer

@LifeXplorer LifeXplorer commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Details

PR 1 of 7 splitting #6900 (MCP OAuth 2.1 Authorization Server) into smaller, shippable chunks per review feedback — working inner-foundations outward, schema first, deployment last. #6900 stays open as the full reference.

This PR is the shared inner foundation every OAuth resource depends on: schema + config + domain types + persistence + token-lifecycle service. It has no REST surface, no guicey bundle, no OpikApplication wiring — nothing is exposed and there is zero behavior change. The feature is gated by MCP_OAUTH_ENABLED (defaults false) and stays inert until the full chain lands.

What's in the box

Layer Files
Schema Liquibase migration 000080mcp_oauth_clients / _codes / _tokens on db-app-state (no seeded clients; all MCP hosts register via DCR in a later chunk)
Config McpOAuthConfig (env-gated by MCP_OAUTH_ENABLED) + config.yml mcpOAuth: block + OpikConfiguration.mcpOAuth field
Constants / models OAuthConstants, McpOAuthClient, McpOAuthCode, McpOAuthToken, McpOAuthTokens (token utils), ValidatedToken, CreateOAuthCodeCommand, TokenResponse, ClientRegistrationRequest
Persistence JDBI DAOs McpOAuthClientDAO / McpOAuthCodeDAO / McpOAuthTokenDAO — atomic mark-used, refresh rotation, revoke-family
Strategy + service OAuthClientStrategy seam + DbOAuthClientStrategy + OAuthClientService; McpOAuthService — code exchange (PKCE/S256 verify), refresh rotation w/ reuse detection + grace, revoke, validate

Deliberately deferred

  • MutableJobConfiguration switch on OpikConfiguration and the config.yml jobs.mcpOAuthScrubCron entry → move with the scrub job (PR 6), which is the only thing that needs them.
  • All REST resources, the McpOAuthBundle kill-switch, AuthFilter/AuthService integration, tests, and helm/nginx deployment → later chunks.

The split (7 PRs)

  1. Foundationsthis PR
  2. Metadata endpoint (/.well-known/oauth-authorization-server) + guicey bundle wiring
  3. Authorize + consent (/oauth/authorize) + AuthService workspace resolution
  4. Token + revoke (/oauth/token, /oauth/revoke)
  5. Dynamic client registration (/oauth/register, RFC 7591)
  6. Data-API bearer integration (AuthFilter) + validator (/opik/auth-oauth) + scrub job + E2E
  7. Deployment (helm/nginx)

Change checklist

  • User facing
  • Documentation update

Neither applies: this chunk has no REST surface and zero behavior change — nothing is user-facing and there is nothing to document until the endpoints land in later PRs.

Testing

  • mvn -o compile — green. Only out-of-set dependencies (IdGenerator, SetFlatArgumentFactory) already exist on main.
  • No tests in this chunk by design — OAuth test suites land with their resources in PRs 3–6.

Issues

Documentation

N/A for this chunk — no user-facing surface. End-user OAuth setup/usage docs land with the deployment PR (7/7).

🤖 Generated with Claude Code

…main, persistence, service

PR 1 of 7 splitting #6900 (MCP OAuth 2.1 Authorization Server) into
shippable chunks per review. This is the shared inner foundation that
every OAuth resource depends on — no REST surface, no app wiring, no
behavior change.

Includes:
- Liquibase migration 000080: mcp_oauth_clients/_codes/_tokens on db-app-state
- McpOAuthConfig (env-gated by MCP_OAUTH_ENABLED) + config.yml mcpOAuth block
  + OpikConfiguration.mcpOAuth field
- Domain models + token utils (clients, codes, tokens, ValidatedToken, etc.)
- JDBI DAOs (clients/codes/tokens) with atomic mark-used, rotation, revoke-family
- OAuthClientStrategy seam + DbOAuthClientStrategy + OAuthClientService
- McpOAuthService — code exchange (PKCE), refresh rotation w/ reuse detection,
  revoke, validate

Deferred to PR 6 (scrub job): the MutableJobConfiguration switch and the
config.yml jobs.mcpOAuthScrubCron entry, which only the scrub job needs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@LifeXplorer LifeXplorer requested a review from a team as a code owner June 5, 2026 13:53
@github-actions github-actions Bot added java Pull requests that update Java code Backend labels Jun 5, 2026
@LifeXplorer LifeXplorer requested a review from andrescrz June 5, 2026 15:09

@andrescrz andrescrz left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Many of the comments are mentioned once, but they apply to other parts of the code base.

I still recommend breaking this down into smaller chunks, to progress and deliver faster.

Comment thread apps/opik-backend/src/main/java/com/comet/opik/infrastructure/McpOAuthConfig.java Outdated
LifeXplorer and others added 7 commits June 7, 2026 20:15
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added the tests Including test files, or tests related like configuration. label Jun 7, 2026
…IdGenerator

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

Backend Tests - Integration Group 7

1 487 tests  ±0   1 487 ✅ ±0   6m 24s ⏱️ -18s
   20 suites ±0       0 💤 ±0 
   20 files   ±0       0 ❌ ±0 

Results for commit a7a015e. ± Comparison against base commit e276974.

♻️ This comment has been updated with latest results.

LifeXplorer and others added 5 commits June 8, 2026 09:01
…ct mapper

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…on the record

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…apper

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
LifeXplorer and others added 9 commits June 8, 2026 09:47
…kenResponse

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
code_challenge is NOT NULL in the schema and PKCE is mandatory, so the value
cannot be null here; annotate the params @nonnull instead.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…hing

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…dation

Defaults were duplicated between McpOAuthConfig and config.yml. Remove the
Java-side defaults (durations, rate limit, enabled) so they live only in YAML,
and enforce presence via @NotNull / @min so a missing value fails fast at boot.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@LifeXplorer LifeXplorer requested a review from andrescrz June 8, 2026 09:38

@andrescrz andrescrz left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The PR mostly LGTM now.

There's still one blocking comment in the MySQL table definitions regarding the type of client_id fields. We cannot change the migrations one pushed, I'd rather it address it now.

The rest of comments are optional. Many of them are nice to have, so I recommend tackling them as you have to send a new revision anyway.

Many comments are reflected once, but applies to multiple places (like record validations).

Let's do a final iteration and this should be good to go.

Comment thread apps/opik-backend/src/main/java/com/comet/opik/domain/mcpoauth/RevokedReason.java Outdated
LifeXplorer and others added 8 commits June 9, 2026 11:43
Replace the hand-written builders in McpOAuthService with a MapStruct
mapper (toCode/toToken/toRotatedToken), building each record complete in
one pass to respect the @nonnull record guards.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Merge revoke and revokeFamily into a single revokeBy query that swaps the
WHERE column via @define; keep both as thin default delegators passing a
hardcoded column literal.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The clients-table primary key follows the table-local id convention; the
client_id foreign keys in mcp_oauth_codes/mcp_oauth_tokens are unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Implement HasValue with @jsonvalue values and register an
AbstractEnumColumnMapper so persistence/serialization no longer rely on
the enum constant name.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@LifeXplorer LifeXplorer requested a review from andrescrz June 9, 2026 12:47

@andrescrz andrescrz left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM.

This was along and complex piece of work, with many moving pieces. But the implementation looks clean and solid.

Well done & thanks for putting it together!

@LifeXplorer LifeXplorer merged commit d12bd39 into main Jun 9, 2026
70 checks passed
@LifeXplorer LifeXplorer deleted the avinahradau/OPIK-6666-oauth-as-chunked-1 branch June 9, 2026 13:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Backend java Pull requests that update Java code tests Including test files, or tests related like configuration.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants