Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,7 @@ go-webservice/go-webservice
go-jwks/go-jwks
go-jwks-multi/go-jwks-multi
go-oidc/go-oidc
kong-mcp/mcp-authgate
kong-mcp/mcp-authgate-linux
kong-mcp/kong-mcp
.env
22 changes: 20 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ Multi-language usage examples for AuthGate authentication (Go, Python, Bash).
| [go-jwks](go-jwks/) | API protection (offline) | JWKS public-key validation | Go | Go 1.25+ |
| [go-jwks-multi](go-jwks-multi/) | API protection (N iss) | JWKS validation (multi) | Go | Go 1.25+ |
| [go-oidc](go-oidc/) | Web login (no SDK) | Auth Code (coreos/go-oidc) | Go | Go 1.25+ |
| [kong-mcp](kong-mcp/) | MCP gateway (Kong) | PKCE entry + JWKS validation | Go | Go 1.25+, Kong |

## Environment Setup

All examples require `AUTHGATE_URL` and `CLIENT_ID`. M2M examples additionally require `CLIENT_SECRET`.
All examples except [kong-mcp](kong-mcp/) require `AUTHGATE_URL` and `CLIENT_ID`. M2M examples additionally require `CLIENT_SECRET`. The kong-mcp gateway reads no environment variables — configure it via the plugin block in [`kong-mcp/kong.yml`](kong-mcp/kong.yml) (`issuer`, `gateway_origin`, `jwks_uri`, ...).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

[Docs] 「All examples」的環境變數說明對 kong-mcp 不成立

kong-mcp 不讀任何環境變數、也不載入 .env——設定全在 kong.yml 的 plugin block(issuergateway_originjwks_uri...)。使用者照本節 export AUTHGATE_URL/CLIENT_ID 或寫 kong-mcp/.env 完全不會生效,且沒有任何訊息把他導向 kong.yml

修法(本 commit):本行與下方 .env 載入說明各加上 kong-mcp 例外。


Set via environment variables:

Expand All @@ -40,7 +41,7 @@ CLIENT_ID=your-client-id
CLIENT_SECRET=your-client-secret # M2M only
```

All examples automatically load `.env` if present. Environment variables take precedence over `.env` values.
All env-configured examples automatically load `.env` if present (kong-mcp does not read `.env`). Environment variables take precedence over `.env` values.

## Interactive CLI Authentication

Expand Down Expand Up @@ -138,6 +139,23 @@ go run main.go
# then open http://localhost:8080/
```

## MCP OAuth Gateway (Kong)

A Kong [go-pdk](https://github.com/Kong/go-pdk) plugin that puts one OAuth front
door in front of every MCP server. Internal MCP services stop accepting
hand-written PATs and instead require an AuthGate-issued access token. Kong does
not run the OAuth flow — it only advertises the entry point (`401` +
`WWW-Authenticate` → RFC 9728 Protected Resource Metadata) and validates the
returned token offline with **RS256 + JWKS** (alg-confusion blocked), then
forwards it to the MCP backend. The MCP client runs Auth Code + PKCE against
AuthGate itself. One plugin config covers all MCP servers.

```bash
cd kong-mcp
go mod tidy && go build -o mcp-authgate . # build the plugin
docker compose up --build # demo stack: Kong + stub MCP upstreams
```

## OAuth 2.0 Flows

- **Authorization Code + PKCE** — Browser-based login, most secure for CLI tools on machines with a browser. The client opens a browser, the user authenticates, and a code is exchanged for tokens.
Expand Down
5 changes: 5 additions & 0 deletions go-jwks-multi/testissuer/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,10 @@ func (i *issuer) sign(w http.ResponseWriter, r *http.Request) {
sub := def(q.Get("sub"), "test-user-1")
scope := def(q.Get("scope"), "email profile")
clientID := def(q.Get("client_id"), "test-client")
// AuthGate stamps a "type" claim on its tokens (access vs refresh); default
// to "access" so minted tokens pass resource servers that require it, and
// allow ?type=refresh to mint a non-access token for rejection tests.
tokenType := def(q.Get("type"), "access")
domain := q.Get("domain")
sa := q.Get("sa")
project := q.Get("project")
Expand All @@ -181,6 +185,7 @@ func (i *issuer) sign(w http.ResponseWriter, r *http.Request) {
"exp": now.Add(time.Duration(ttlSec) * time.Second).Unix(),
"client_id": clientID,
"scope": scope,
"type": tokenType,
}
// Server-attested claims are only set when explicitly requested, so you
// can mint "missing claim" tokens to exercise the resource server's
Expand Down
9 changes: 9 additions & 0 deletions kong-mcp/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Keep the image build context to the Go sources: editing docs or the
# runtime-mounted kong.yml must not invalidate the build cache.
*.md
kong.yml
docker-compose.yml
Dockerfile
.dockerignore
mcp-authgate
kong-mcp
28 changes: 28 additions & 0 deletions kong-mcp/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Build the Go plugin as a standalone pluginserver binary, then bake it into the
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
# Kong image. go-pdk plugins are ordinary executables that speak the Kong
# pluginserver RPC protocol over stdio — no cgo, no .so loading.
#
# Pin the binary to the FINAL image's architecture. Under BuildKit, TARGETOS /
# TARGETARCH are populated (e.g. by `docker build --platform`) and the binary
# cross-compiles to that target, matching the kong image it lands in. Under the
# classic builder they are empty, and an empty GOOS/GOARCH means "use the native
# default" — which is also the daemon's arch, so both stages still agree. Either
# way the binary's arch matches the kong container. A mismatch would make the
# pluginserver fail with `exec format error` and emit *nothing* on stdout, and
# Kong would die at init with `failed decoding plugin info: Expected value but
# found T_END at character 1` (an empty `-dump`).
FROM golang:1.26 AS build
ARG TARGETOS
ARG TARGETARCH
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \
go build -trimpath -ldflags="-s -w" -o /mcp-authgate .

FROM kong:3.9
COPY --from=build /mcp-authgate /usr/local/bin/mcp-authgate
USER kong
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
CMD kong health
Loading