Aller au contenu
English

Configuration du fournisseur

  • Leçon 10
  • 50 min
  • Module 3 : Sessions et intégration à l'application

Ce que vous saurez faire

Faites correspondre les paramètres du fournisseur et de l’API, un par un. Vérifiez cette correspondance avec une commande avant toute connexion. Ensuite, connectez la même API à un vrai fournisseur d’identité et découvrez ce qu’un fournisseur simulé peut cacher.

Avant de commencerCe qu’il faut savoir et avoir sous la mainEnviron 50 minutes

Ce que vous devez savoir

Ce qu’il vous faut sur votre ordinateur

  • Le projet après Cycle de vie d’une session, avec le fournisseur et l’API en marche.
  • Facultatif, pour le vrai fournisseur : Docker avec environ 1 Go de mémoire libre et 750 Mo d’espace disque pour l’image Keycloak. Vous n’avez besoin ni de compte ni d’argent, et vous supprimez tout à la fin.

Testé sur macOS (Apple silicon) avec Keycloak 26.8.0. Les commandes Windows sont écrites, mais pas testées. La description de Microsoft Entra ID vient de la documentation de Microsoft et n’a pas été testée.

Vous commandez un livre en ligne. La boutique donne votre nom et votre adresse au livreur, qui apporte le colis uniquement à cette adresse. Si un chiffre du numéro de porte est erroné, le livreur ne laisse pas le colis chez un voisin sympathique. Le colis retourne au dépôt, et vous attendez.

Les deux côtés doivent avoir les mêmes renseignements. La boutique a noté l’adresse à la création de votre compte; le livreur la lit sur l’étiquette. Personne ne remarque une erreur avant qu’un colis n’arrive pas. Et la règle « uniquement à l’adresse inscrite » assure la sécurité de la livraison.

Le problème : un caractère, et personne ne peut se connecter

Section intitulée « Le problème : un caractère, et personne ne peut se connecter »

La connexion de ce module compte environ dix paramètres répartis entre deux côtés. Le fournisseur conserve une fiche de l’application, et l’API conserve son propre .env. Un seul caractère différent bloque toutes les connexions, et l’erreur apparaît à un endroit différent chaque fois.

L’auteur a changé un paramètre à la fois dans un projet fonctionnel. Voici trois résultats réels :

  • OIDC_REDIRECT_URI indiquait http://localhost:8000/auth/callback au lieu de http://127.0.0.1:8000/auth/callback. Les deux noms désignent le même ordinateur, mais leur texte diffère. Le navigateur s’est arrêté chez le fournisseur, avec un code 400 et {"error":"invalid_request","error_description":"This redirect URI is not registered for the application."} (cette URI de redirection n’est pas inscrite pour l’application). L’API n’a rien reçu.
  • OIDC_CLIENT_SECRET était incorrect. La page de connexion fonctionnait, Sam choisissait son nom, puis le rappel de l’API répondait 400 login_failed (échec de connexion) : « Le fournisseur a refusé le code : invalid_client. »
  • OIDC_AUDIENCE indiquait billing-api. Tout fonctionnait jusqu’à la vérification des jetons par l’API : 400 login_failed, « Les jetons ne sont pas valides : le jeton n’est pas destiné à billing-api. »

Trois causes, trois endroits, et chaque fois, la personne devant le navigateur voit seulement que « ça ne fonctionne pas ».

L’idée : une inscription d’application et son équivalent dans .env

Section intitulée « L’idée : une inscription d’application et son équivalent dans .env »

Un fournisseur connecte les personnes uniquement aux applications qu’il connaît. Sa fiche pour une application est l’inscription d’application (Keycloak dit « client »; Microsoft dit « app registration », ou inscription d’application). L’API conserve les mêmes renseignements dans ses paramètres. Chacun a un rôle :

  • Identifiant du client (help-desk-web) : le nom de l’application dans chaque requête.
  • Type de client et secret : un client confidentiel, comme un serveur d’application, peut conserver un secret. Ce secret prouve qu’une demande de jeton vient de l’application. Un client public, comme ticket-cli sur votre ordinateur, n’a aucun secret et utilise uniquement PKCE.
  • URI de redirection : les seules adresses qui peuvent recevoir des codes. Un fournisseur n’envoie jamais de code à une adresse absente de la liste. De même, le livreur ne laisse jamais un colis à la mauvaise porte.
  • URI de redirection après déconnexion : où le fournisseur peut envoyer le navigateur après sa propre déconnexion.
  • Origines autorisées (CORS) : les autres sites dont le JavaScript peut appeler l’API. La page du service de soutien est sur le site de l’API, donc la liste est vide : ALLOWED_ORIGINS reste non défini.
  • Portées et audience : ce que l’application peut demander et l’API à laquelle les jetons d’accès sont destinés (ticket-api).
  • Émetteur et clés : qui signe les jetons et où se trouvent les clés publiques.
