docs/AUTH0_SETUP.md · ordre = important
Les étapes à faire dans l’ordre dans le dashboard Auth0, avec les valeurs exactes du lab. Prévoir 20–30 minutes la première fois.
Feuille de valeurs
| Variable / réglage | Valeur exacte du lab | Notes |
|---|---|---|
AUTH0_DOMAIN | ex. dev-xxxx.us.auth0.com | Sans https:// |
API Identifier / AUTH0_AUDIENCE | http://127.0.0.1:8080/mcp | Doit être égal à l’URL MCP d’OpenCode |
| Permission API | urn:oracle:dbtools:ords:mcpserver:all | Sur l’API et sur chaque rôle |
| Rôles | POOL.HR, POOL.FIN | Chaînes exactes = mcp.role ORDS |
| Claim des rôles JWT | roles2 | Pas roles (réservé par Auth0) |
ORDS MCP_ROLE_CLAIM | /roles2 | JSON pointer |
| Type d’app | Native | Pour le PKCE d’OpenCode |
AUTH0_CLIENT_ID | Client ID de l’app Native | Pas de client secret requis en PKCE |
| Callback URL | http://127.0.0.1:19876/mcp/oauth/callback | Chemin exact |
Les étapes
dev-xxxx.us.auth0.com → c’est AUTH0_DOMAIN.OpenCode envoie resource=http://127.0.0.1:8080/mcp : Auth0 ne l’accepte que si une API existe avec cet Identifier.
ORDS MCP Local, Identifier http://127.0.0.1:8080/mcp, Signing Algorithm RS256.http (pas https), 127.0.0.1 (pas localhost), :8080, /mcp, pas de slash final.https://ords.lab/mcp a déjà été créée par erreur, en créer une nouvelle avec
l’Identifier localhost et laisser l’ancienne inutilisée.
API → Permissions → Add : urn:oracle:dbtools:ords:mcpserver:all (« ORDS MCP global access »).
| Réglage | Valeur |
|---|---|
| Enable RBAC | On |
| Add Permissions in the Access Token | On |
| Allow Offline Access | On (pour offline_access d’OpenCode) |
Menu : User Management → Roles. Créer deux rôles dont les noms correspondent exactement aux mcp.role ORDS :
| Rôle | Pool ORDS |
|---|---|
POOL.HR | mcp-hr |
POOL.FIN | mcp-fin |
Pour chacun des deux rôles : onglet Permissions → Add Permissions → API
ORDS MCP Local → cocher urn:oracle:dbtools:ords:mcpserver:all → Add.
| Le jeton a | Résultat ORDS / OpenCode |
|---|---|
roles2: ["POOL.FIN"] seul | 403 Forbidden ; OpenCode reste needs_auth après le succès navigateur. |
roles2 et permissions: ["…mcpserver:all"] | Marche — pools filtrés par rôle. |
Menu : Applications → Applications → Create Application. Nom ex. OpenCode Native,
type Native. Ne pas utiliser Machine to Machine (pas de login navigateur).
| Réglage | Valeur |
|---|---|
| Client ID | → AUTH0_CLIENT_ID |
| Client Secret | Non nécessaire (PKCE) ; ne pas le mettre dans .env |
| Allowed Callback URLs | http://127.0.0.1:19876/mcp/oauth/callback, http://localhost:19876/mcp/oauth/callback |
| Allowed Logout URLs | http://127.0.0.1:19876, http://localhost:19876 |
| Allowed Web Origins (si affiché) | mêmes hôtes |
/mcp/oauth/callback, pas seulement /callback.Advanced Settings → Grant Types : Authorization Code = On, Refresh Token = On (recommandé), Client Credentials = Off pour Native (normal).
roles2Auth0 réserve roles : la liste personnalisée doit porter un autre nom. Dans l’UI, c’est
Actions → Triggers.
ORDS MCP roles2/**
* Auth0 réserve "roles". ORDS lit "roles2" (MCP_ROLE_CLAIM=/roles2).
* event.authorization.roles est rempli quand RBAC est activé et
* que des rôles sont assignés à l'utilisateur.
*/
exports.onExecutePostLogin = async (event, api) => {
const roles = event.authorization?.roles || [];
api.accessToken.setCustomClaim("roles2", roles);
};
scripts/test_auth0.py)Si on utilise une app Machine to Machine séparée pour les tests automatisés, attacher aussi au trigger credentials-exchange :
exports.onExecuteCredentialsExchange = async (event, api) => {
api.accessToken.setCustomClaim("roles2", ["POOL.HR", "POOL.FIN"]);
};
Puis autoriser cette app M2M sur l’API avec la permission MCP.
Menu : User Management → Users.
| Méthode | Notes |
|---|---|
| Compte base de données | Créer, définir le mot de passe, assigner les rôles. |
| Google / social | Après la 1re connexion, ouvrir l’utilisateur (google-oauth2|…) et assigner les rôles. |
| Identité de test | Rôle assigné |
|---|---|
| FIN uniquement | POOL.FIN seulement |
| HR uniquement | POOL.HR seulement |
| Les deux pools | les deux rôles |
L’onglet Roles sur l’utilisateur assigne le rôle. Les Permissions sur le rôle (étape 3)
fournissent le permissions du jeton.
.env du labAUTH0_DOMAIN=dev-xxxx.us.auth0.com
AUTH0_AUDIENCE=http://127.0.0.1:8080/mcp
AUTH0_CLIENT_ID=<Client ID de l'app Native>
# Pas d'AUTH0_CLIENT_SECRET pour Native + PKCE OpenCode
MCP_ROLE_CLAIM=/roles2
ORACLE_PWD=OracleFree123
ORDS_MODE=mcp
ORDS_HTTP_PORT=8080
ORDS_ENABLE_HTTPS=false
Le premier boot exécute sql/01_lab_schemas.sql dans un nouveau volume Oracle.
docker compose up -d --build
docker compose ps
Si l’audience/issuer Auth0 a changé alors qu’ORDS avait déjà tourné :
docker compose stop ords && docker compose rm -f ords
docker volume rm ords_ords-config
docker compose up -d ords
Vérifications
# /mcp sans jeton → 401 + resource_metadata=.../oauth-protected-resource/mcp
curl -sI http://127.0.0.1:8080/mcp
# métadonnées de ressource
curl -s http://127.0.0.1:8080/.well-known/oauth-protected-resource/mcp
# → "resource":"http://127.0.0.1:8080/mcp"
# → "authorization_servers":["https://VOTRE_DOMAINE"]
# ORDS doit joindre la JWKS d'Auth0 (le compose définit dns 1.1.1.1/8.8.8.8)
docker exec ords curl -fsSI "https://${AUTH0_DOMAIN}/.well-known/jwks.json" | head -5
Réglages JWT ORDS (via scripts/configure-mcp.sh) :
| Réglage | Valeur |
|---|---|
feature.mcp | true |
mcp.security.jwt.profile.issuer | https://<domaine>/ |
mcp.security.jwt.profile.audience | http://127.0.0.1:8080/mcp |
mcp.security.jwt.profile.jwk.url | https://<domaine>/.well-known/jwks.json |
mcp.security.jwt.profile.role.claim.name | /roles2 |
pool mcp-hr | mcp.role=POOL.HR, mcp.scope=…mcpserver:all |
pool mcp-fin | mcp.role=POOL.FIN, même scope |
Modèle mental
Utilisateur (+ login Google ou DB)
+ rôle Auth0 POOL.FIN
+ permission du rôle : urn:oracle:dbtools:ords:mcpserver:all
+ Action post-login → roles2
│
▼
Access token
aud = http://127.0.0.1:8080/mcp
roles2 = ["POOL.FIN"]
permissions = ["urn:oracle:dbtools:ords:mcpserver:all"]
│
▼
ORDS /mcp
→ MCP autorisé
→ database_list = [mcp-fin] seulement