Accueil > OpenID Connect OAuth Server par DnC > Développer > OpenID Connect > OpenID Connect : SSO, management de session etc.

SSO et connexion unique (Single Login Identification, SLI)

  publié le par DnC

Une fonctionnalité attendue de la délégation d’autorisation, que l’on associe naturellement à l’inscription unique (Single Sign On, SSO) est la capacité donnée à l’utilisateur final de ne se connecter qu’une seule fois pour accéder à différentes applications (Single Login Identification, SLI).

De façon standard, les flux d’OpenID Connect ne permettent pas de réaliser directement le SLI, mais des possibilités existent. On est là sur une technique peu divulguée, qui nécessite des développement significatifs du côté des applications.

A côté de ces méthodes standard, OAuthSD offre l’option de gérer le SLI et la ré-authentification silencieuse sans nécessiter de code spécifique du côté des applications clientes. Ceci est traité dans cet article : SLI, SLO et SRA sont dans un bateau : OAuthSD.

SSO et SLI

Le SSO n’implique pas automatiquement le SLI
Lorsque l’on évoque SSO ( Single Sign On ), on englobe souvent deux fonctionnalités :
- le fait de ne s’enregistrer (sign on [1]) qu’une seule fois sur un serveur d’identité et de pouvoir se connecter (entrer son login et son mot de passe) sur différentes applications sans avoir à s’enregistrer sur chacune d’elle. C’est à proprement parler le SSO.
- le fait de se connecter à une application ( sign in ) et de naviguer vers une autre sans avoir à entrer à nouveau son login et son mot de passe. Il s’agit de la Connexion Unique Single Connection, Single Login Identification (SLI).

Si le SSO est dans la nature même de OAuth 2 et OpenID Connect, il n’en est pas de même pour le SLI. En revanche, OpenID Connect offre "tout ce qu’il faut" pour le réaliser. Sans pour autant présenter de flux particulier pour cela : c’est donc une fonctionalité à développer du côté des applications (voir : Draft : Management de session OpenID Connect), ou bien une nouvelle fonctionnalité à ajouter [2] à OpenID Connect. C’est ce qu’offre OAuthSD, voir : SLI, SLO et SRA sont dans un bateau : OAuthSD .

Bien entendu, le SLI n’est possible que dans un ensemble d’applications liées à un même serveur d’identité, c’est à dire dans un espace de réseau et d’applications contrôlé par une même entité (corporate realm).

Applications situées dans le même domaine

Dans le cas d’applications situées dans le même domaine (par exemple dans diférents sous-domaine d’un domaine principal, en plus de ce domaine lui_même), une solution simple est de partager le cookie de session entre les applications. Pour cela, le concepteur des applications enregistrera le cookie pour le domaine principal au lieu de l’enregistrer pour le sous-domaine des applications (paramètres cookie_domain et cookie_path).

Cependant, les scopes sont particuliers aux applications. L’application cible doit donc ré-interroger le serveur d’autorisation pour obtenir un JWT qui lui soit propre.

Applications situées dans des domaines différents

Dans le cas d’applications situées dans des domaines différents le problème est un peu plus complexe.
La transmission d’autorisation est réputée sans solution sûre avec les flux OpenID Connect standard. En effet, l’application cible doit obtenir la certitude de l’origine du jeton reçu, et donc de l’identité de l’utilisateur final, sans pouvoir lui imposer une nouvelle demande d’identification. C’est à cela que sert la demande d’authentification silencieuse.

Demande d’authentification silencieuse (Silent Re-Authentication, SRA)

Le protocole OpenID Connect prend en charge un paramètre prompt = none sur la demande d’authentification qui permet aux applications d’indiquer que le serveur d’autorisations ne doit afficher aucune interaction de l’utilisateur (telle que l’authentification, le consentement). OAuthSD renverra la réponse demandée à l’application ou renverra une erreur si l’utilisateur n’est pas déjà authentifié, ou si un type de consentement ou une invite est requis avant de continuer.

Erreurs et suites

Les valeurs possibles pour ERROR_CODE sont définies par la spécification OpenID Connect :

- login_required : l’utilisateur n’était pas connecté à OAuthSD, l’authentification silencieuse n’est pas possible.
L’utilisateur doit être redirigé vers la page de connexion OAuthSD (sans le paramètre prompt =’none’) afin de s’authentifier.

- consent_required : l’utilisateur était connecté à OAuthSD, mais l’application attend son consentement (pour lequel il faudrait afficher une invite, ce qui est exclu par prompt=’none’).
D’après la norme, l’utilisateur devrait être redirigé vers la page de connexion OAuthSD comme précédemment.

Nous avons deux solutions :

  • éviter cette situation en considérant que les applications sont des applications de confiance, connues des utilisateurs, ou que les utilisateurs sont suffisamment avertis par ailleurs (par exemple en leur présentant les conditions générales du site à approuver lors de leur première connexion directe à l’application). Il suffira de modifier le flux pour ne pas retourner une erreur.
  • modifier le flux pour présenter la demande de consentement plutôt que retourner une erreur.

- interaction_required : l’utilisateur était connecté à OAuthSD et a autorisé l’application, mais doit être redirigé ailleurs avant que l’authentification ne puisse être complétée, par exemple, lors de l’utilisation d’une règle de redirection.
D’après la norme, l’utilisateur devrait être redirigé vers la page de connexion OAuthSD.
Cette situation ne devrait pas se produire dans un espace contrôlé par une même organisation (corporate realm), dans lequel le SLI devrait être mis en œuvre pour toutes les applications clientes. On se ramène donc aux deux cas précédents.

Ces erreurs sont retournées à l’application cliente qui a tenté la connexion. Ceci implique des développements significatif des applications clientes. Heureusement, il y a OAuthSD !

Remarque de sécurité :
Interroger Authorize avec prompt = ’none’ sans id_token_hint ne peut être considéré comme une authentification de l’utilisateur, mais comme une simple information sur sa connexion.
Voir : id_token_hint : ré-authentification silencieuse du sujet avec l’ID Token.

OAuthSD va plus loin ...

A côté de ces méthodes standard qui nécessitent d’adapter l’application, OAuthSD offre l’option de gérer le SLI et la ré-authentification silencieuse sans nécessiter de code spécifique du côté des applications clientes : c’est le controleur authorize qui se charge de tout le travail.

SLI, SLO et SRA sont dans un bateau : OAuthSD

  publié le par DnC

Dans le cadre des flux OpenID Connect, outre la fonction d’inscription unique (Single Sign On, SSO) le serveur OAuthSD implémente la fonction d’identification unique (Single Login Identification, SLI), la déconnexion unique (Single Login Out, SLO) ainsi que la ré-authentification silencieuse (Silent Re-Authentication, SRA) [1]. Ceci sans modification du code d’interface des applications avec OIDC, tout étant pris en compte au niveau du contrôleur authorize.

Le code du serveur OAuthSD peut être paramétré pour implémenter (ou non) le SLI. La déconnexion unique (SLO) et la ré-authentification silencieuse (SRA) découlent du SLI.

Identification unique (Single Login Identification, SLI)