Deux colonnes reliées par six lignes. À gauche, chez le fournisseur, l'inscription de help-desk-web : émetteur http://localhost:8400; identifiant du client help-desk-web; secret du client, dont le fournisseur conserve le SHA-256; URI de redirection http://127.0.0.1:8000/auth/callback; audience des jetons d'accès ticket-api; type de jeton dans l'en-tête at+jwt, ou JWT pour Keycloak. À droite, dans l'API, les paramètres .env OIDC_ISSUER, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET, OIDC_REDIRECT_URI, OIDC_AUDIENCE et ACCESS_TOKEN_TYPES. Chaque ligne indique ce qui arrive quand les deux côtés diffèrent : la découverte échoue et aucune connexion n'est possible; invalid_client, application non inscrite; login_failed, le fournisseur a refusé le code; 400 chez le fournisseur, URI de redirection non inscrite; login_failed, le jeton n'est pas destiné à cette API; login_failed, ce n'est pas un jeton d'accès.
Six paramètres qui doivent correspondre, et ce qui arrive quand l'un d'eux ne correspond pas. Chaque message sur les lignes vient d'une exécution réelle.
Description textuelle du diagramme

Lisez chaque ligne comme une paire. La case de gauche contient un renseignement du fournisseur; celle de droite indique où l’API conserve le même renseignement. L’étiquette montre ce que reçoit la personne devant le navigateur quand les deux diffèrent. Seule l’URI de redirection provoque un échec chez le fournisseur, avant la création d’un code. Les autres échecs surviennent plus tard, au rappel de l’API, quand elle vérifie ce qu’elle a reçu.

Le fournisseur d’exercice conserve ses inscriptions dans .idp/clients.json. Ouvrez ce fichier. L’entrée du service de soutien ressemble à ceci (votre secret_sha256 est différent) :

{
"client_id": "help-desk-web",
"name": "Help desk (web)",
"type": "confidential",
"grant_types": ["authorization_code", "refresh_token"],
"redirect_uris": ["http://127.0.0.1:8000/auth/callback"],
"post_logout_redirect_uris": ["http://127.0.0.1:8000/app/"],
"scopes": ["openid", "profile", "email", "offline_access",
"tickets:read", "tickets:write", "members:manage"],
"secret_sha256": "a595db1059bb4466f0162c6e0d2ede1e0a8d354cbaccd8d420857348c815d0c8"
}

Comparez-la avec votre .env. OIDC_CLIENT_ID=help-desk-web correspond à la première ligne. OIDC_REDIRECT_URI correspond à l’unique entrée de redirect_uris, caractère pour caractère. OIDC_CLIENT_SECRET est le secret dont le SHA-256 figure à la dernière ligne. Comme la table des sessions de l’API, le fournisseur conserve seulement une empreinte. Les portées comprennent offline_access, qui permet le jeton de renouvellement de la dernière leçon.

Larkfield : vérifier les deux côtés avec une commande

Section intitulée « Larkfield : vérifier les deux côtés avec une commande »

