Skip to main content
Le mécanisme prompt=login peut être contourné en supprimant simplement le paramètre lors de son passage par l’agent utilisateur (navigateur) et ne sert essentiellement qu’à fournir une indication d’expérience utilisateur au fournisseur (OP) lorsque la (RP) veut afficher un lien comme : « Bonjour Josh. Ce n’est pas vous ? Cliquez ici. » Cependant, vous ne devriez pas vous y fier pour valider qu’une authentification récente a bien eu lieu. Pour atténuer ce risque, le client doit valider que la réauthentification a bien eu lieu à l’aide du claim auth_time. Ce claim sera automatiquement inclus dans le lorsque les paramètres prompt=login ou max_age=0 sont fournis dans la requête d’authentification. Vous devez transmettre le paramètre max_age au point de terminaison /authorize de l’API Authorization. Si vous utilisez Auth0.js ou Lock, vous pouvez définir ce paramètre dans les options appropriées de la bibliothèque. La façon d’implémenter la réauthentification dépend de votre cas d’utilisation. Il importe de distinguer une simple réauthentification pour les opérations sensibles de la montée en assurance (c.-à-d. l’) pour les opérations sensibles. Les deux sont des mesures de sécurité valides. La première exige que l’utilisateur final saisisse de nouveau son mot de passe, tandis que la seconde exige aussi qu’il utilise un moyen d’authentification multifacteur préconfiguré.

Limites du paramètre prompt=login

La spécification OIDC définit le paramètre prompt=login, qui peut être utilisé pour déclencher l’interface de réauthentification (généralement un écran de connexion) :
promptFACULTATIF : liste de valeurs de type chaîne ASCII, sensibles à la casse et délimitées par des espaces, qui indique si le serveur d’autorisation demande à l’utilisateur final de se réauthentifier et de donner son consentement. Les valeurs définies sont :loginLe serveur d’autorisation doit demander à l’utilisateur final de se réauthentifier. S’il ne peut pas réauthentifier l’utilisateur final, il doit renvoyer une erreur, généralement login_required.
Cependant, l’utilisation de ce paramètre pour garantir la réauthentification pose un problème : le RP n’a aucun moyen de valider qu’une réauthentification a bien eu lieu. Examinons le trafic pour comprendre pourquoi. Le flux d’une requête d’authentification provenant du RP est le suivant :
À la suite d’une authentification réussie auprès de l’AS, le RP recevra un jeton d’identité :
JSON
Le document d’identité fiable renvoyé par l’AS ne contient aucune claim permettant de valider à quel moment la dernière connexion a eu lieu. Cela devient problématique lorsque la demande d’autorisation initiale prend la forme d’une redirection 302 via le navigateur de l’utilisateur final. Si un acteur malveillant veut contourner l’étape de réauthentification demandée par le RP, il lui suffit de supprimer le paramètre prompt=login, et le RP ne peut pas faire la différence entre les champs contenus dans le jeton d’identité. Voici un diagramme d’un flux implicite simplifié utilisant le paramètre prompt=login :
Réauthentification forcée OIDC : flux implicite
Notez qu’il suffit à l’utilisateur final de supprimer le paramètre prompt=login pour contourner l’étape de réauthentification :
Flux implicite simplifié : suppression de prompt=login
Le ou les jetons renvoyés par le premier flux ci-dessus seront identiques au ou aux jetons renvoyés par le deuxième flux. Le RP n’a aucun moyen défini par la spécification de vérifier qu’une réauthentification a bien eu lieu et, par conséquent, ne peut pas tenir pour acquis qu’un prompt=login a réellement entraîné une réauthentification.

paramètre de requête d’authentification max_age

Contrairement à prompt=login, le paramètre de requête d’authentification max_age fournit un mécanisme permettant aux RP de confirmer avec certitude qu’une réauthentification a eu lieu dans un intervalle donné. La spécification OIDC indique :
max_ageFACULTATIF : ancienneté maximale de l’authentification. Spécifie le délai maximal autorisé, en secondes, depuis la dernière fois que l’utilisateur final a été authentifié activement par l’OP. Si le temps écoulé dépasse cette valeur, l’OP doit tenter de réauthentifier activement l’utilisateur final. (Le paramètre de requête max_age correspond au paramètre de requête OpenID 2.0 PAPE max_auth_age.) Lorsque max_age est utilisé, le jeton d’identité renvoyé doit inclure une claim auth_time.
La dernière phrase de la définition est la plus importante. Lorsque max_age est demandé par le RP, une claim auth_time doit être présente dans le RP. Cela signifie que max_age peut être utilisé de l’une des deux façons suivantes :
  • Pour imposer une ancienneté maximale de session : Si une application exige que les utilisateurs se réauthentifient une fois par jour, cela peut être imposé même dans le contexte d’une session beaucoup plus longue, en attribuant une valeur à max_age. Ces valeurs sont définies en secondes.
  • Pour forcer une réauthentification immédiate : Si une application exige qu’un utilisateur se réauthentifie avant d’obtenir l’accès, fournissez une valeur de 0 pour le paramètre max_age et l’AS forcera une nouvelle connexion.
