docs/REVERSE_PROXY.md
Cette page ne concerne que le proxy devant ORDS. L’IdP d’entreprise (authorize, token, JWKS, découverte OIDC) est un hôte différent — ne pas mettre ces URL IdP sur le proxy ORDS.
Topologie
OpenCode / client MCP
│
│ 1. POST https://ords-mcp.company.com/mcp
▼
public reverse proxy ──► ORDS :8080/mcp
│ │
│ │ 3. ORDS va chercher la JWKS (côté serveur)
│ ▼
│ https://login.company.com/.well-known/jwks.json
│
│ 2. OAuth navigateur (authorize / token) — PAS via le proxy ORDS
▼
https://login.company.com/authorize
| URL | Hôte | Qui l’appelle |
|---|---|---|
https://ords-mcp.company.com/mcp | ORDS (ce proxy) | Le client MCP |
https://ords-mcp.company.com/.well-known/oauth-protected-resource/mcp | ORDS (ce proxy) | Le client MCP (métadonnées de ressource, RFC 9728) |
https://login.company.com/authorize (token, etc.) | IdP d’entreprise | Navigateur / OpenCode |
https://login.company.com/.well-known/jwks.json | IdP d’entreprise | ORDS (sortant ; doit atteindre l’IdP depuis le processus ORDS) |
https://login.company.com/.well-known/openid-configuration | IdP d’entreprise | Client MCP / OpenCode |
/mcp), pas sous le contexte
REST /ords. standalone.context.path (par défaut /ords) ne concerne que REST/APEX.
2) Il n’existe aucun réglage ORDS de préfixe de chemin pour un proxy — l’audience n’est pas ce réglage.
L’audience
mcp.security.jwt.profile.audience est l’URL publique du MCP : la chaîne exacte que les clients
envoient en OAuth resource=, et l’API identifier / aud JWT de l’IdP.
Même valeur aux quatre endroits :
| Endroit | Exemple (recommandé) | Exemple (préfixe chemin — à éviter) |
|---|---|---|
| URL MCP du client | https://ords-mcp.company.com/mcp | https://my-proxy/ords/mcp |
| API identifier IdP | idem | idem |
JWT aud | idem | idem |
ORDS mcp.security.jwt.profile.audience | idem | idem |
Faire correspondre schéma, hôte, port, chemin. Pas de slash final, sauf si tous les côtés en ont un.
https://my-proxy/mcp et https://my-proxy/ords/mcp sont des audiences différentes.
https://my-proxy/ords/mcp ne fait pas générer à ORDS un
/ords/mcp dans WWW-Authenticate ni dans les métadonnées de ressource protégée.
Dans opencode.json, mcp.<name>.url est envoyée comme OAuth resource=
à l’authorize :
https://<idp>/authorize
?…
&resource=https://ords-mcp.company.com/mcp
Auth0/Entra fait correspondre resource= à l’API identifier ; cela devient le JWT aud ;
ORDS vérifie aud contre mcp.security.jwt.profile.audience. Utiliser l’URL publique
joignable depuis le poste, pas l’interne http://ords:8080/mcp.
Config ORDS liée au proxy
ords --config "$ORDS_CONFIG" config set --global \
mcp.security.jwt.profile.audience "https://ords-mcp.company.com/mcp"
ords --config "$ORDS_CONFIG" config set --global \
security.httpsHeaderCheck "X-Forwarded-Proto: https"
| Réglage | Effet |
|---|---|
mcp.security.jwt.profile.audience | aud JWT attendu = URL publique du MCP |
security.httpsHeaderCheck | X-Forwarded-Proto: https pour qu’ORDS sache que le TLS s’arrête au proxy |
security.externalSessionTrustedOrigins | Allowlist CORS si un Origin navigateur est l’hôte public |
standalone.context.path | REST/APEX uniquement — ne déplace pas /mcp |
Helm : config.jwtAudience doit être cette même URL publique (deploy/k8s/ords-mcp/values.yaml).
Topologie recommandée
Donner un nom d’hôte à ORDS. Ne pas retirer /ords. Ne pas placer l’IdP sur cet hôte.
https://ords-mcp.company.com/mcp
→ http://ords:8080/mcp
https://ords-mcp.company.com/.well-known/oauth-protected-resource/mcp
→ http://ords:8080/.well-known/oauth-protected-resource/mcp
Proxyer ces deux chemins ORDS sans les réécrire. Audience : https://ords-mcp.company.com/mcp.
C’est ce que supposent les docs Oracle MCP, ce lab et le Helm chart (ingress.hosts[].paths par défaut /).
Un catch-all / convient uniquement parce que cet hôte est ORDS, pas l’IdP.
Transmettre : Host (ou X-Forwarded-Host), X-Forwarded-Proto: https,
X-Forwarded-For. Désactiver le buffering et utiliser de longs timeouts sur /mcp (HTTP streamé).
ORDS doit toujours pouvoir sortir vers l’URL JWKS de l’IdP (NetworkPolicy, proxy, DNS). Ce trafic ne passe pas par ce reverse proxy.
# ORDS MCP uniquement. IdP / JWKS restent sur login.company.com — ne pas les ajouter ici.
location /mcp {
proxy_pass http://ords:8080;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Connection "";
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
# Métadonnées de *ressource* RFC 9728 sur l'hôte ORDS — pas JWKS, pas OIDC
location /.well-known/oauth-protected-resource/mcp {
proxy_pass http://ords:8080;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
Le piège
https://my-proxy/ords/ → http://ords)C’est la disposition qui casse la découverte :
https://my-proxy/ords/mcp → http://ords/mcp
ORDS ne voit que l’hôte my-proxy et le chemin /mcp. Il annonce donc :
| Élément | Reconstruit par ORDS | Utilisé par le client |
|---|---|---|
| Ressource MCP | https://my-proxy/mcp | https://my-proxy/ords/mcp |
WWW-Authenticate resource_metadata | https://my-proxy/.well-known/oauth-protected-resource/mcp | sous /ords/ si c’est tout ce qui est proxyé |
C’est attendu : il n’y a pas de bouton de préfixe. Ne pas forwarder
https://my-proxy/ords/mcp vers http://ords/ords/mcp sans retirer le préfixe : MCP n’est pas
à /ords/mcp côté backend (404).
/ords/ords pour que les POST atteignent /mcp.https://my-proxy/ords/mcp
→ http://ords/mcp
https://my-proxy/ords/.well-known/oauth-protected-resource/mcp
→ http://ords/.well-known/oauth-protected-resource/mcp
L’insertion de chemin RFC 9728 tente aussi la racine de l’hôte :
https://my-proxy/.well-known/oauth-protected-resource/ords/mcp
et ORDS lui-même annonce https://my-proxy/mcp et
https://my-proxy/.well-known/oauth-protected-resource/mcp — ces deux-là ne sont pas sous
/ords/. Une location qui ne proxye que /ords/ ne les verra jamais.
Ensuite, soit :
/mcp et /.well-known/oauth-protected-resource/mcp à la racine,
et pointer OpenCode / l’audience sur https://my-proxy/mcp, ouWWW-Authenticate et le champ JSON resource pour y inclure /ords
(fragile ; pas une fonctionnalité ORDS)./.well-known/ de l’IdP sur my-proxy
Ils restent sur l’hôte de l’IdP. Plus propre : exposer MCP à la racine (https://my-proxy/mcp) et
laisser /ords au REST/APEX classique s’il faut partager l’hôte.
Récapitulatif
| Chemin public (hôte recommandé) | Chemin ORDS | Rôle |
|---|---|---|
/mcp | /mcp | MCP HTTP streamé + défi 401 Bearer |
/.well-known/oauth-protected-resource/mcp | idem | Métadonnées de ressource protégée RFC 9728 (liste authorization_servers si configuré) |
| URL | Config | Rôle |
|---|---|---|
| JWKS IdP | mcp.security.jwt.profile.jwk.url | ORDS valide les signatures JWT |
| Issuer IdP | mcp.security.jwt.profile.issuer | JWT iss |
| authorize/token IdP | mcp.security.jwt.profile.authorization.server.url | Login navigateur OpenCode ; copié dans les métadonnées comme authorization_servers |
Un GET/POST non authentifié sur /mcp doit renvoyer 401 avec un
WWW-Authenticate pointant vers l’URL well-known ORDS ci-dessus. Ce document peut nommer
l’IdP ; le client parle ensuite à l’IdP sur son propre hôte.
Vérification
Comparer les URL annoncées à l’URL réellement utilisée par le client :
curl -sI https://ords-mcp.company.com/mcp
curl -s https://ords-mcp.company.com/.well-known/oauth-protected-resource/mcp
401 + WWW-Authenticate: Bearer … resource_metadata="https://ords-mcp.company.com/.well-known/oauth-protected-resource/mcp""resource": "https://ords-mcp.company.com/mcp"Si ces corps disent https://my-proxy/mcp pendant qu’OpenCode utilise
https://my-proxy/ords/mcp, c’est la reconstruction Host + /mcp. Changer l’audience ne corrige
que la validation JWT, pas ces URL annoncées. Après OAuth, le aud du jeton doit égaler cette même URL
publique du MCP (voir la page OAuth — contrôle du jeton).