Amira ne veut pas découvrir une différence par l’intermédiaire d’une personne qui ne peut pas se connecter. Le projet comprend une vérification qui demande au fournisseur ce qu’une vraie connexion rencontrerait, paramètre par paramètre. Elle ne change rien.

  1. Copiez la vérification, le document d’identité et les fichiers facultatifs du vrai fournisseur depuis l’instantané :

    Fenêtre de terminal
    S=../nextia-learning-labs/C23/snapshots/end-of-m03
    mkdir -p docs optional
    cp "$S/scripts/check_identity.py" scripts/
    cp "$S/docs/identity-config.md" docs/
    cp -R "$S/optional/keycloak" optional/

    Votre projet correspond maintenant à l’instantané end-of-m03, fichier pour fichier.

  2. Avec le fournisseur en marche, lancez la vérification :

    Fenêtre de terminal
    python -m scripts.check_identity
    Sign-in settings:
    OK issuer: .env says http://localhost:8400, the provider says http://localhost:8400
    OK published keys: 1 (1 for RS256)
    OK the provider supports PKCE
    OK application help-desk-web with redirect URI http://127.0.0.1:8000/auth/callback: 200
    OK client secret: the provider answers 'invalid_grant' to a made-up code
    OK SESSION_KEY is set
    OK cookie Secure=False, redirect URI over HTTP
    OK audience: ticket-api
    allowed origins (CORS): none (same site only)
    0 problem(s).

    Si vous avez publié une deuxième clé dans Lire un jeton : JWT, signatures et clés sans retirer l’ancienne, la ligne des clés indique 2 (2 for RS256) (2, dont 2 pour RS256). C’est aussi correct.

    Regardez la ligne du secret du client. La vérification envoie un code inventé avec le vrai secret. invalid_grant (autorisation invalide) signifie « le code est incorrect » : le fournisseur a donc accepté le secret. invalid_client (client invalide) indiquerait un secret incorrect.

  3. Rendez un paramètre incorrect pour une seule exécution. Une variable d’environnement a priorité sur .env, donc le fichier ne change pas :

    Fenêtre de terminal
    OIDC_REDIRECT_URI=http://localhost:8000/auth/callback python -m scripts.check_identity
    PROBLEM application help-desk-web with redirect URI http://localhost:8000/auth/callback: 400
    Register this exact redirect URI for the application at the provider.

    Les autres lignes restent correctes, et la dernière indique 1 problem(s). (1 problème). Essayez aussi OIDC_CLIENT_SECRET=not-the-secret :

    PROBLEM client secret: the provider answers 'invalid_client' to a made-up code
    invalid_client means the secret (OIDC_CLIENT_SECRET) is wrong or missing.
  4. Arrêtez le fournisseur (Ctrl+C dans son terminal) et relancez la vérification. Elle s’arrête à la première ligne, car elle ne peut rien vérifier d’autre :

    Sign-in settings:
    PROBLEM discovery at http://localhost:8400: [Errno 61] Connection refused
    Is the provider running? python -m idp

    Sur Windows et Linux, le numéro et le texte de l’erreur diffèrent. Redémarrez le fournisseur.

  5. Ouvrez docs/identity-config.md. Deux tableaux y présentent tous les paramètres des deux côtés, avec les valeurs d’exercice et leur raison d’être. Une autre équipe peut configurer l’API avec ce document. Remise du travail l’utilise.

Un vrai fournisseur : Keycloak dans Docker (facultatif)

Section intitulée « Un vrai fournisseur : Keycloak dans Docker (facultatif) »

Le fournisseur d’exercice respecte les normes, mais il a été écrit pour ce cours. Un fournisseur simulé s’accorde avec le code développé à ses côtés et peut donc cacher des erreurs. Pour les trouver, exécutez un vrai fournisseur une fois. Keycloak est un fournisseur d’identité libre (licence Apache-2.0) qui fonctionne dans Docker sur votre ordinateur, sans compte. Son mode de développement conserve tout en mémoire et utilise HTTP sans chiffrement. Il ne doit jamais contenir de vraies personnes.

