Skip to content

refactor(server): slim the Server and dedupe issuer-URL helpers - #4929

Merged
nabokihms merged 9 commits into
dexidp:masterfrom
deckhouse:refactor-server-slimdown
Jul 27, 2026
Merged

refactor(server): slim the Server and dedupe issuer-URL helpers#4929
nabokihms merged 9 commits into
dexidp:masterfrom
deckhouse:refactor-server-slimdown

Conversation

@nabokihms

@nabokihms nabokihms commented Jul 24, 2026

Copy link
Copy Markdown
Member

Overview

Housekeeping on the server package after the handler-extraction refactor: the Server struct had accumulated fields and methods that no longer earn their place, the issuer-URL path helpers were copy-pasted across every handler, and the gRPC API reached back into the Server for discovery.

What this PR does / why we need it

  • One issuer-URL helper. New oauth2.IssuerURL wraps url.URL with AbsPath/AbsURL. Handlers hold it instead of a bare url.URL, replacing four identical absPath and three absURL implementations (authflow, consent, mfa, logout, and Server) with a single one.
  • Drop dead connector methods. Server.CloseConnector had no callers left — the connector cache was extracted into connectors.Cache, and the gRPC API invalidates it directly (d.connectors.Close). Server.OpenConnector was a thin pass-through; its callers now use s.connectors.Open. Both removed.
  • Build discovery once, hand it to the API. The discovery handler was reconstructed from scattered Server fields on every call; it is now built once and stored. apiserver.NewAPI takes the *discovery.Handler directly (symmetric with the *connectors.Cache it already holds) instead of a func(ctx) Document that called back into the Server, and Server.ConstructDiscovery becomes a plain Discovery() accessor.
  • Slim the Server struct. Six construction-only fields (alwaysShowLogin, passwordConnector, auth/deviceRequestsValidFor, mfaProviders, defaultMFAChain) only ever fed a handler at wiring time and are now locals / direct config reads. Together with the discovery change, the struct drops from 20 fields to 14 — the ones actually read after construction, plus a few the in-package tests use.
  • Concrete routeMux. It was a bundle of four closures with four delegating methods over a fifth header-wrapping closure. It is now a real router.Mux implementation: issuer-path prefixing, header/context wrapping, and CORS live in its methods, and the direct routes (/healthz, /static, /robots.txt) go through it too.

No behavior change; existing tests cover the affected paths.

Special notes for your reviewer

Tests that constructed handlers with a raw url.URL were updated to oauth2.IssuerURL{...}.

Follow-up cleanup after the handler-extraction refactor:

- oauth2.IssuerURL: a small url.URL wrapper with AbsPath/AbsURL. Handlers
  hold it instead of a bare url.URL, so the four copies of absPath and three
  of absURL scattered across authflow, consent, mfa, logout and Server are
  gone in favor of one implementation.
- Drop Server.OpenConnector and Server.CloseConnector. Both were thin
  pass-throughs to the connector cache; CloseConnector had no callers left
  after the cache was extracted (the gRPC API invalidates it directly), and
  OpenConnector's only users now call s.connectors.Open.
- Build the discovery handler once and store it, instead of reconstructing it
  from scattered Server fields on every call. The mounted HTTP route and the
  gRPC API's ConstructDiscovery serve the same handler, and the
  response-types/grant-types/pkce fields no longer need to live on Server.

Signed-off-by: maksim.nabokikh <max.nabokih@gmail.com>
Copilot AI review requested due to automatic review settings July 24, 2026 14:53

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Refactors the server package to reduce Server surface area and centralize issuer-relative URL/path construction, while keeping discovery generation consistent across HTTP and gRPC.

Changes:

  • Introduces server/oauth2.IssuerURL (wrapping url.URL) to provide shared AbsPath/AbsURL helpers used by handlers and tests.
  • Removes unused Server.OpenConnector/Server.CloseConnector methods and updates call sites to use the connector cache directly.
  • Builds the OIDC discovery handler once during server construction and reuses it for both HTTP routing and gRPC discovery construction.

Reviewed changes

Copilot reviewed 25 out of 25 changed files in this pull request and generated no comments.