Lorsque le SLI est autorisé (ENABLE_SLI = true) ET que l’application cliente a été enregistrée sur le serveur avec le scope ’sli’, le contrôleur authorize traite une demande d’autorisation sans paramètre prompt comme ceci :

- La présence d’un cookie SLI enregistré sur l’agent dans le domaine du serveur (et non dans celui de l’application) est testée,
- si le cookie SLI est présent, non périmé et validé, il est utilisé pour l’authentification (le formulaire de login n’est pas présenté à l’utilisateur), le contrôleur retournant les jetons au client (le code d’autorisation dans le cas du flux d’autorisation avec code). A ce moment, la validité du cookie SLI est rafraichie à sa valeur initiale.
- En cas d’échec : soit il y a retour standard au client avec une erreur, soit le contrôleur présente le formulaire de login si la constante REAUTHENTICATE_NO_ROUNDTRIP est true. Dans un cas comme dans l’autre, le cookie SLI est d’abord détruit.

- Si le cookie SLI est absent :

  • si l’application cliente ne présente pas le scope ’sli’, le contrôleur agit de façon standard.
  • si l’application cliente autorise le SLI, et si l’authentification mentionnée précédemment réussit, un cookie SLI est enregistré sur l’agent dans le domaine du serveur. C’est le seul cas où le cookie SLI est créé.

Notes :
- Toute erreur "technique" (autre que les erreurs non-répétées de l’utilisateur dans l’entrée de son login ou de son mot de passe) conduit à la destruction du cookie SLI afin de protéger l’application contre les tentatives d’intrusion. Dans un tel cas le serveur ne répond pas pour ne pas orienter l’attaquant.
- Une tentative de connexion sous un identifiant d’utilisateur différent provoque également la destruction du cookie SLI, et conduit à la présentation du formulaire d’identification.
- Une tentative de se connecter depuis un poste de travail différent a le même effet.
- La fonctionnalité SLI est active dans les trois flux OpenID Connect, donc avec le plus sécurisé : Autorisation avec code. Ceci est un avantage par rapport à des solutions souvent présentées utilisant le flux Implicite.
- Le comportement des demandes avec prompt = ’none’, ’login’ et ’consent’ reste conforme au standard.
- Lorsque l’utilisateur change d’application et bénéficie du SLI, de nouvelles demandes de consentement pourraient lui être présentées.

Déconnexion unique (Single Login Out, SLO)

Le serveur expose un nouveau point de terminaison logout, permettant de déconnecter l’utilisateur de toutes les applications clientes.

La demande de déconnexion unique se fait comme une demande d’introspection en fournissant le jeton d’identité.

Lorsque la déconnexion est effectuée, tous les jetons d’accès enregistrés sur le serveur pour le sujet considéré sont effacés. De ce fait, les cookies SLI éventuellement présents sur les agents de l’utilisateur seront détruits si une tentative de connexion est effectuée.

Notez que les application clientes doivent gérer la déconnexion à leur niveau, il n’y a pas de miracle !

Si on souhaite connaître l’état actuel de connexion d’une application, il suffit de répéter la demande d’authentification avec prompt = ’none’. Si la déconnexion a eu lieu, le serveur répondra avec ’login_required’. Cette solution a l’avantage de répondre totalement à la norme OpenID Connect dans son état définitif.

Ré-authentification silencieuse (Silent Re-Authentication, SRA)

Dans le cadre du serveur OAuthSD, cette fonctionnalité résulte directement du SLI [2].

S’agissant des applications n’autorisant pas le SLI, elles ne bénéficient pas du SRA. Mais il est probable que les raisons qui ont conduit à ne pas autoriser le SLI conduiraient à ne pas autoriser le SRA.

Notons :
- qu’il est toujours possible de Rafraîchir (actualiser) un jeton d’accès,
- l’existence de la méthode id_token_hint : ré-authentification silencieuse du sujet avec l’ID Token.

Le Cookie SLI

Le cookie SLI est un concept propre à DnC permettant au serveur OAuthSD d’offrir toutes les fonctionnalités de la connexion unique (SLI, SLO, SRA, SLO) sans modification des applications clientes pourvu qu’elles soient déjà interfacées avec OpenID Connect. Compte-tenu de la complexité et de la diversité des solutions proposées par ailleurs, qui n’ont pas fait à ce jour (début 2019) de spécification définitive, c’est une contribution majeure d’OAuthSD à OpenID Connect.

Le cookie SLI est créé par le contrôleur Authorize si le SLI est autorisé pour l’application cliente (scope ’sli’).

Le cookie SLI est doublement sécurisé :
- il est chiffré par un algorithme symétrique fort dont la clé est connue du seul serveur ;
- il est enregistré dans le domaine du serveur, il n’est donc pas accessible côté client.

Il existe des informations complémentaires, connectez vous pour les voir.

Notes

