Skip to content

feat(oidc): serve OpenID discovery document and JWKS endpoint#30

Merged
polaz merged 3 commits into
mainfrom
feat/#29-oidc-discovery
Jun 19, 2026
Merged

feat(oidc): serve OpenID discovery document and JWKS endpoint#30
polaz merged 3 commits into
mainfrom
feat/#29-oidc-discovery

Conversation

@polaz

@polaz polaz commented Jun 19, 2026

Copy link
Copy Markdown
Member

Summary

oidc_discovery config was parsed but no endpoints were served. This implements the OIDC discovery surface so the proxy can front an identity provider, completing the last Roadmap-tagged config block.

What's added (when oidc_discovery.enabled)

  • GET /.well-known/openid-configuration — OpenID Provider metadata built from config: issuer, optional authorization_endpoint / token_endpoint / userinfo_endpoint, jwks_uri, and the standard advertised fields (response_types_supported, subject_types_supported, id_token_signing_alg_values_supported from the signing key, scopes_supported, token_endpoint_auth_methods_supported). Absent endpoints are omitted, not null.
  • JWKS endpoint — served at the path of jwks_uri (default /.well-known/jwks.json), built from the Ed25519 public-key PEM as an OKP/Ed25519 JWK (x = the 32 SPKI public bytes, kid derived from the key). Non-EdDSA signing keys are rejected with a clear error.

Routes are public, alongside the health endpoints (and covered by the default maintenance exempt paths).

Design

Discovery doc, JWKS, PEM→JWK, and URI-path extraction are pure, unit-tested functions; responses are precomputed at startup and served as static JSON.

Testing

7 tests: discovery fields (incl. endpoint omission), jwks_uri path extraction, Ed25519 PEM → OKP JWK, non-EdDSA rejection, build wiring, and an end-to-end test serving both endpoints through a real router. cargo nextest run --features redis: 92 passed. clippy (all-features) + fmt clean.

Out of scope (follow-up)

RSA / EC signing keys in the JWKS, dynamic key rotation.

Docs

README: OIDC discovery moved from Roadmap to Features; the last [roadmap] config tag is removed.

Closes #29

When oidc_discovery.enabled, the proxy now serves the OpenID Provider
metadata and a JWKS so it can front an identity provider:

- GET /.well-known/openid-configuration built from config: issuer, the
  optional authorization/token/userinfo endpoints, jwks_uri, and the
  standard advertised fields (response_types, subject_types,
  id_token_signing_alg_values from the signing key, scopes, auth methods).
- A JWKS served at the path of jwks_uri (default /.well-known/jwks.json)
  built from the Ed25519 public-key PEM (OKP / Ed25519 JWK, kid derived
  from the key). Non-EdDSA signing keys are rejected with a clear error.

Routes are public, alongside the health endpoints. RSA/EC signing keys
in the JWKS are left for a follow-up.

Closes #29
@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 8ae59823-453a-4bb9-b281-e8e138706980

📥 Commits

Reviewing files that changed from the base of the PR and between 1bea33a and ed16f83.

📒 Files selected for processing (1)
  • src/oidc/mod.rs

📝 Walkthrough

Summary by CodeRabbit

Release Notes

  • New Features

    • Added OpenID Connect (OIDC) discovery endpoints, including /.well-known/openid-configuration and a JWKS endpoint.
    • When enabled, the server publishes configured authorization/token/userinfo endpoints and serves a JWKS (empty keys when no signing key is configured).
  • Documentation

    • Updated configuration examples and the Roadmap section to reflect OIDC discovery support as a tracked (not enforced) setting.

Walkthrough

Implements OIDC discovery serving: adds a new oidc module that builds an OpenID Provider metadata JSON document and an optional JWKS endpoint from OidcDiscoveryConfig, including Ed25519 PEM-to-JWK conversion with a SHA-256-derived key id. The module is wired into the proxy router, and the README reflects the feature as active.

Changes

OIDC Discovery and JWKS Endpoint