Show a summary per file
File Description
server/session/session.go Switches session manager issuer field to oauth2.IssuerURL.
server/server.go Stores issuer as oauth2.IssuerURL, removes dead connector methods, and builds/caches discovery handler once.
server/server_test.go Updates tests to use IssuerURL.AbsPath/AbsURL and validates grants via srv.discovery.
server/server_oauth2_test.go Updates server test setup to wrap issuer URL and pass raw url.URL to token issuer.
server/server_login_test.go Updates tests to open connectors via s.connectors.Open.
server/server_introspection_test.go Updates introspection helpers to use the embedded url.URL where needed.
server/server_grant_password_test.go Updates tests to open connectors via s.connectors.Open.
server/server_authorize_test.go Updates tests to open connectors via s.connectors.Open.
server/oauth2/issuer.go Adds IssuerURL helper type with AbsPath/AbsURL.
server/mfa/handler.go Replaces per-handler absPath helper with IssuerURL.AbsPath.
server/mfa/handler_test.go Updates MFA handler tests to construct oauth2.IssuerURL.
server/logout/logout.go Replaces per-handler absURL helper with IssuerURL.AbsURL.
server/home/home.go Uses IssuerURL.AbsURL for logout link construction.
server/device/device.go Uses IssuerURL.AbsPath for device verification and callback path building.
server/device/device_test.go Updates device handler tests to construct oauth2.IssuerURL.
server/consent/consent.go Replaces per-handler absPath helper with IssuerURL.AbsPath.
server/authflow/urls.go Switches flow redirect construction to IssuerURL.AbsPath.
server/authflow/sessionlogin_test.go Updates session test server setup to use oauth2.IssuerURL for handlers and sessions.
server/authflow/request.go Updates device callback redirect handling to use IssuerURL.AbsPath.
server/authflow/request_test.go Updates authflow handler setup to construct oauth2.IssuerURL.
server/authflow/render.go Removes per-handler absPath/absURL helpers in favor of IssuerURL.
server/authflow/login.go Updates connector/login URL construction to use IssuerURL.AbsPath/AbsURL.
server/authflow/handler.go Changes handler issuer field type to oauth2.IssuerURL.
server/authflow/handler_test.go Updates flow assembly tests to construct oauth2.IssuerURL for all handlers.
server/authflow/authorize.go Updates connector selection redirects to use IssuerURL.AbsPath.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@nabokihms nabokihms added the release-note/ignore Ignore this change when generating release notes label Jul 24, 2026
… fields, make routeMux concrete

Follow-ups on the same theme:

- The gRPC API took a func(ctx) Document that called back into the Server.
  Since it just needs the discovery handler, NewAPI now takes *discovery.Handler
  directly (symmetric with the *connectors.Cache it already holds) and calls
  Construct itself. Server.ConstructDiscovery becomes a plain Discovery()
  accessor.
- Demote six construction-only fields (alwaysShowLogin, passwordConnector,
  auth/deviceRequestsValidFor, mfaProviders, defaultMFAChain) to locals /
  direct config reads; they only ever fed a handler at wiring time. Server is
  down to the fields actually read after construction plus the few in-package
  tests reach into.
- routeMux was a bundle of four closures with four delegating methods over a
  fifth header-wrapping closure. It is now a concrete router.Mux: the path
  prefixing, header/context wrapping, and CORS live in its methods, and the
  direct routes (/healthz, /static, /robots.txt) go through it too.

Signed-off-by: maksim.nabokikh <max.nabokih@gmail.com>
Copilot AI review requested due to automatic review settings July 24, 2026 15:23

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 28 out of 28 changed files in this pull request and generated 3 comments.

Comment thread server/server.go Outdated
Comment thread server/server.go Outdated
Comment thread server/apiserver/api.go
…e fields

The Server struct kept several construction-only fields alive only because
in-package tests reached into them. Rewire those tests so the fields can go:

- oauth2_test built a throwaway Server just to get a token issuer; build the
  tokens.Issuer directly instead.
- introspection_test read s.refreshTokenPolicy; it already has the policy as a
  local, so use that.
- the multi-connector test helper set s.skipApproval after construction; set
  SkipApprovalScreen in its config like the others do.

With those readers gone, drop signer, now, idTokensValidFor, refreshTokenPolicy
and skipApproval from the struct — they only fed handlers at construction and
are now locals / direct config reads. Server is down from 20 fields to 9: the
six read after construction (connectors, storage, mux, templates, logger,
discovery) plus issuer, issuerURL and sessionConfig, which the tests still use
as fixture handles to mint tokens, build URLs and session cookies.

Signed-off-by: maksim.nabokikh <max.nabokih@gmail.com>
Copilot AI review requested due to automatic review settings July 24, 2026 15:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 28 out of 28 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (2)