[1Ces fonctionnalités sont souvent comprises comme faisant partie du SSO, mais en toute rigueur le SSO ne signifie que "inscription unique".

[2D’autres implémentations font appel à de nombreux développements du côté des applications (voir Draft : Management de session OpenID Connect). La solution offerte par OAuthSD a l’énorme avantage de ne pas nécessiter de modification à l’application cliente. Cependant, la solution n’est pas standard. Le management de session OpenID Connect faisant l’objet d’une proposition de standard, OAuthSD offre également cette méthode.

SRA et durée de la session de l’utilisateur

  publié le par DnC

La ’durée de la session locale’ est définie par le concepteur de l’application cliente. Dans le cadre d’OAuthSD, avec les flux entrant sur le contrôleur OpenID Authorize, comment définir en rapport les durées de vie des jetons et du cookie SLI ?

Durée de la session locale

La ’durée de la session locale’ est définie par l’application. Généralement, après authentification, une application définit une durée de validité de la connexion qui en résulte, ou "durée de la session". Lorsque celle-ci a dépassé sa fin de vie ("la session a expiré"), une nouvelle authentification est demandée à l’utilisateur s’il tente une nouvelle action nécessitant l’authentification.

L’utilisateur perçoit la durée de la session locale en ayant la mauvaise surprise de se voir présenter une nouvelle demande de connexion. L’objet de la ré-authentification silencieuse est de prolonger la session de façon transparente.

Ré-authentification silencieuse (Silent Re-Authentication, SRA)

Avec OAuthSD, si l’application a autorisé le SLI, et si la demande d’authentification est effectuée sans définir le paramètre prompt, il y a ré-authentification silencieuse (SRA) : plutôt que présenter un dialogue d’authentification, le contrôleur OpenID Authorize :
- vérifie l’existence d’un cookie SLI valide,
- vérifie que le jeton d’accès associé à l’authentification n’a pas expiré
pour répondre immédiatement avec un nouveau code d’accès [1].

L’application cliente poursuit normalement avec une demande de jetons.

Les effets de bord sont les suivants :
- le serveur rafraîchit le cookie SLI dont la durée de vie est étendue de SLI_COOKIE_LIFETIME [2] secondes,
- le jeton d’accès (Access Token) est renouvelé avec une durée de vie étendue de ACCESS_TOKEN_LIFETIME secondes,
- le jeton d’identité (ID Token) est renouvelé avec une durée de vie étendue de ID_TOKEN_LIFETIME secondes.

Rapport entre les durées de vie

Le contrôleur Authorize considère que l’utilisateur final est connecté si le jeton d’accès n’a pas expiré. On en déduit une première règle :
- Le SRA ne peut fonctionner que si ACCESS_TOKEN_LIFETIME > ’durée de la session locale’.

Si l’utilisateur n’effectue plus d’action nécessitant l’authentification (sur une des applications authentifiées avec SLI), le cookie SLI expirera SLI_COOKIE_LIFETIME après le dernier SRA, ce qui est un terme mal déterminé pour l’utilisateur [3]. Ceci incite à :
- fixer une durée de vie du cookie SLI assez importante, typiquement plusieurs fois la ’durée de la session locale’.

Pour ce qui est de la durée de vie du jeton d’identité (ID Token), qui joue le même rôle que le jeton d’accès, il parait adéquat de :
- fixer la durée de vie ID_TOKEN_LIFETIME égale à celle du jeton d’accès.

Valeurs par défaut de la configuration d’OAuthSD

Le valeurs suivantes sont adoptées par défaut dans le fichier configure.php :
ACCESS_TOKEN_LIFETIME : 7200s
ID_TOKEN_LIFETIME : 7200s
SLI_COOKIE_LIFETIME : 7200s x 3
Une ’durée de la session locale’ de 3600s serait cohérente avec ces valeurs.

Notons que le SRA sondera le contrôleur Authorize au rythme de ’durée de la session locale’ tant que l’utilisateur effectue des actions nécessitant l’authentification.

Considérations relatives à la sécurité

Si l’utilisateur n’effectue plus d’action nécessitant l’authentification sur une application donnée, celle-ci se déconnectera localement après ’durée de la session locale’. En effet, le SRA ne fonctionne que sur une demande d’authentification, elle-même déclenchée par une action de l’utilisateur après la fin de vie de la session locale. Pour autant, le serveur considérera l’utilisateur comme connecté tant que le jeton d’accès n’a pas expiré, et effectuera une reconnexion silencieuse à la première action nécessitant l’authentification. Dans ce cas, il est important de se rendre compte que la déconnexion locale n’est qu’apparente.

On pourrait donc considérer que le SRA ne va pas dans le sens de la sécurité. En effet, la limitation de durée des sessions a pour objet de déconnecter automatiquement les applications ouvertes sur un un poste de travail abandonné par son utilisateur.
Cependant, même les durées de session les plus courte sont rarement inférieures à 15mn. Ce temps est largement suffisant pour permettre une compromission.
Ce n’est donc pas tant le SRA qui est en cause, mais le fait d’abandonner sans surveillance une session ouverte.

La seule bonne parade est de déconnecter systématiquement toutes les applications avant de quitter son poste de travail, c’est le but de la Déconnexion unique (Single LogOut, SLO).

Que faire si la durée de session locale est mal connue ou non maîtrisée ?

Il arrive que la durée de la session locale soit variable ou extensible. C’est par exemple le cas lorsque le dialogue de connexion à l’application comporte une option du genre "Conserver ma connexion" ou "Se souvenir de moi" etc.
Dans ce cas, la ’durée de la session locale’ peut être beaucoup plus grande que la durée de vie fixée pour les jetons et le mécanisme SRA ne fonctionne pas, ou n’a pas vraiment d’objet.

Si malgré tout on veut absolument faire fonctionner le SRA, y compris sur de très longues ’durée de la session locale’, il est toujours possible de programmer l’application cliente pour qu’elle rafraîchisse systématiquement le jeton d’accès. Cela doit être fait avant qu’il n’arrive à péremption, sinon la déconnexion unique ne fonctionnera pas pour cette application.

Du point de vue de la sécurité, notons que le mécanisme SLI + SRA fera en sorte que toutes les applications pour lesquelles le SLI est autorisé, et auxquelles l’utilisateur sera connecté à partir du même poste de travail, hériteront de cette extension de la durée de vie apparente de leur session.

Notes

[1Dans le cas contraire, le dialogue d’identification est présenté, ce qui différencie le SRA de l’appel à Authenticate avec prompt = ’none’.

[2Voir le fichier /oidc/includes/configure.php.

[3Sauf à ce que l’application cliente vérifie fréquemment la connexion (monitoring) et signale l’approche de l’échéance.

Monitoring de l’état de l’authentification et SLO

  (publié initialement le vendredi 5 avril 2019) par DnC

Le monitoring de l’état de l’authentification a pour but de synchroniser la connexion locale d’une application avec le jeton d’accès correspondant.

OAuthSD, suivant en cela OAuth 2.0, considère que l’utilisateur final est connecté à une application tant que le jeton d’accès associé est valide.

Il n’y a pas "naturellement" de relation directe entre ce jeton et l’état de connexion local d’une application. Chaque application devra donc mettre en place un monitoring, côté client, dans le but de :
- surveiller la validité du jeton d’accès,
- compléter le mécanisme de déconnexion unique (Single Logout, SLO) en provoquant la déconnexion locale de l’application lorsque le jeton d’accès a expiré.

OAuthSD propose deux solutions pour le Monitoring :

1. La communauté se voit proposer une "norme" de gestion de la session OpenID Connect (voir : Draft : Management de session OpenID Connect) qui s’appuie sur des iframes du côté du serveur et du côté de l’application.

Il ne s’agit là que d’un procédé de communication entre une application et le serveur OIDC, communication qui peut être réalisée par d’autres moyens, et notamment par interrogation régulière du contrôleur Authorize avec prompt = ’none’. Nous pensons (peut-être à tort ?) qu’une "norme" concernant une couche ISO de niveau protocole ne doit pas introduire à ce niveau un procédé de communication particulier.

2. Une implémentation côté client s’appuyant sur la norme OpenID Connect telle qu’elle existe actuellement est bien préférable à une nouvelle complication du serveur OIDC.
Notre solution présentée ici : Implémentation du monitoring avec Javascript : exemples pour SPIP et WordPress interroge le contrôleur Authorise avec le paramètre display=’none’.

Avertir de la fin de session OIDC et la prolonger

  publié le par DnC

Le monitoring de l’état de l’authentification permet à l’utilisateur final de visualiser l’état de la session OIDC. Voici comment avertir l’utilisateur de l’imminence de la fin de session et lui permettre de la prolonger.

Le monitoring sera complet si l’utilisateur est averti de la fin imminente de la session OIDC et si il lui est proposé de la prolonger.

Lorsque l’on interroge Authorize avec prompt = ’none’, une particularité d’OAuthSD est de retourner dans le corps de la réponse le temps restant avant la fin de session (variable ’timeleft’, en secondes).

Toujours dans l’exemple du plugin OIDC Client pour SPIP, l’avertissement est implémenté en modifiant le code de l’objet ’200’ pour proposer à l’utilisateur de prolonger la session :

jQuery

  1. 200 : function ( data, textStatus, jqXHR){
  2.     // Signaler la connexion
  3.     $('#oidc').css('background-color', '#8f8');
  4.     $('#oidc').text(' OIDC ');
  5.     var timeleft = data['timeleft'];
  6.     if ( timeleft < 600 ) {  //[dnc28d]
  7.         // La fin de session approche
  8.         var retVal = confirm( "<?php echo _T('oidcclient:session_expire'); ?>" );
  9.         if( retVal == true ) {
  10.             // étendre la session
  11.             $.ajax({
  12.                 type : "get",
  13.                 url : "<?php echo OIDC_AUTHORIZATION_ENDPOINT; ?>",
  14.                 data : { 'response_type' : 'code',
  15.                     'client_id' : "<?php echo OIDC_CLIENT_ID; ?>",
  16.                     'user_id' : login,
  17.                     'state' :  state,
  18.                     'scope' : 'openid sli',
  19.                 }
  20.             });
  21.         }
  22.     }
  23. },

Télécharger

Les jetons sont rafraîchis en utilisant notre mécanisme de SLI, et non un jeton de rafraîchissement. Voyez ici pourquoi : Rafraîchir (actualiser) un jeton d’accès : § "Bonnes pratiques pour la sécurité".

Draft : Management de session OpenID Connect

  publié le par DnC

OpenID Connect Session Management.
Cette proposition [1] de spécification répond au besoin de connaître l’état réel de connexion de l’utilisateur et de gérer la déconnexion unique.
Dans l’état actuel, ce document présente de multiples défauts. DnC suit l’évolution de cette spécification et OAuthSD devra intégrer les spécifications qui seront finalement approuvées.

Sans attendre, le serveur OAuthSD répond aux fonctionnalités évoquées avec la technique de la connexion unique et de la déconnexion unique qui en découle ainsi que le monitoring.

Traduction du document OpenID Connect Session Management 1.0 - draft 28

...

Cette spécification définit le terme suivant :

Session

Période continue pendant laquelle un utilisateur final accède à un RP (Relying Party) en s’appuyant sur l’authentification de l’utilisateur final effectuée par le fournisseur OpenID.

2. Découverte du point de terminaison

Pour prendre en charge la gestion de session OpenID Connect, le RP doit obtenir les URL de point de terminaison associées à la gestion de session. Ces URL sont normalement obtenues via la réponse à la découverte de l’OP, comme décrit dans OpenID Connect Discovery 1.0 [OpenID.Discovery], ou PEUVENT être apprises via d’autres mécanismes.

2.1. Métadonnées de découverte du fournisseur OpenID

Ces paramètres de métadonnées de fournisseur OpenID DOIVENT être inclus dans les réponses de découverte du serveur lorsque la gestion de session et la découverte sont prises en charge :

check_session_iframe

CHAMPS OBLIGATOIRES. URL d’un iframe de l’OP qui prend en charge les communications entre origines croisées pour les informations d’état de session avec le client RP, à l’aide de l’API postMessage de HTML5. La page est chargée à partir d’un iframe invisible incorporé dans une page du RP afin de pouvoir s’exécuter dans le contexte de sécurité de l’OP. Il accepte les requêtes postMessage en provenance de l’iframe du RP concerné et utilise postMessage pour renvoyer le statut de connexion de l’utilisateur final vers l’OP.

end_session_endpoint

CHAMPS OBLIGATOIRES. URL de l’OP sur laquelle un RP peut effectuer une redirection pour demander à l’utilisateur final d’être déconnecté de l’OP.

3. Création et mise à jour de sessions

Dans OpenID Connect, la session du RP commence généralement lorsque le RP valide le jeton d’identification de l’utilisateur final. Reportez-vous à la spécification OpenID Connect Core 1.0 [OpenID.Core] pour savoir comment obtenir un jeton ID et le valider. Lorsque l’OP prend en charge la gestion de session, il DOIT également renvoyer l’état de session en tant que paramètre supplémentaire session_state dans la réponse d’authentification. La réponse d’authentification OpenID Connect est spécifiée dans la Section 3.1.2.5 d’OpenID Connect Core 1.0.

Ce paramètre est :

session_state
Etat de session. Chaîne JSON [RFC7159] représentant l’état de connexion de l’utilisateur final sur l’OP. Il NE DOIT PAS contenir le caractère espace (""). Cette valeur est opaque pour le RP. Ceci est OBLIGATOIRE si la gestion de session est prise en charge.

La valeur de l’état de session est initialement calculée sur le serveur. La même valeur d’état de session est également recalculée par l’iframe dans le navigateur du client. La génération de valeurs appropriées pour l’état de session est spécifiée dans la section 4.2 et est basée sur un hachage cryptographique salé de l’ID client, de l’URL d’origine et de l’état du navigateur OP. Pour l’URL d’origine, le serveur peut utiliser l’URL d’origine de la réponse d’authentification, conformément à l’algorithme spécifié à la section 4 de la RFC 6454 [RFC6454].

4. Notification de changement d’état de session

Un jeton d’identité vient généralement avec une date d’expiration. Le RP PEUT s’y fier pour expirer la session du RP. Cependant, il est tout à fait possible que l’utilisateur final se soit déconnecté de l’OP avant la date d’expiration. Par conséquent, il est hautement souhaitable de pouvoir connaître le statut de connexion de l’utilisateur final à l’OP.

Pour ce faire, il est possible de répéter la demande d’authentification avec prompt = none [2]. Cependant, cela entraîne un trafic réseau, ce qui est problématique sur les appareils mobiles qui deviennent de plus en plus populaires. Par conséquent, une fois que la session est établie avec la demande d’authentification et la réponse, il est souhaitable de pouvoir vérifier l’état de la connexion sur l’opérateur sans générer de trafic sur le réseau en interrogeant un OP iframe caché à partir d’un RP iframe avec un postMessage à origine restreinte, comme suit.

4.1. RP iframe

Le RP charge un iframe invisible à partir de lui-même. Cette iframe DOIT connaître l’ID de l’OP iframe, comme décrit à la section 4.2, afin de pouvoir envoyer un message à l’OP iframe. L’iframe RP interroge l’OP iframe avec postMessage à un intervalle approprié [3] pour l’application RP. Avec chaque postMessage, il envoie l’état de session défini dans la section 4.2.

Le postMessage de l’iframe RP fournit la concaténation suivante en tant que données :

ID client + " " + état de session

Il doit également pouvoir recevoir le postMessage de l’OP iframe. Les données reçues seront soit ’changed’, soit ’unchanged’, sauf si la syntaxe du message envoyé est déterminée par le terminal opérateur comme étant malformée, auquel cas les données reçues seront erronées. Dès réception de la modification, le RP DOIT effectuer une nouvelle authentification avec prompt = none pour obtenir l’état de la session en cours sur le terminal opérateur. À la réception d’une erreur, le RP NE DOIT PAS effectuer de nouvelle authentification avec prompt = none, afin de ne pas causer de boucles infinies potentielles générant un trafic réseau vers le terminal opérateur.

Voici un exemple de pseudo-code non normatif pour l’iframe RP :

Javascript

  1. var stat = "unchanged";
  2.   var mes = client_id + " " + session_state;
  3.  
  4.   function check_session()
  5.   {
  6.     var targetOrigin = "https://server.example.com";
  7.     var win = window.parent.document.getElementById("op").
  8.                 contentWindow;
  9.     win.postMessage( mes, targetOrigin);
  10.   }
  11.  
  12.   function setTimer()
  13.   {
  14.     check_session();
  15.     timerID = setInterval("check_session()",3*1000);
  16.   }
  17.  
  18.   window.addEventListener("message", receiveMessage, false);
  19.  
  20.   function receiveMessage(e)
  21.   {
  22.     var targetOrigin = "https://server.example.com";
  23.     if (e.origin !== targetOrigin ) {return;}
  24.     stat = e.data;
  25.  
  26.     if stat == "changed" then take the actions below...
  27.   }

Télécharger

4.2. OP iframe

Le RP charge également un OP iframe invisible à partir de check_session_iframe de l’OP. Le RP DOIT assigner un attribut id à l’iframe afin qu’il puisse l’adresser, comme décrit ci-dessus. L’OP iframe DOIT imposer que l’appelant ait la même origine que son cadre parent. Il DOIT rejeter les demandes postMessage de toute autre source.

Comme spécifié dans la section 4.1, le postMessage de l’iframe RP fournit la concaténation suivante en tant que données :

ID client + " " + session_state

L’op iframe a accès à l’état du navigateur dans l’OP (dans un cookie ou dans un stockage HTML5) qu’il utilise pour calculer et comparer l’état de session OP passé par le RP. L’OP iframe DOIT le recalculer à partir de l’ID client précédemment obtenu, de l’URL d’origine de la source (à partir de postMessage) et de l’état actuel du navigateur OP. L’état de session inclut toutes ces informations pour des raisons de confidentialité, de sorte que différents clients actifs dans le même navigateur ont des valeurs d’état de session distinctes.

Si le postMessage reçu est syntaxiquement incorrect, de sorte que l’ID client publié et l’URL d’origine ne peuvent pas être déterminés ou sont incorrects sur le plan syntaxique, l’OP iframe DOIT poster (postMessage) à la source la chaîne d’erreur. Si la valeur reçue et la valeur calculée ne correspondent pas, l’OP iframe DOIT poster à la source la chaîne modifiée. S’il y a correspondance, alors il DOIT poster la chaîne inchangée.

Voici un exemple non normatif de pseudo-code pour l’OP iframe :

Javascript

  1. window.addEventListener("message", receiveMessage, false);
  2.  
  3.   function receiveMessage(e){ // e.data has client_id and session_state
  4.  
  5.     // Validate message origin
  6.     var client_id = e.data.split(' ')[0];
  7.     var session_state = e.data.split(' ')[1];
  8.     var salt = session_state.split('.')[1];
  9.  
  10.     // if message syntactically invalid
  11.     //     postMessage('error', e.origin) and return
  12.  
  13.     // get_op_browser_state() is an OP defined function
  14.     // that returns the browser's login status at the OP.
  15.     // How it is done is entirely up to the OP.
  16.     var opbs = get_op_browser_state();
  17.  
  18.     // Here, the session_state is calculated in this particular way,
  19.     // but it is entirely up to the OP how to do it under the
  20.     // requirements defined in this specification.
  21.     var ss = CryptoJS.SHA256(client_id + ' ' + e.origin + ' ' +
  22.       opbs + ' ' + salt) + "." + salt;
  23.  
  24.     var stat = '';
  25.     if (session_state == ss) {
  26.       stat = 'unchanged';
  27.     } else {
  28.       stat = 'changed';
  29.     }
  30.  
  31.     e.source.postMessage(stat, e.origin);
  32.   };

Télécharger

L’état du navigateur OP sera généralement stocké dans un cookie ou dans un stockage local HTML5. Il a pour origine le serveur d’autorisations. Il capture des événements significatifs tels que les connexions, les déconnexions, le changement d’utilisateur, le changement de statut d’authentification pour les clients utilisés par l’utilisateur final, etc. Ainsi, l’OP DEVRAIT mettre à jour la valeur de l’état du navigateur en réponse à de tels événements significatifs. En conséquence, le prochain appel à check_session () après un tel événement renverra la valeur modifiée. Il est RECOMMANDÉ que le terminal opérateur ne mette pas à jour l’état du navigateur trop fréquemment en l’absence d’événements significatifs, afin d’éviter un trafic réseau excessif sur le client en réponse à des événements erronés.

Le calcul de l’état de session renvoyé en réponse à des demandes d’authentification infructueuses DEVRAIT, en plus de l’état du navigateur, incorporer suffisamment d’aléa sous la forme d’un sel afin d’empêcher l’identification d’un utilisateur final lors d’appels successifs au point de terminaison d’autorisation de l’OP.

Dans le cas d’un client autorisé (réponse d’authentification réussie), l’OP DEVRAIT changer la valeur de l’état de session renvoyé au client à l’occurrence de l’un des événements suivants :

- L’ensemble des utilisateurs authentifiés auprès du navigateur change (connexion, déconnexion, ajout de session).

- Le statut d’authentification des clients utilisés par l’utilisateur final change.

De plus, l’état du navigateur utilisé pour vérifier l’état de la session DEVRAIT changer avec de tels événements. Les appels à check_session() renverront les modifications apportées par rapport aux versions antérieures de l’état de session après de tels événements. Il est RECOMMANDÉ que l’état du navigateur NE DEVRAIT PAS varier trop souvent en l’absence de tels événements afin de minimiser le trafic sur le réseau causé par la réponse du client aux notifications modifiées.

Dans le cas d’une demande d’authentification infructueuse, la valeur de l’état de session renvoyée DEVRAIT varier avec chaque demande. Cependant, l’état de la session du navigateur n’a pas besoin de changer sauf si un événement significatif se produit. En particulier, de nombreuses valeurs d’état de session peuvent être simultanément valides, par exemple en introduisant un sel aléatoire dans les états de session émis en réponse à des demandes d’authentification infructueuses.

Si un cookie est utilisé pour conserver l’état du navigateur OP, l’indicateur HttpOnly ne peut probablement pas être défini pour ce cookie car il doit être accessible à partir de JavaScript. Par conséquent, les informations pouvant être utilisées pour identifier l’utilisateur ne doivent pas être placées dans le cookie, car elles pourraient être lues par du JavaScript non associé.

Dans certaines implémentations, les notifications modifiées ne se produiront que lorsque la session de l’utilisateur final sera modifiée, alors que dans d’autres implémentations, elles pourront également survenir à la suite de modifications apportées à d’autres sessions entre l’agent d’utilisateur et le terminal opérateur. Les PR doivent être préparés à toute éventualité, en gérant en silence tous les faux positifs susceptibles de se produire.

5. Déconnexion initiée par le RP

Un RP peut notifier à l’OP que l’utilisateur final s’est déconnecté du site et peut également vouloir se déconnecter de l’OP. Dans ce cas, le RP, après avoir déconnecté l’utilisateur final du RP, redirige l’agent utilisateur de l’utilisateur final vers l’URL du point de terminaison de déconnexion de l’OP. Cette URL est normalement obtenue via l’élément end_session_endpoint de la réponse à la découverte de l’OP ou peut être apprise via d’autres mécanismes.

Cette spécification définit également les paramètres suivants qui sont transmis en tant que paramètres de requête dans la demande de déconnexion :

id_token_hint

CONSEILLÉ [4]. Le jeton d’ID précédemment émis a été transmis au point de terminaison de la déconnexion en tant qu’indication de la session authentifiée actuelle de l’utilisateur final avec le client. Ceci est utilisé comme indication de l’identité de l’utilisateur final que le RP demande à être déconnecté par l’OP. L’OP n’a pas besoin d’être répertorié en tant que public du jeton ID lorsqu’il est utilisé en tant que valeur id_token_hint.

post_logout_redirect_uri

OPTIONNEL. URL à laquelle le RP demande que l’agent utilisateur de l’utilisateur final soit redirigé après la déconnexion. La valeur DOIT avoir déjà été enregistrée auprès de l’OP, soit à l’aide du paramètre d’enregistrement post_logout_redirect_uris, soit via un autre mécanisme. S’il est fourni, l’OP DEVRAIT honorer cette demande après la déconnexion.

state

OPTIONNEL. Valeur opaque utilisée par le RP pour maintenir l’état entre la demande de déconnexion et le rappel au noeud final spécifié par le paramètre de requête post_logout_redirect_uri. S’il est inclus dans la demande de déconnexion, l’OP retransmet cette valeur au RP en utilisant le paramètre de requête d’état lors de la redirection de l’agent d’utilisateur vers le RP.

Au point de terminaison de la déconnexion, l’OP DEVRAIT demander à l’utilisateur final s’il souhaite également se déconnecter de l’OP. Si l’utilisateur final dit "oui", alors l’OP DOIT déconnecter l’utilisateur final.

5.1. Redirection vers RP après la déconnexion

Dans certains cas, le RP demande à ce que l’agent d’utilisateur de l’utilisateur final soit redirigé vers le RP après la déconnexion. La redirection post-déconnexion n’est effectuée que lorsque la déconnexion est lancée par le RP, auquel cas la cible de la redirection est la valeur du paramètre de requête post_logout_redirect_uri utilisée par le RP initiateur ; sinon ce n’est pas fait. Cette spécification définit ce paramètre d’enregistrement dynamique à cette fin, conformément à la section 2.1 d’OpenID Connect Dynamic Client Registration 1.0 [OpenID.Registration].

5.1.1. Métadonnées d’inscription du client

Ce paramètre de métadonnées client PEUT être inclus dans les informations d’enregistrement du client lorsque la gestion de session et l’enregistrement dynamique sont pris en charge :

post_logout_redirect_uris

OPTIONNEL. Tableau d’URL fournies par le RP auquel il PEUT demander que l’agent d’utilisateur de l’utilisateur final soit redirigé à l’aide du paramètre post_logout_redirect_uri après la déconnexion [5].

6. Validation

Si l’une des procédures de validation définies dans la présente spécification échoue, les opérations nécessitant des informations qui n’ont pas été correctement validées DOIVENT être annulées et les informations qui n’ont pas été validées NE DOIVENT PAS être utilisées.

7. Considérations de mise en œuvre

Cette spécification définit les fonctionnalités utilisées par les parties utilisatrices et les fournisseurs OpenID ayant choisi d’implémenter la gestion de session. Toutes ces parties utilisatrices et fournisseurs OpenID DOIVENT implémenter les fonctions listées dans cette spécification comme étant "REQUIRED" ou décrites avec un "MUST". Aucune autre considération d’implémentation pour les implémentations de Session Management n’est définie par cette spécification.

8. Considérations de sécurité

L’OP iframe DOIT imposer que l’appelant ait la même origine que son cadre parent. Il DOIT rejeter les demandes postMessage provenant de toute autre source, afin d’empêcher les attaques de script entre sites.

Le paramètre id_token_hint associé à une demande de déconnexion peut être utilisé pour déterminer quel RP a lancé la demande de déconnexion. Les demandes de déconnexion sans valeur id_token_hint valide constituent un moyen de déni de service potentiel ; par conséquent, les opérateurs peuvent vouloir demander une confirmation explicite de l’utilisateur avant d’agir.

...

Critique raisonnée de la méthode et solution pour OAuthSD

Rappelons tout d’abord que la méthode des iframes n’est pas celle que préconise OAuthSD, voyez : Monitoring de l’état de l’authentification et SLO et Implémentation du monitoring avec Javascript : exemples pour SPIP et WordPress. OAuthSD met en œuvre la méthode présentée précédemment à des fins d’expérimentation et de suivi de la norme OpenID Connect.

Cependant, ce document présente des erreurs de principe conduisant à des complications inutiles, résultant en une méthode de monitoring inefficace. En particulier, de très nombreux événements hors sujet nécessitent d’interroger l’OP sur la réalité de la connexion, ce qui entraîne un trafic important.

De toutes façons, les erreurs, omissions et imprécisions de ce document conduisent à un non fonctionnement du système tel que décrit.

1. Se concentrer sur notre sujet : un utilisateur et une application donnée

"L’état du navigateur ... capture des événements significatifs tels que les connexions, les déconnexions, le changement d’utilisateur, le changement de statut d’authentification pour les clients utilisés par l’utilisateur final, etc. Ainsi, l’OP DEVRAIT mettre à jour la valeur de l’état du navigateur en réponse à de tels événements significatifs."

Dans cette phrase, l’expression "les connexions, les déconnexions, le changement d’utilisateur, le changement de statut d’authentification pour les clients utilisés par l’utilisateur final, etc." est un galimatias redondant, encore embrouillé par "des événements tels que" (il y aurait encore d’autres événements à prendre en compte ?) et par le "etc." final. Le texte qui suit "Dans le cas d’un client autorisé (réponse d’authentification réussie), l’OP DEVRAIT changer la valeur de l’état de session renvoyé au client à l’occurrence de l’un des événements suivants : - L’ensemble des utilisateurs authentifiés auprès du navigateur change (connexion, déconnexion, ajout de session). - Le statut d’authentification des clients utilisés par l’utilisateur final change." n’est qu’une redite du précédent. Passons sur le fait que l’authentification n’est pas relative au client mais à l’utilisateur final : on n’est pas à une approximation près.

"De plus, l’état du navigateur utilisé pour vérifier l’état de la session DEVRAIT changer avec de tels événements."

"Il est RECOMMANDÉ que l’état du navigateur NE DEVRAIT PAS varier trop souvent en l’absence de tels événements afin de minimiser le trafic sur le réseau causé par la réponse du client aux modifications notifiées". C’est une absurdité : s’il n’y a pas d’événement, il n’y a pas de notification de changement.

Il apparaît que les changements d’états relatifs à toutes les applications et tous les utilisateurs donnent lieu à notification d’un changement d’état.

La seule chose qui nous intéresse est de savoir si l’utilisateur de l’application ouverte sur le navigateur considéré est connecté ou non.

2. Obtenir directement l’état de connexion, et non l’indication d’un changement

"Les appels à check_session() renverront les modifications apportées par rapport aux versions antérieures de l’état de session après de tels événements.

Dès réception de la modification, le RP DOIT effectuer une nouvelle authentification avec prompt = none pour obtenir l’état de la session en cours sur le terminal opérateur..

L’idée est de notifier un changement d’état, et de réinterroger le serveur pour savoir si l’utilisateur est toujours vu connecté.

Solution OAuthSD : L’état de connexion est relatif à une application et à un utilisateur. Il est traduit par la validité du jeton d’accès.

Notes

[1Ce n’est pas DnC qui la propose ! Nous sommes d’ailleurs au recul par rapport à celle-ci.

[2Cette solution a notre préférence, voir la note suivante.

[3Il est donc inexact d’affirmer, comme indiqué au paragraphe précédent, que la connaissance de l’état de session peut être effectué "sans générer de trafic sur le réseau". Pour réduire le trafic, il conviendra d’augmenter l’intervalle d’interrogation de l’OP. Chez DnC, nous préférons tester la connexion avec prompt = none, le serveur OAuthSD étant optimisé pour répondre très rapidement à une requête de type XMLHttpRequest.

[4Obligatoire pour OAuthSD

[5Nous considérons cette fonctionnalité comme très dangereuse, car elle permet à un malware de rediriger l’utilisateur final sur une page étrangère à l’application initiale, favorisant ainsi le physing.

Drupal et Google Accounts : méthode alternative pour le monitoring

  publié le par DnC

Pour synchroniser l’état de connexion de chaque application utilisée par un même utilisateur, nous avons utilisé un code javascript pour interroger régulièrement le serveur d’authentification.
Il existe une alternative utilisée par Drupal et Google Accounts.

Voir : https://www.drupal.org/project/openid_connect_sso

Après la connexion de l’utilisateur sur le serveur ou la déconnexion de l’un des sites du réseau, le module démarre une chaîne de redirection qui visite le script SSO de chaque site du réseau.
Le script SSO définit ensuite un cookie informant le site parent de la connexion/déconnexion en attente.
Lorsque l’utilisateur visite le site réel, le cookie est lu et l’utilisateur se connecte/déconnecte automatiquement.

Cette approche est identique à celle utilisée par les comptes Google.

Le but des redirections est de donner à chaque site la possibilité de définir un cookie valide pour son domaine contournant ainsi la politique de même origine qui interdit à un site de définir un cookie pour un autre domaine.

Les redirections sont rapides et imperceptibles, car le script SSO est autonome ... et ne définit que le cookie.

  1. <?php
  2.  
  3. /**
  4.  * @file
  5.  * Creates cookies for each of the network sites to signal a login/logout.
  6.  */
  7.  
  8. if (empty($_SERVER['HTTP_HOST'])) {
  9.   // Some pre-HTTP/1.1 clients will not send a Host header.
  10.   // We can't work around this.
  11.   exit;
  12. }
  13.  
  14. // The collection of SSO script addresses which form the redirection network.
  15. // Don't include the protocol (http://, https://).
  16. // Example url (SSO script on subdomain): "a.firstsite.com"
  17. // Example url (SSO script in the Drupal directory): "firstsite.com/sso.php"
  18. $network = array(
  19.   'a.firstsite.com',
  20.   'a.shop.secondsite.com',
  21. );
  22.  
  23. // An array of network domain names. The keys are potential origin host names
  24. // which do not appear in the list above, and each value is the cookie domain
  25. // name for that host.
  26. // $domains = array();
  27.  
  28. // Enable HTTPS for all redirect URLs.
  29. // $https = true;
  30.  
  31. // Enable adding the domain name to the cookie name.
  32. // $cookie_name_strict = true;
  33.  
  34. // Validate the query parameters and network size.
  35. if (!sso_validate_query_params() || count($network) < 2) {
  36.   exit;
  37. }
  38.  
  39. // $_SERVER['HTTP_HOST'] is lowercased here per specifications.
  40. $host = strtolower($_SERVER['HTTP_HOST']);
  41.  
  42. $origin_host = $_GET['origin_host'];
  43. $origin_domain = isset($domains[$origin_host]) ? $domains[$origin_host] : $origin_host;
  44.  
  45. // Find the next site that needs to be visited in the $network, by removing
  46. // the origin site re-keying the array.
  47. foreach ($network as $delta => $site) {
  48.   if (strpos($site, $origin_domain) === 0 || strpos($site, 'a.' . $origin_domain) === 0) {
  49.     unset($network[$delta]);
  50.   }
  51. }
  52. $network = array_values($network);
  53.  
  54. if (ltrim($host, 'a.') == $origin_domain) {
  55.   // We are on the site which has started the process.
  56.   // No need to create the cookie, the site already handled its login / logout.
  57.   // Start from the beginning of the redirect list.
  58.   $redirect_destination = sso_redirect_url($network[0], !empty($https));
  59. }
  60. else {
  61.   sso_create_cookie($_GET['op']);
  62.  
  63.   foreach ($network as $delta => $site) {
  64.     if (strpos($site, $host) === 0 || strpos($site, 'a.' . $host) === 0) {
  65.       $current_site_delta = $delta;
  66.       break;
  67.     }
  68.   }
  69.  
  70.   if (!isset($current_site_delta)) {
  71.     trigger_error('Current site not found in network', E_USER_ERROR);
  72.     exit;
  73.   }
  74.  
  75.   $next_site_delta = $current_site_delta + 1;
  76.   if (isset($network[$next_site_delta])) {
  77.     // Redirect to the next network site.
  78.     $redirect_destination = sso_redirect_url($network[$next_site_delta], !empty($https));
  79.   }
  80.   else {
  81.     // We are at the last network site. In these scenarios, we need to
  82.     // redirect to the destination, or to the original host in case of a logout.
  83.     if ($_GET['op'] == 'login') {
  84.       $redirect_destination = $_GET['destination'];
  85.     }
  86.     else {
  87.       $redirect_destination = ($https ? 'https://' : 'http://') . $_GET['origin_host'];
  88.     }
  89.   }
  90. }
  91.  
  92. // Redirect the user. We need to prevent the redirect from being cached.
  93. header('Cache-Control: max-age=0', TRUE);
  94. header('Expires: Sun, 09 Aug 1987 22:00:00 +0100', TRUE);
  95. header('Pragma: no-cache', TRUE);
  96. header('Location: ' . $redirect_destination, TRUE, 302);
  97.  
  98. /**
  99.  * Validates the query parameters.
  100.  *
  101.  * Required parameters:
  102.  * - op: Tells us what the current operation is: login or logout.
  103.  * - origin_host: Indicates which site initiated the login/logout.
  104.  * Additional required parameter when the operation is "login":
  105.  * - destination: The url to redirect the user to after all redirects are done.
  106.  */
  107. function sso_validate_query_params() {
  108.   if (empty($_GET['op']) || empty($_GET['origin_host'])) {
  109.     return FALSE;
  110.   }
  111.   if (!in_array($_GET['op'], array('login', 'logout'))) {
  112.     return FALSE;
  113.   }
  114.   if ($_GET['op'] == 'login' && !isset($_GET['destination'])) {
  115.     return FALSE;
  116.   }
  117.  
  118.   return TRUE;
  119. }
  120.  
  121. /**
  122.  * Creates a cookie signaling the required operation.
  123.  *
  124.  * Removes any conflicting cookies.
  125.  *
  126.  * @param $operation
  127.  *   The operation to signal, login or logout.
  128.  */
  129. function sso_create_cookie($operation) {
  130.   if ($operation == 'login') {
  131.     $remove = 'Drupal.visitor.SSOLogout';
  132.     $create = 'Drupal.visitor.SSOLogin';
  133.   }
  134.   else {
  135.     $remove = 'Drupal.visitor.SSOLogin';
  136.     $create = 'Drupal.visitor.SSOLogout';
  137.   }
  138.  
  139.   $secure = !empty($GLOBALS['https']);
  140.  
  141.   $domain = ltrim(strtolower($_SERVER['HTTP_HOST']), 'a.');
  142.  
  143.   if (!empty($GLOBALS['cookie_name_strict'])) {
  144.     $remove .= '_' . $domain;
  145.     $create .= '_' . $domain;
  146.   }
  147.  
  148.   setcookie($remove, '', time() - 3600, '/', $domain, $secure);
  149.   // The expiration should be less than the Drupal session duration.
  150.   // The most common Drupal `session.gc_maxlifetime` value is 200000 seconds,
  151.   // so we define the expiration to half a minute before that, accordingly.
  152.   setcookie($create, 1, time() + 200000 - 30, '/', $domain, $secure);
  153. }
  154.  
  155. /**
  156.  * Returns an URL to which redirection can be issued.
  157.  *
  158.  * @param string $host
  159.  * @param bool $https
  160.  * @return string
  161.  */
  162. function sso_redirect_url($host, $https) {
  163.   if (!strpos($host, '//')) {
  164.     $host = ($https ? 'https://' : 'http://') . $host;
  165.   }
  166.   $args = array(
  167.     'op' => $_GET['op'],
  168.     'origin_host' => $_GET['origin_host'],
  169.   );
  170.   if ($_GET['op'] == 'login') {
  171.     $args['destination'] = $_GET['destination'];
  172.   }
  173.   return $host . '/?' . http_build_query($args);
  174. }

Télécharger

Offline Access

  publié le par DnC

Dans le cadre du flux Authorization Code d’OpenID Connect, "offline_access" est défini comme valeur de portée (scope) dans une demande d’autorisation. L’objectif serait de permettre à une application d’effectuer des opérations au nom d’un utilisateur final alors que celui-ci est hors ligne.

Ceci mérite un éclaircissement.

Spécification de la demande "offline_access"

Traduction du document OpenID Connect Core 1.0 incorporating errata set 1 § 11 :

11. Accès hors ligne
OpenID Connect définit la valeur de portée suivante pour demander un accès hors ligne :

offline_access

OPTIONNEL. Cette valeur de portée demande qu’un jeton d’actualisation OAuth 2.0 soit émis et puisse être utilisé pour obtenir un jeton d’accès qui accorde un accès au point de terminaison UserInfo [1] de l’utilisateur final même lorsque l’utilisateur final n’est pas présent (non connecté).

Lorsqu’un accès hors connexion est demandé, une valeur de paramètre d’invite de consentement DOIT être utilisée [2], sauf si d’autres conditions de traitement de la demande permettant un accès hors connexion aux ressources demandées sont en place. L’OP DOIT toujours obtenir l’autorisation de renvoyer un jeton d’actualisation permettant un accès hors ligne aux ressources demandées. Un consentement de l’utilisateur préalablement enregistré ne suffit pas toujours pour accorder un accès hors connexion [3].

À la réception d’un paramètre d’étendue contenant la valeur offline_access, le serveur d’autorisations :

- DOIT s’assurer que le paramètre prompt contient un consentement sauf si d’autres conditions de traitement de la demande permettant un accès hors ligne aux ressources demandées sont en place ; sauf si l’une de ces conditions ou les deux sont remplies, il DOIT alors ignorer la demande offline_access,
- DOIT ignorer la demande offline_access sauf si le client utilise une valeur response_type qui entraînerait le renvoi d’un code d’autorisation,
- DOIT explicitement recevoir ou avoir le consentement de tous les clients lorsque le type d’application enregistré est Web,
- DEVRAIT recevoir explicitement ou avoir le consentement de tous les clients lorsque le type d’application enregistré est natif.

L’utilisation des jetons d’actualisation n’est pas exclusive au cas d’utilisation offline_access. Le serveur d’autorisations PEUT accorder des jetons d’actualisation dans d’autres contextes qui ne relèvent pas de la portée de cette spécification [4].

Cas d’usage

L’objectif est de permettre à une application d’effectuer des opérations au nom d’un utilisateur final alors que celui-ci est hors ligne (le jeton d’accès n’est plus valide).

Un exemple est une sauvegarde périodique de certaines données chaque nuit.

Lors de la connexion, l’application cliente demandera le consentement de l’utilisateur en appelant le contrôleur Authorize avec la valeur ’offline_access’ dans le paramètre scope.

L’application devra enregistrer le jeton de rafraîchissement obtenu de façon persistante pour l’utiliser ultérieurement, même si l’utilisateur est déconnecté.

OAuthSD accorde une acception générale au jeton d’actualisation et en assure un contrôle serré

Certaines implémentations distinguent le jeton d’actualisation obtenu avec le consentement de l’utilisateur en réponse à la demande "offline_access" du jeton d’actualisation "ordinaire" (fourni sans ce consentement). Il est alors nommé "jeton hors ligne".

Nous ne voyons rien de tel dans les spécifications ni dans les tests de certification. Nous nous en tiendrons à l’interprétation de Brent Shaffer matérialisée dans la librairie OAuth 2.0 Server PHP :

Les demandes OpenID Connect incluent le jeton d’actualisation (refresh token) uniquement si la portée offline_access a été demandée et accordée.

Autrement dit : dans le cadre d’OpenID Connect, le jeton d’actualisation a une portée générale. Qu’il soit utilisé pour un scénario "hors ligne" ou pour tout autre usage ne dépend que du concepteur de l’application [5].

Bonnes pratiques pour la sécurité

Nous sommes d’avis que permettre l’actualisation du jeton d’accès affaiblit la sécurité des flux OpenID Connect (comme toute forme de délivrance de jeton en dehors du flux d’autorisation avec code).
Si vous mettez en œuvre le jeton d’actualisation, voyez les recommandations de cet article : Rafraîchir (actualiser) un jeton d’accès.

Notes

[1Seulement UserInfo ? L’examen de différents codes et documentations montre qu’un jeton retourné n’a pas un usage prédéfini. Un point à éclaircir.

[2Autrement dit un appel à Authorize avec prompt=consent.

[3C’est assez curieux : on voit mal comment une application "hors connexion" pourrait demander le consentement de l’utilisateur sans avoir pré-enregistré ce consentement quand l’utilisateur était connecté à l’application.

[4La sécurité requiert de contrôler l’usage du jeton d’actualisation, OAuthSD ne fournit ce jeton que si offline_access a été demandé.

[5On retrouve le principe général selon lequel la signification d’une portée d’autorisation (scope) ne dépend que de l’usage qui en est fait par l’application.