Ce parcours est facultatif; le reste du cours utilise le fournisseur d’exercice. Prévoyez environ 20 minutes.

  1. Démarrez Keycloak. Choisissez votre propre mot de passe d’administration au lieu de choose-one :

    Fenêtre de terminal
    docker run -d --name ticket-keycloak -p 127.0.0.1:8480:8080 \
    -e KC_BOOTSTRAP_ADMIN_USERNAME=admin -e KC_BOOTSTRAP_ADMIN_PASSWORD=choose-one \
    quay.io/keycloak/keycloak:26.8.0 start-dev

    Attendez environ une minute, jusqu’à ce que http://127.0.0.1:8480 réponde dans votre navigateur.

  2. Créez le realm (domaine d’identité), l’inscription et une personne fictive avec le script du projet. Il utilise l’API d’administration de Keycloak :

    Fenêtre de terminal
    KEYCLOAK_ADMIN_PASSWORD=choose-one python optional/keycloak/setup.py
    Keycloak is ready. Put these lines in .env (instead of the practice provider's):
    OIDC_ISSUER=http://127.0.0.1:8480/realms/helpdesk
    OIDC_CLIENT_ID=help-desk-web
    OIDC_CLIENT_SECRET=<a secret made by Keycloak>
    ACCESS_TOKEN_TYPES=JWT
    Sign in as sam with the password <a random password> (made up; shown once).
    Keycloak's user ID for sam (the token's sub): 364a4212-e593-464b-8055-bcd9773134e9
    Give it a membership, for example:
    INSERT INTO users (user_id, name, email) VALUES ('364a4212-e593-464b-8055-bcd9773134e9', 'Sam (Keycloak)', 'sam.keycloak@larkfield.example');
    INSERT INTO memberships (tenant_id, user_id, role) VALUES ('larkfield', '364a4212-e593-464b-8055-bcd9773134e9', 'staff');

    Votre secret, votre mot de passe et votre identifiant d’utilisateur diffèrent. Gardez la sortie ouverte.

  3. Dans .env, placez # devant vos lignes d’exercice OIDC_ISSUER et OIDC_CLIENT_SECRET. Ajoutez les lignes OIDC_ISSUER et OIDC_CLIENT_SECRET affichées. OIDC_CLIENT_ID=help-desk-web est déjà dans .env avec la même valeur, donc ne l’ajoutez pas une deuxième fois. N’ajoutez pas encore ACCESS_TOKEN_TYPES=JWT : la prochaine étape montre pourquoi il est nécessaire. Redémarrez fastapi dev.

  4. Ouvrez http://127.0.0.1:8000/app/ et choisissez Sign in (se connecter). Cette fois, une vraie page de connexion demande un mot de passe :

    Page de connexion de Keycloak pour le realm HELPDESK : titre Sign in to your account (connectez-vous à votre compte), champ Username or email (nom d'utilisateur ou courriel), champ Password (mot de passe) avec un bouton pour afficher le mot de passe, et bouton bleu Sign In (se connecter).
    Le même bouton Sign in (se connecter), maintenant avec un vrai fournisseur : Keycloak demande le mot de passe de la personne fictive. L'API ne le voit jamais. L’image est en anglais.

    Connectez-vous comme sam avec le mot de passe affiché. Vous revenez à l’API avec une erreur :

    {"error":{"code":"login_failed","message":"The tokens are not valid: This is not an access token (an ID token is for the application, not the API).", … }}

    Les jetons d’accès de Keycloak ont typ: JWT dans leur en-tête, plutôt que at+jwt comme ceux du fournisseur d’exercice. La vérification supplémentaire peu coûteuse de l’API, présentée dans Ce que signifie un jeton, les a refusés.

  5. Ajoutez ACCESS_TOKEN_TYPES=JWT à .env, redémarrez fastapi dev, puis reconnectez-vous. Vous arrivez sur /app/. La page affiche l’identifiant Keycloak de Sam, un UUID, avec "memberships": []. Le Sam de Keycloak est une nouvelle personne pour l’API. Le usr-sam d’exercice et lui partagent un nom et une adresse courriel, mais pas un sub.

  6. La connexion a déjà ajouté la ligne de Sam à users. Exécutez donc seulement le deuxième INSERT affiché, celui de l’adhésion, dans psql (docker compose exec db psql -U tickets). Rechargez la page. Maintenant, memberships contient larkfield, Larkfield, staff.

  7. Exécutez python -m scripts.check_identity. Les 8 lignes indiquent OK pour Keycloak, et la dernière indique 0 problem(s). (0 problème). Choisissez ensuite Sign out (se déconnecter). La réponse de l’API indique l’adresse de fin de session de Keycloak, http://127.0.0.1:8480/realms/helpdesk/protocol/openid-connect/logout. La page y envoie le navigateur.

  8. Supprimez tout :

    Fenêtre de terminal
    docker rm -f ticket-keycloak

    Dans .env, retirez les quatre lignes Keycloak et le # devant vos lignes d’exercice. Redémarrez fastapi dev et vérifiez avec python -m scripts.check_identity.

setup.py a aussi créé deux paramètres dans Keycloak dont le fournisseur d’exercice n’a pas besoin. Un mappage d’audience place ticket-api dans le aud des jetons d’accès de Keycloak. Le client reçoit aussi la portée basic de Keycloak, qui ajoute sub. Sans elle, l’API refuse le jeton avec « Le jeton n’a pas de revendication sub ».

Pourquoi ACCESS_TOKEN_TYPES=JWT ne laisse-t-il pas entrer les jetons d’identité? Parce que la vérification de l’audience reste active. L’audience d’un jeton d’identité est l’application, help-desk-web; l’API accepte seulement ticket-api. La vérification du type était un ajout peu coûteux; celle de l’audience est essentielle.

Deux vrais bogues que le fournisseur simulé avait cachés

Section intitulée « Deux vrais bogues que le fournisseur simulé avait cachés »

Quand l’auteur du cours a connecté Keycloak pour la première fois, la connexion a échoué deux fois. Les deux bogues étaient dans l’API, et le fournisseur d’exercice avait accepté les deux. Ils sont corrigés dans votre copie. L’auteur a recréé chacun d’eux volontairement pour montrer les vrais messages.

Premier bogue : un vérificateur PKCE trop court. La première version de l’API créait le vérificateur avec secrets.token_urlsafe(24) : 32 caractères. La norme PKCE (RFC 7636) exige de 43 à 128 caractères. Le fournisseur d’exercice ne vérifiait pas la longueur, donc tous les tests réussissaient. Keycloak la vérifie :

{"error":"invalid_grant","error_description":"PKCE verification failed: Invalid code verifier"}

La personne devant le navigateur voyait seulement 400 login_failed : « Le fournisseur a refusé le code : invalid_grant. » La correction tient en une ligne dans sessions.py : secrets.token_urlsafe(48), soit 64 caractères. Le fournisseur d’exercice vérifie maintenant la longueur aussi, et un test le prouve. Avec 32 caractères, il répond : « Le code_verifier doit avoir de 43 à 128 caractères (A-Z a-z 0-9 - . _ ~). »

Deuxième bogue : une adresse courriel qui devait être unique. Le deuxième bogue est le cas d’échec ci-dessous.

La leçon des deux bogues est la même. Un fournisseur simulé que vous écrivez partage vos hypothèses. Testez une fois avec un vrai fournisseur avant que des personnes dépendent de la connexion, même si vous le supprimez ensuite.

Erreur courante

La première connexion avec un vrai fournisseur échoue avec un code 500

Symptôme : avec Keycloak, Sam entre son mot de passe, et le rappel de l’API répond 500 internal_error (erreur interne), « Une erreur s’est produite sur le serveur. » Le fournisseur d’exercice ne provoquait jamais cette erreur.

Cause : la première version de la table users avait une contrainte UNIQUE sur email. Le Sam de Keycloak a un nouvel identifiant, mais la même adresse que le usr-sam d’exercice, sam@larkfield.example. À la première connexion, l’API ajoute une ligne pour chaque nouvelle personne, et l’insertion a enfreint la contrainte. Le journal de l’API indiquait :

psycopg.errors.UniqueViolation: duplicate key value violates unique constraint "users_email_unique"
DETAIL: Key (email)=(sam@larkfield.example) already exists.

Diagnostic : une erreur 500 a toujours une ligne de journal. Cherchez Error (erreur) dans le terminal de l’API, près de l’heure du rappel. Le nom d’une contrainte dans le message vous indique quelle règle a été enfreinte.

Correction : une adresse courriel est une coordonnée, jamais la clé d’une personne. La clé d’une personne est le sub du fournisseur. Deux fournisseurs ou deux comptes peuvent partager une adresse, et une personne peut la changer. La migration 013 de votre projet contient un index normal sur lower(email), pas un index unique. Le cours l’a modifiée avant qu’un apprenant l’applique. Après l’exécution, la base de données de l’auteur contenait 2 personnes avec sam@larkfield.example, et les deux pouvaient se connecter.

Microsoft Entra ID : les mêmes paramètres, d’autres noms (non testé)

Section intitulée « Microsoft Entra ID : les mêmes paramètres, d’autres noms (non testé) »

Plusieurs entreprises utilisent Microsoft Entra ID, le service d’identité de Microsoft 365 et d’Azure. Ce cours ne l’a pas testé : il exige un compte Azure, et le cours n’en utilise aucun. Le tableau associe les paramètres à partir de la documentation de Microsoft. Vérifiez-les avec un vrai jeton avant de vous y fier.

Ce dont l’API a besoin Fournisseur d’exercice Microsoft Entra ID (selon la documentation)
Identifiant du client help-desk-web l’Application (client) ID (identifiant de l’application cliente) d’une app registration (inscription d’application), un UUID
Secret du client fourni par python -m idp init Certificates & secrets (certificats et secrets) → un nouveau secret du client, affiché une seule fois
URI de redirection http://127.0.0.1:8000/auth/callback Authentication (authentification) → une plateforme de type Web avec cette URI
Audience ticket-api Expose an API (exposer une API) : une Application ID URI (URI d’identifiant d’application), par exemple api://ticket-api, et des portées comme tickets.read
Émetteur http://localhost:8400 https://login.microsoftonline.com/<directory ID>/v2.0, quand le manifeste définit requestedAccessTokenVersion à 2
Type de jeton at+jwt JWT : définissez ACCESS_TOKEN_TYPES=JWT
Portées dans le jeton scope scp (l’API lit les deux)
Identifiant de la personne sub sub diffère pour chaque application; oid est identique dans toutes les applications de l’annuaire. Cette API utilise sub

Deux différences comptent pour cette API. Les noms de portées contenant : peuvent nécessiter d’autres noms, comme tickets.read. Les vérifications de portées de l’API devraient alors utiliser les mêmes noms. De plus, les jetons d’accès version 2 d’Entra ID peuvent contenir l’identifiant du client de l’API dans aud, plutôt que l’Application ID URI. Décodez un vrai jeton avec scripts.token decode et définissez OIDC_AUDIENCE selon ce que vous voyez.

À vous de jouer : une deuxième adresse pour l’API

Section intitulée « À vous de jouer : une deuxième adresse pour l’API »

Lancez une deuxième copie de l’API sur le port 8001, par exemple pour essayer un changement sans arrêter la copie actuelle. N’arrêtez rien. Dans un nouveau terminal, avec l’environnement virtuel actif :

Fenêtre de terminal
OIDC_REDIRECT_URI=http://127.0.0.1:8001/auth/callback POST_LOGOUT_REDIRECT_URI=http://127.0.0.1:8001/app/ fastapi dev --port 8001

Prédisez ce que http://127.0.0.1:8001/app/ → Sign in (se connecter) affiche. Ensuite, faites fonctionner la connexion en modifiant seulement la configuration du fournisseur. Ne modifiez pas l’API.

Afficher la réponse

Le navigateur s’arrête chez le fournisseur avec 400 et « This redirect URI is not registered for the application. » (cet URI de redirection n’est pas enregistré pour l’application). Le fournisseur n’a que le port 8000 dans sa liste. Il n’envoie aucun code à une adresse qui n’y figure pas.

Ajoutez la nouvelle adresse à la liste dans .idp/clients.json, dans l’entrée de help-desk-web :

"redirect_uris": ["http://127.0.0.1:8000/auth/callback", "http://127.0.0.1:8001/auth/callback"],
"post_logout_redirect_uris": ["http://127.0.0.1:8000/app/", "http://127.0.0.1:8001/app/"],

Le fournisseur d’exercice lit le fichier à chaque requête. La prochaine connexion fonctionne donc sans redémarrage. L’auteur a fait la même chose avec un deuxième port. La connexion sur ce port s’est terminée par Set-Cookie: ticket_session=…, comme d’habitude. Supprimez les deux nouvelles entrées quand vous avez terminé : chaque adresse enregistrée est un endroit de plus où envoyer des codes.

Quand utiliser un fournisseur simulé ou réel, et ce que chacun coûte

Section intitulée « Quand utiliser un fournisseur simulé ou réel, et ce que chacun coûte »

Utilisez le fournisseur d’exercice pour le développement et les tests. Il ne nécessite aucun compte et démarre immédiatement. Une seule commande permet de créer des utilisateurs, de les désactiver et de faire la rotation des clés. Son coût est celui que vous avez vu : il partage vos hypothèses.

Utilisez un vrai fournisseur avant que des personnes dépendent de la connexion, puis à nouveau quand vous changez de fournisseur. Keycloak dans Docker est gratuit et local, et vous pouvez le supprimer avec une seule commande. Le fournisseur d’une entreprise (Entra ID ou un autre) demande un compte et un administrateur qui enregistre l’application. Il faut aussi du temps pour chaque changement. En échange, il apporte ce qu’un fournisseur simulé n’a jamais : de vrais mots de passe et un deuxième facteur. Il offre aussi la récupération de compte et un audit des connexions. Ce sont les responsabilités du fournisseur, pas celles de l’API.

Vérifiez votre compréhension

Le fournisseur refuse d'envoyer un code à un URI de redirection non enregistré et affiche plutôt une page d'erreur. Pourquoi une page d'erreur est-elle plus sûre qu'une redirection vers cette adresse?

Afficher la réponse

Le code est l’étape qui précède les jetons. Supposons que le fournisseur redirige vers n’importe quelle adresse dans la requête. Un attaquant pourrait lancer une connexion avec sa propre adresse comme redirect_uri et envoyer le lien à Sam. Après la connexion de Sam, l’attaquant reçoit le code de Sam. La liste des adresses enregistrées limite l’envoi des codes aux adresses que l’application possède. Une page d’erreur n’envoie rien nulle part : la personne voit un problème, et aucun code n’existe.

Vérifiez votre compréhension

Prédisez : avec Keycloak et les paramètres par défaut de l'API, la connexion échoue avec « This is not an access token » (ce n'est pas un jeton d'accès). Vous définissez ACCESS_TOKEN_TYPES=JWT. Quelqu'un peut-il maintenant utiliser le jeton d'identité de Keycloak pour appeler l'API? Expliquez.

Afficher la réponse

Non. La vérification du type n’était qu’un premier filtre peu coûteux. Le jeton d’identité de Keycloak a pour audience help-desk-web, l’application. L’API accepte seulement les jetons dont l’audience est ticket-api. La vérification de l’audience refuse donc le jeton d’identité. Keycloak ajoute ticket-api aux jetons d’accès seulement parce que setup.py a créé une règle de correspondance d’audience. La vérification essentielle porte sur l’audience, pas sur le type.

Vérifiez votre compréhension

Exercice pratique : exécutez python -m scripts.check_identity, puis rendez un paramètre incorrect avec une variable d'environnement. Expliquez ce que la vérification a fait pour chaque ligne OK (correct) concernant le fournisseur. Expliquez aussi ce qu'une personne verrait dans le navigateur avec le paramètre incorrect.

Afficher la réponse

La vérification a lu le document de découverte du fournisseur et comparé son issuer (émetteur) à OIDC_ISSUER. Elle a récupéré les clés à jwks_uri et compté celles de type RS256, puis lu les méthodes PKCE. Elle a ouvert l’adresse d’autorisation avec l’identifiant client et l’URI de redirection (200 signifie que les deux sont connus). Elle a envoyé un code inventé avec le secret client : invalid_grant (autorisation invalide) signifie que le secret a été accepté. Avec un URI de redirection incorrect, la personne voit l’erreur 400 du fournisseur : « This redirect URI is not registered for the application. » Avec un secret incorrect, elle choisit son nom, puis reçoit 400 login_failed (échec de connexion) de l’API. Le message est « The provider refused the code : invalid_client. » (le fournisseur a refusé le code : client invalide).

Récapitulatif

  • L’enregistrement de l’application chez le fournisseur et le fichier .env de l’API décrivent les mêmes faits, de l’émetteur au type de jeton. Un seul caractère différent bloque toutes les connexions.
  • python -m scripts.check_identity vérifie les faits auprès du fournisseur avant toute connexion; docs/identity-config.md les répertorie pour une autre équipe.
  • Un URI de redirection incorrect échoue chez le fournisseur, avant qu’un code existe. Les autres différences échouent au point de retour de l’API avec 400 login_failed.
  • Keycloak 26.8.0 dans Docker a fonctionné avec ACCESS_TOKEN_TYPES=JWT, une règle de correspondance d’audience et sa portée basic (pour sub). Ses utilisateurs ont besoin de leurs propres adhésions, car leur sub est nouveau.
  • Le vrai fournisseur a révélé deux bogues que le fournisseur simulé avait cachés. Le vérificateur PKCE avait 32 caractères, au lieu de 43 à 128. Une adresse courriel unique a causé une erreur 500 à la première connexion. Le courriel est une coordonnée; sub est la clé.
  • Microsoft Entra ID utilise les mêmes paramètres sous d’autres noms. La description présentée ici vient de sa documentation, sans avoir été testée.

Le module 3 est terminé : les personnes se connectent par l’API avec un témoin (cookie). Les sessions se terminent à temps, et les paramètres du fournisseur sont vérifiés. Le module 4 utilise l’identité pour décider ce que chaque personne peut faire, dans quelle organisation, sur chaque route.

Leçon suivante : L'autorisation dans l'API