server/server.go:219

  • The routeMux doc comment says every route is "wrapped" with common headers/context/instrumentation, but HandlePrefix mounts prefix handlers without calling wrap (so it only prefixes/strips). This makes the comment misleading for static/theme prefix routes.
// routeMux implements router.Mux over a gorilla mux.Router: every route is
// prefixed with the issuer path and wrapped with the common response headers,
// request-id/real-ip context, and instrumentation; HandleCORS additionally adds
// CORS for the public endpoints. instrument and realIP are the server's
// (prometheus- and config-bound) closures.

server/server.go:398

  • This comment still references the removed ConstructDiscovery method (it now passes the shared discovery.Handler to the gRPC API). Updating it avoids confusion about how discovery is served over gRPC.
	// Build the discovery handler once from config; both the mounted HTTP route
	// and the gRPC API's ConstructDiscovery serve this same handler.

Comment thread server/server_api_cache_test.go Outdated
Address review + lint:

- The router package held only interfaces while the sole implementation lived
  in server. Move it there as router.New(router.Config) so the package owns
  both the contract and its default implementation; server just wires config
  into it. Handlers are unchanged (still Mount(router.Mux)).
- Fix the routeMux doc: HandlePrefix mounts static trees directly, it does not
  apply the header/instrumentation wrap (unchanged behavior).
- Update the discovery comment that still named the removed ConstructDiscovery.
- GetDiscovery returns an error instead of panicking when no discovery handler
  is configured (some tests pass nil).
- Drop the stale rate-limit TODO.
- gci import grouping.

Signed-off-by: maksim.nabokikh <max.nabokih@gmail.com>
Copilot AI review requested due to automatic review settings July 24, 2026 15:47
Signed-off-by: maksim.nabokikh <max.nabokih@gmail.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 29 out of 29 changed files in this pull request and generated no new comments.

…resolver from Server

Extract the connector registry (ConnectorsConfig, openConnector), the local
password DB, and the resolver into server/connector.go. resolveConnector was a
Server method reading s.storage/s.logger; it becomes connectorResolver(storage,
logger) returning the connectors.ResolveFunc the cache already injects, so
connector construction no longer depends on the Server. server.go sheds all the
connector/* imports along with it.

Signed-off-by: maksim.nabokikh <max.nabokih@gmail.com>
Copilot AI review requested due to automatic review settings July 24, 2026 16:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

…tor map

The connector registry, config parsing and the local password DB moved into the
connectors package as connectors.Resolver(storage, logger, configs), which
returns the ResolveFunc the cache already takes. The config map is injected by
the caller, so the connectors package imports no connector implementation and a
library consumer can supply its own set. server keeps only ConnectorsConfig (the
built-in map) plus ConnectorConfig/LocalConnector aliases for compatibility.
NewPasswordDB is exported so a custom resolver can reuse the local connector.

Signed-off-by: maksim.nabokikh <max.nabokih@gmail.com>
Copilot AI review requested due to automatic review settings July 24, 2026 17:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 31 out of 31 changed files in this pull request and generated no new comments.

…he server aliases

Split passwordDB into connectors/password.go, leaving resolve.go with just the
resolver and the config-map contract. Remove the server-package type/const
aliases to connectors.ConnectorConfig / connectors.LocalConnector; cmd now
references those symbols in the connectors package directly. server keeps the
built-in ConnectorsConfig map (typed with connectors.ConnectorConfig) that it
hands to connectors.Resolver.

Signed-off-by: maksim.nabokikh <max.nabokih@gmail.com>
Copilot AI review requested due to automatic review settings July 27, 2026 15:30

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 33 out of 33 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

server/router/router.go:75

  • mountMux.wrap always calls Config.Instrument; if Instrument is nil, this will panic. Since router.New is exported and Config doesn’t document Instrument as required, it’s safer to fall back to calling the handler directly when Instrument isn’t provided.
		m.c.Instrument(name, h)(w, r.WithContext(rCtx))

Comment thread server/router/router.go
Comment thread server/connectors/resolve.go
Comment thread server/connector.go
…ig directly

Address review: router.wrap only calls RealIP when both RealIPHeader is set and
a resolver was provided (a library caller could set one without the other);
openConnector unmarshals conn.Config ([]byte) directly instead of round-tripping
through string.

Signed-off-by: maksim.nabokikh <max.nabokih@gmail.com>
Copilot AI review requested due to automatic review settings July 27, 2026 15:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 33 out of 33 changed files in this pull request and generated no new comments.

@nabokihms
nabokihms merged commit d439245 into dexidp:master Jul 27, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release-note/ignore Ignore this change when generating release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants