About Gateway Oauth Helpers
Gateway Oauth Helpers is an MCP server published by docker in the Security category: library containing OAuth Dynamic Client Registration (DCR) functionality for MCP servers.
The repository has 4 stars and 4 forks, with the last commit 9 days ago. Recent commit activity is the strongest signal that reported issues will get attention.
Install
git clone https://github.com/docker/mcp-gateway-oauth-helpersThis server has no ConduID identity, so agent calls to it are not receipted. Pin the version you install and review the source before granting it credentials.
Ask AI
Ask AI about Gateway Oauth Helpers
Powered by Claude · Grounded in docs
Security checks
Static checks derived from the repository 19 hours ago. Heuristics on the source, not an audit.
- ✓README presentThe repository ships documentation.
- ✓License declaredUsable under a stated license.
- ✓Tests presentA test suite exists in the repository.
- !Dependencies pinnedNo lockfile. Installs can pull different code over time.
- ✓No dynamic code executionSource scan found no eval, dynamic Function, or shell execution.
- !Scoped permissionsDoesn't declare a permission scope. Assume it can do anything its process can.
Releases
README
MCP Gateway OAuth Helpers
Library containing OAuth Dynamic Client Registration (DCR) functionality for MCP servers.
Note: This code was extracted from MCP Gateway PR: https://github.com/docker/mcp-gateway/pull/148
Purpose
This library provides the core OAuth/DCR functions for MCP Gateway:
- OAuth Discovery: Discover OAuth requirements from MCP servers (RFC 9728 + 8414)
- Dynamic Client Registration: Register OAuth clients automatically (RFC 7591)
- WWW-Authenticate Parsing: Parse OAuth challenge headers
Local development
OAuth discovery's authorization-server SSRF guard is advisory, not blocking:
by default it flags localhost, private, link-local, and reserved destinations
(including redirect targets and the DNS-rebinding-safe dial-time address) as
disallowed, logs a warning naming the rejected address via the Logger
installed with WithLogger, and then still makes the request against that
real address. Discovery only fails when the fetch itself fails for an
unrelated reason (connection error, TLS failure, timeout, non-200 status,
unparseable JSON, issuer mismatch).
For a single DiscoverOAuthRequirements call, WithSkipSSRFCheck(ctx) turns
the guard off entirely for that call: no scheme/hostname/address checks, no
dial-time pinning, and no warning log. Use it when the caller has already
made its own trust decision about the target (e.g. an operator explicitly
opted a request out of the guard).
DOCKER_MCP_ALLOW_INSECURE_REMOTE_URLS=1 is unrelated and unaffected by the
above: it still exists to relax the RFC 8414 HTTPS scheme requirement for
local development with an HTTP OAuth provider, and continues to disable the
authorization server network guard process-wide when set. Because it is
process-wide and disables both the HTTPS requirement and the network guard
entirely, it must not be enabled with untrusted MCP servers.
For running a single MCP server on localhost during local development,
WithAllowLocalHTTP(ctx) is a narrower, recommended alternative: it scopes
the same "allow http" relaxation to localhost, *.localhost, and loopback
addresses (127.0.0.0/8, ::1) for that one DiscoverOAuthRequirements
call, while every other blocked hostname (.local, .internal, cloud
metadata hosts, etc.) and every other blocked address range (RFC1918,
link-local, etc.) stays subject to the guard exactly as without the option.
It answers "is this destination local," not "is the guard on at all," so it
does not imply WithSkipSSRFCheck (or vice versa) — set either, both, or
neither depending on what the caller needs:
ctx := oauth.WithAllowLocalHTTP(context.Background())
discovery, err := oauth.DiscoverOAuthRequirements(ctx, "http://localhost:8080/mcp")
Reusing a registration (RFC 7592)
PerformDCRWithConfig keeps only the client credentials, so a caller cannot
reuse or manage the registration afterwards. To persist a registration and
reuse it instead of registering on every authorization, use these helpers:
RegisterClient(ctx, discovery, serverName, config)— same request asPerformDCRWithConfig, but returns the fullClientRegistration: issuer,client_id,client_secret,client_secret_expires_at,registration_access_token,registration_client_uri, redirect URIs, scope and token endpoint auth method.GetRegistration(ctx, reg)— RFC 7592 GET ofregistration_client_uri. Returns the server's current view of the client.UpdateRegistration(ctx, reg, req)— RFC 7592 PUT.reqis the complete client metadata (the server replaces, it does not merge); use it to renew an expired secret or add a scope or redirect URI.ErrRegistrationGone— returned by both when the server answers 401 or 404: the record was dropped, so discard it and register again.ErrNoRegistrationManagement— the registration has noregistration_client_uri/registration_access_token, so RFC 7592 is not possible; register again instead.ErrInsecureRegistrationURI—registration_client_uri, or a redirect from it, is not https; the registration access token is never sent. The check follows the authorization-server transport:WithAllowLocalHTTPpermits http to loopback hosts, andWithSkipSSRFCheckorDOCKER_MCP_ALLOW_INSECURE_REMOTE_URLS=1turn it off. The token is also dropped from any redirect that leaves the original origin.
RegisterClient, GetRegistration and UpdateRegistration send their requests
through the same guarded authorization-server HTTP client as discovery: dial-time
address pinning and the warn-by-default private-address check (a flagged address
is logged, not refused), with a 30 second timeout in addition to any context
deadline. Server-supplied error bodies are truncated to 256 bytes in returned
errors.
(*ClientRegistration).SecretExpired(now)— whetherclient_secret_expires_athas passed (0never expires).IsInvalidClientError(status, body)/IsInvalidClientErrorCode(code)— detect aninvalid_client(RFC 6749 §5.2) token endpoint response or authorize redirect, meaning the cached registration should be discarded.
reg, err := oauth.RegisterClient(ctx, discovery, "my-server", cfg)
// ... persist reg, keeping ClientSecret and RegistrationAccessToken out of
// plain-text storage ...
got, err := oauth.GetRegistration(ctx, reg)
switch {
case errors.Is(err, oauth.ErrRegistrationGone):
// the server dropped the client: register again
case errors.Is(err, oauth.ErrNoRegistrationManagement):
// nothing to validate with
}
ClientRegistration marshals client_secret and registration_access_token
into JSON, so do not write it to disk as is. mcpruntime keeps those two in a
separate secret store; see its docs/dcr-memoization.md.
Configuring redirect URI validation
By default DCR only accepts redirect URI hosts for localhost, mcp.docker.com, and mcp-stage.docker.com.
Use PerformDCRWithConfig to provide a custom allowlist:
allowedHosts := append(oauth.DefaultAllowedRedirectURIHosts(), "oauth.example.com")
creds, err := oauth.PerformDCRWithConfig(ctx, discovery, "my-server", oauth.DCRConfig{
RedirectURI: "https://oauth.example.com/callback",
AllowedRedirectURIHosts: allowedHosts,
})
README fetched from the repository 19 hours ago. The original is authoritative.