Cette exigence est décrite comme suit :
Flux max_age de réauthentification OIDC
Notez que le RP reçoit un token contenant l’information nécessaire pour valider si une réauthentification a eu lieu ou non. Le RP peut maintenant consulter la claim auth_time dans le jeton d’identité pour déterminer si le paramètre max_age qu’il a demandé a bien été respecté. De cette façon, le paramètre max_age=0 est à l’abri du même type de manipulation côté client qui pourrait contourner le paramètre prompt=login.
Gardez à l’esprit qu’il revient uniquement au RP de valider qu’il reçoit un jeton d’identité avec un auth_time approprié. Cette validation supplémentaire devra être prise en charge par les auteurs d’applications et les frameworks qui utilisent le paramètre max_age.

Utiliser les claims auth_time

Nous avons établi que la spécification OIDC fournit le paramètre max_age comme moyen de confirmer avec certitude qu’une ré-authentification a eu lieu, alors que prompt=login ne le permet pas. Cela n’offre pas d’options très sécuritaires si vous souhaitez forcer une ré-authentification :
  • prompt=login : inclure seulement le paramètre prompt et ne pas valider que l’AS a réellement ré-authentifié l’utilisateur.
  • prompt=login & max_age=999999 : inclure un max_age arbitraire pour qu’un claim auth_time soit présent. Vous pouvez valider qu’une ré-authentification a eu lieu, mais les paramètres deviennent compliqués.
  • max_age=0 : force effectivement un écran de connexion en utilisant seulement le paramètre max_age. Notez qu’une mise à jour récente de la spécification a clarifié davantage ce paramètre, en indiquant qu’il est effectivement équivalent à prompt=login. Cette option n’est pas viable, puisqu’elle mélange ce qui devrait être un paramètre d’UX avec un paramètre de maintien de session.
Auth0 a plutôt choisi d’envoyer le claim auth_time dans le jeton d’identité en réponse à un paramètre de requête prompt=login. Cela signifie que vous pouvez utiliser prompt=login ET valider qu’une ré-authentification a bien eu lieu.

Exemple de validation de auth_time

Vous devez mettre en place une validation pour vous assurer qu’une réauthentification a bien eu lieu. Vous devez valider qu’une valeur auth_time appropriée a été renvoyée.
L’exemple suivant utilise le module passport-auth0-openidconnect pour montrer comment valider la réauthentification. La première façon (et la plus simple) consiste à ajouter l’option max_age=0 à Auth0OidcStrategy :
JavaScript
Notez qu’aucune étape de validation supplémentaire n’est nécessaire, puisque la stratégie gère déjà la validation du paramètre max_age :
JavaScript
Vous pouvez également utiliser prompt=login dans le même contexte, mais comme la norme n’exige pas qu’un auth_time accompagne la réponse du jeton d’identité, vous devez effectuer la validation manuellement. Le constructeur de la stratégie serait donc :
JavaScript
Contrairement à max_age=0, le client doit valider manuellement le paramètre auth_time. Pour en savoir plus, consultez Utiliser les claims auth_time.
L’exemple ci-dessus représente une preuve de concept simplifiée (l’utilisateur doit s’être authentifié au cours des 10 dernières secondes). Idéalement, si vous voulez valider qu’une réauthentification a bien eu lieu, vous devrez :
  1. Enregistrer l’heure à laquelle la demande d’authentification initiale a été effectuée.
  2. À la réception de la réponse d’authentification, récupérer l’heure à laquelle la demande a été envoyée.
  3. Comparer l’heure de la demande d’authentification initiale avec le claim auth_time pour vous assurer que auth_time correspond à un horodatage ultérieur.
Auth0 ne recommande pas de suivre l’approche utilisée dans cet exemple dans des systèmes de production.

Problèmes connus

Auth0 ne peut garantir qu’un échange a eu lieu qu’avec le en amont. Il se peut que l’utilisateur se soit réellement connecté à un fournisseur d’identité tiers, ou qu’il ait déjà eu une session active et n’ait pas eu à se reconnecter. Quoi qu’il en soit, l’échange entre Auth0 et le fournisseur d’identité en amont mettra à jour auth_time. Auth0 ne prend pas en charge le forçage de la réauthentification auprès du fournisseur d’identité en amont, car tous les fournisseurs ne le permettent pas. Le schéma ci-dessous présente un exemple de flux pour un utilisateur qui choisit de se réauthentifier avec une connexion fédérée :
Schéma montrant que les connexions fédérées ne forcent pas la réauthentification
Cette méthode suppose que vous utilisez des connexions de base de données. Les fournisseurs d’identité externes peuvent ou non prendre en charge le forçage de la réauthentification. L’utilisation de prompt=login ou de prompt=consent permet généralement d’indiquer à un fournisseur d’identité externe (social) de réauthentifier un utilisateur, mais Auth0 ne peut pas l’imposer.
Ne vous fiez pas à la vérification côté client (c.-à-d. dans le navigateur) du jeton d’identité ou de auth_time pour empêcher des opérations sensibles.

Pour en savoir plus