Layer / File(s) Summary
Oidc struct, build, routes, discovery doc, and Ed25519 JWK
src/oidc/mod.rs
Defines Oidc::build to conditionally construct OpenID Provider metadata JSON and an optional JWKS from OidcDiscoveryConfig. Oidc::routes registers GET /.well-known/openid-configuration and the JWKS path derived from the configured URI. Ed25519 PEM public keys are converted to OKP JWKs with a stable SHA-256-truncated kid. Non-EdDSA algorithms are rejected.
Unit and integration tests
src/oidc/mod.rs
Tests cover discovery document field inclusion/omission, JWKS URI path parsing, Ed25519 JWK field values, algorithm rejection, disabled-config behavior, and Axum route HTTP responses for both endpoints.
Router wiring
src/lib.rs
Exports pub mod oidc, conditionally builds oidc_routes from config.oidc_discovery via oidc::Oidc::build, and merges the result into the main axum Router.
README config and roadmap update
README.md
Moves the OIDC discovery bullet from Roadmap to active features, inlines an oidc_discovery YAML block in the configuration snippet, and removes the prior disclaimer that the section was accepted but not yet served.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant AxumRouter
  participant Oidc
  participant ed25519_jwk

  Note over AxumRouter,Oidc: Startup — ProxyServer::router()
  AxumRouter->>Oidc: Oidc::build(OidcDiscoveryConfig)
  Oidc->>ed25519_jwk: decode PEM → compute kid via SHA-256
  ed25519_jwk-->>Oidc: OKP JWK (kty, crv, x, kid)
  Oidc-->>AxumRouter: routes merged (discovery + JWKS)

  Note over Client,AxumRouter: Runtime requests
  Client->>AxumRouter: GET /.well-known/openid-configuration
  AxumRouter-->>Client: OpenID Provider metadata JSON

  Client->>AxumRouter: GET /.well-known/jwks.json
  AxumRouter-->>Client: JWK Set JSON
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the main change: implementing OIDC discovery endpoints (GET /.well-known/openid-configuration and JWKS endpoint) to serve OpenID Provider metadata and key sets.
Description check ✅ Passed The description comprehensively explains what was added, why, and how the implementation works. It details the two endpoints, design decisions, testing coverage, and out-of-scope items, all closely related to the changeset.
Linked Issues check ✅ Passed The changeset fully implements all coding objectives from issue #29: OpenID configuration endpoint [#29], JWKS endpoint with Ed25519 support [#29], public access [#29], comprehensive unit tests [#29], and README update moving OIDC from Roadmap to Features [#29].
Out of Scope Changes check ✅ Passed All changes are directly aligned with the linked issue scope. The README update, new OIDC module, router integration, and comprehensive tests are all within the stated objectives. RSA/EC keys and key rotation are explicitly deferred.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#29-oidc-discovery

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

@greptile-apps

greptile-apps Bot commented Jun 19, 2026

Copy link
Copy Markdown

Greptile Summary

This PR implements the OIDC discovery surface that was previously config-parsed but never served: a /.well-known/openid-configuration endpoint and a JWKS endpoint (defaulting to /.well-known/jwks.json), both precomputed at startup and mounted as public routes alongside health endpoints.

  • The SPKI validation for Ed25519 keys is exact (prefix + 32-byte key), so RSA/EC keys under an EdDSA label are rejected at startup rather than silently publishing garbage bytes; an empty {"keys":[]} is always served at the JWKS path when no signing key is configured, so the advertised jwks_uri never returns 404.
  • The JWKS route correctly sets Content-Type: application/jwk-set+json (RFC 7517); the discovery document uses application/json as required by OIDC Discovery §4.2.
  • Previous review findings (SPKI bounds check, empty JWKS fallback, content-type) are all addressed; remaining notes concern the discovery document advertising EdDSA as the signing algorithm and token_endpoint_auth_methods_supported even when no signing key / token endpoint is configured.

Confidence Score: 5/5

