docs/AUTH0_SETUP.md · ordre = important

Configurer Auth0 pour ORDS MCP

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

Valeurs à retenir (tout le reste en découle)

Variable / réglageValeur exacte du labNotes
AUTH0_DOMAINex. dev-xxxx.us.auth0.comSans https://
API Identifier / AUTH0_AUDIENCEhttp://127.0.0.1:8080/mcpDoit être égal à l’URL MCP d’OpenCode
Permission APIurn:oracle:dbtools:ords:mcpserver:allSur l’API et sur chaque rôle
RôlesPOOL.HR, POOL.FINChaînes exactes = mcp.role ORDS
Claim des rôles JWTroles2Pas roles (réservé par Auth0)
ORDS MCP_ROLE_CLAIM/roles2JSON pointer
Type d’appNativePour le PKCE d’OpenCode
AUTH0_CLIENT_IDClient ID de l’app NativePas de client secret requis en PKCE
Callback URLhttp://127.0.0.1:19876/mcp/oauth/callbackChemin exact

Les étapes

Étape par étape

1 · Dashboard et domaine

2 · L’API (resource server)

OpenCode envoie resource=http://127.0.0.1:8080/mcp : Auth0 ne l’accepte que si une API existe avec cet Identifier.

⚠️ Auth0 ne renomme pas un Identifier Si une API 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.

2a · La permission

API → Permissions → Add : urn:oracle:dbtools:ords:mcpserver:all (« ORDS MCP global access »).

2b · Réglages de l’API

RéglageValeur
Enable RBACOn
Add Permissions in the Access TokenOn
Allow Offline AccessOn (pour offline_access d’OpenCode)

2c · Au sujet de « APIs » sur l’application Native

💡 Normal : pas d’onglet APIs/Permissions sur une app Native Les permissions se définissent sur l’API et s’attachent aux rôles. L’onglet « Machine to Machine Applications » de la page API ne liste que les apps M2M — à ignorer pour OpenCode.

3 · Les rôles + la permission sur chaque rôle (critique)

Menu : User Management → Roles. Créer deux rôles dont les noms correspondent exactement aux mcp.role ORDS :

RôlePool ORDS
POOL.HRmcp-hr
POOL.FINmcp-fin

Pour chacun des deux rôles : onglet Permissions → Add Permissions → API ORDS MCP Local → cocher urn:oracle:dbtools:ords:mcpserver:all → Add.

⚠️ Pourquoi c’est critique
Le jeton aRésultat ORDS / OpenCode
roles2: ["POOL.FIN"] seul403 Forbidden ; OpenCode reste needs_auth après le succès navigateur.
roles2 et permissions: ["…mcpserver:all"]Marche — pools filtrés par rôle.

4 · L’application Native (OpenCode)

Menu : Applications → Applications → Create Application. Nom ex. OpenCode Native, type Native. Ne pas utiliser Machine to Machine (pas de login navigateur).

RéglageValeur
Client ID→ AUTH0_CLIENT_ID
Client SecretNon nécessaire (PKCE) ; ne pas le mettre dans .env
Allowed Callback URLshttp://127.0.0.1:19876/mcp/oauth/callback, http://localhost:19876/mcp/oauth/callback
Allowed Logout URLshttp://127.0.0.1:19876, http://localhost:19876
Allowed Web Origins (si affiché)mêmes hôtes
⚠️ Chemin exact — /mcp/oauth/callback, pas seulement /callback.

Advanced Settings → Grant Types : Authorization Code = On, Refresh Token = On (recommandé), Client Credentials = Off pour Native (normal).

5 · L’Action Trigger : écrire les rôles dans roles2

Auth0 réserve roles : la liste personnalisée doit porter un autre nom. Dans l’UI, c’est Actions → Triggers.

  1. Actions → Library → Build Custom
  2. Trigger : Login / Post Login — nom 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);
};
  1. Deploy
  2. Actions → Triggers → post-login → ajouter l’action au pipeline → Apply
⚠️ Library seule = jamais exécutée. Il faut l’ajouter au trigger.

5b · credentials-exchange (optionnel, M2M / 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.

6 · Les utilisateurs

Menu : User Management → Users.

MéthodeNotes
Compte base de donnéesCréer, définir le mot de passe, assigner les rôles.
Google / socialAprès la 1re connexion, ouvrir l’utilisateur (google-oauth2|…) et assigner les rôles.
Identité de testRôle assigné
FIN uniquementPOOL.FIN seulement
HR uniquementPOOL.HR seulement
Les deux poolsles 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.

7 · Le .env du lab

AUTH0_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

8 · Démarrer la pile

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

Contrôles de santé

# /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églageValeur
feature.mcptrue
mcp.security.jwt.profile.issuerhttps://<domaine>/
mcp.security.jwt.profile.audiencehttp://127.0.0.1:8080/mcp
mcp.security.jwt.profile.jwk.urlhttps://<domaine>/.well-known/jwks.json
mcp.security.jwt.profile.role.claim.name/roles2
pool mcp-hrmcp.role=POOL.HR, mcp.scope=…mcpserver:all
pool mcp-finmcp.role=POOL.FIN, même scope

Modèle mental

Récapitulatif en une image

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

Revoir la séquence OAuth Passer au démarrage complet →