Skip to content

Mount on your router

op.New returns a *op.Provider whose ServeHTTP makes it a standard http.Handler. The library does not own the listener and does not care which router you use.

The provider sits one level below your router, as a sibling of your own handlers. Your middleware still wraps it, and every path outside the ones it claims stays yours.

Where the provider sits in your server
The provider handler sits inside your own router, next to your own handlers. Your listener and your middleware wrap both of them, so telemetry and access logging cover the OP endpoints without any extra wiring.your listenerhttp.ListenAndServeyour middlewareotelhttp · promhttp · access logyour routernet/http · chi · ginYour handlers/ · /api/* · /healthz/metrics · /debug/pprofeverything the OP has no opinion aboutop.Provider/oidc/* — whatever prefix you mount it on/.well-known/openid-configurationone subtree, no global routes
The provider claims one subtree and nothing outside it — with the single exception of the discovery document, which the spec fixes at the host root. Everything wrapped around it is yours and keeps working unchanged.

Mount on net/http, chi, or gin

go
mux := http.NewServeMux()
mux.Handle("/", provider)
http.ListenAndServe(":8080", mux)
go
r := chi.NewRouter()
r.Mount("/", provider)
http.ListenAndServe(":8080", r)
go
r := gin.New()
r.Any("/*path", gin.WrapH(provider))
http.ListenAndServe(":8080", r)

Mount under a prefix

go
op.New(
  op.WithMountPrefix("/oidc"),
  /* ... */
)

The default /auth endpoint becomes /oidc/auth, /token becomes /oidc/token, and so on. Discovery (/.well-known/openid-configuration) is always mounted at the root — OIDC Discovery 1.0 §4 requires it.

Custom endpoint paths

go
op.New(
  op.WithEndpoints(op.Endpoints{
    Authorize: "/oauth/authorize",
    Token:     "/oauth/token",
  }),
  /* ... */
)

Empty fields keep the default. Defaults match OIDC Core 1.0 conventions: /auth, /token, /userinfo, /end_session, /jwks, plus option-gated protocol endpoints such as /par, /introspect, /revoke, /register, /device_authorization, /bc-authorize, and /grant_management.

What the OP does NOT mount

The OP is intentionally narrow about what it puts on your router:

The OP mountsThe OP does NOT mount
/.well-known/openid-configuration/metrics (you mount with promhttp)
/jwks/healthz, /readyz
/auth, /token, /userinforequest-duration histogram middleware
/end_sessionOpenTelemetry HTTP server span middleware
Optional: /par, /introspect, /revoke, /register, /device_authorization, /bc-authorize, /grant_managementa /debug/pprof mount
Optional: /interaction/*, /session/* (when SPA driver is used)a generic per-IP rate limiter

This is deliberate. The OP emits business metrics, traces, and audit events through op.WithPrometheus, op.WithLogger, and op.WithAuditLogger into registries / handlers that you own. HTTP-lifecycle observation is embedder territory — you wrap the router with otelhttp / promhttp.InstrumentHandler according to your SRE conventions.

TLS / proxies

The OP expects to run behind a TLS-terminating ingress. Use op.WithTrustedProxies(cidrs ...) to allow-list the proxy ranges that supply X-Forwarded-For.

If the proxy also terminates client TLS and forwards the certificate in a header for RFC 8705 certificate-bound access tokens, add op.WithMTLSProxy(headerName, cidrs).

Next

  • Use cases — concrete wirings for SPA, client_credentials, hot/cold storage, MFA, etc.
  • Security: posture — what the cookie / CSRF / SSRF defaults are, and how to widen them when you actually need to.