Safe to merge; the OIDC routes are additive and public, startup errors are propagated cleanly, and all key-parsing guards are correct.

The change is self-contained — it adds new public routes that don't touch auth, transcoding, or rate-limiting paths. Key parsing is exact and fails fast at startup for bad input. The two observations in the review are spec-compliance nits around advertised metadata values; neither causes incorrect behavior in the common case where a signing key is actually configured.

No files require special attention; the new src/oidc/mod.rs is well-tested and all three previous blocking findings are addressed.

Important Files Changed

Filename Overview
src/oidc/mod.rs New OIDC discovery module: builds and serves /.well-known/openid-configuration and a JWKS endpoint; key parsing is properly validated; previous review issues all addressed. Minor: id_token_signing_alg_values_supported hard-defaults to ["EdDSA"] when no signing key is configured, creating a semantic mismatch with the empty JWKS.
src/lib.rs Wires OIDC routes into the main router at startup, propagating config errors cleanly; routes are merged in the correct public position alongside health and OpenAPI routes.
README.md OIDC discovery promoted from Roadmap to Features; [roadmap] tag removed from config comment; jwks_uri example added with an inline clarifying note.

Reviews (2): Last reviewed commit: "fix(oidc): validate Ed25519 SPKI, always..." | Re-trigger Greptile

Comment thread src/oidc/mod.rs
Comment thread src/oidc/mod.rs Outdated
Comment thread src/oidc/mod.rs Outdated

@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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/oidc/mod.rs`:
- Around line 45-57: The `Oidc::build` function unconditionally creates a
default `jwks_uri` and passes it to `discovery_document`, but the `routes()`
function only serves JWKS when `config.signing_key` is present, causing the
discovery document to advertise a URI that returns 404. Make the `jwks_uri`
creation conditional on the presence of `config.signing_key` (similar to how
`jwks` is already conditionally created), so the discovery document only
advertises a JWKS endpoint when it will actually be served by the routes.
- Around line 149-154: The current validation in the Ed25519 key extraction
logic only checks if the DER payload is at least 32 bytes long, but does not
validate the actual SPKI structure format to confirm it is a valid Ed25519
public key. This allows non-Ed25519 keys to pass validation if they happen to be
>= 32 bytes. Before extracting the raw key material from the DER bytes using the
slice operation at `&der[der.len() - ED25519_PUBLIC_KEY_LEN..]`, add proper SPKI
structure validation that checks the algorithm identifier and other structural
markers to ensure the DER payload is a legitimate Ed25519 SPKI-encoded public
key, not just any 32+ byte payload.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e0ca9021-5eaf-47aa-bbd4-f45ebd0040f1

📥 Commits

Reviewing files that changed from the base of the PR and between 5e12cc6 and 1bea33a.

📒 Files selected for processing (3)
  • README.md
  • src/lib.rs
  • src/oidc/mod.rs

Comment thread src/oidc/mod.rs
Comment thread src/oidc/mod.rs Outdated
polaz added 2 commits June 19, 2026 19:15
- A non-Ed25519 key (EC P-256) under algorithm "EdDSA" is accepted by the
  loose length check and its last 32 bytes are published as a bogus key.
- When no signing key is set, the discovery document still advertises a
  local jwks_uri, but that path returns 404 (no JWKS route mounted).

Both tests fail on current code; fixes follow.

Part of #29
- ed25519_jwk now requires the exact Ed25519 SPKI shape (12-byte prefix +
  32-byte key) instead of any payload >= 32 bytes, so an RSA/EC key under
  algorithm "EdDSA" fails loudly at startup rather than publishing its
  tail bytes as a bogus Ed25519 key.
- The JWKS endpoint is always mounted (an empty key set when no signing
  key is configured), so the advertised jwks_uri never returns 404.
- The JWKS is served with the RFC 7517 media type
  application/jwk-set+json instead of application/json.

Part of #29
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.

feat(oidc): serve OpenID discovery document and JWKS endpoint

1 participant