Accueil > OpenID Connect OAuth Server par DnC > Développer > Adaptation des applications

Les applications "clientes" doivent pouvoir déléguer l’authentification de l’utilisateur au serveur OIDC. De plus en plus d’applications offrent cette possibilité en conformité avec le standard OpenID Connect.

Lorsque ce n’est pas le cas, un développement particulier est nécessaire pour substituer un module OIDC au code de la connexion classique. L’adaptation est donc particulièrement aisée dans le cadre d’un développement nouveau ou d’une application "open source".

Il existe quelques plugins pour faciliter la tâche du développeur. DnC en développe quelques uns que nous présentons ici.

Note :
- Toutes les configurations d’applications, de serveurs et de ressources n’offrent pas le même niveau de sécurité. Pour une meilleure sécurité, l’application devrait être de type "web" et mettre en œuvre le flux d’autorisation avec code ( Authorization Code Grant ). Voir : Typologie des applications au regard de la sécurité des données.

Principes et organisation générale d’un déploiement OpenID Connect

  publié le par DnC

Loin d’être une contrainte pour les différentes parties d’une grande entité, la délégation de l’authentification des utilisateurs et des applications à un serveur unique permet de déployer des applications et des serveurs de données protégées, dans des lieux différents et sous des maîtrise d’œuvre différentes, tout en assurant la sécurité des données.
L’entité coordonnant ces lieux et ces autorités devra cependant faire observer des règles pour le développement et le déploiement des applications.

Principes auxquels doivent se conformer les applications

1. Toutes les applications ( au moins celles manipulant des données confidentielles ), doivent être inscrites sur le serveur OpenID Connect de l’entité et lui déléguer l’authentification des utilisateurs.

2. Toutes les données confidentielles doivent se trouver sur des serveur de ressources ( par exemple des web-services ) appartenant à l’entité et dont l’accès est sécurisé à l’aide d’OpenID Connect.

Un tel service est d’ailleurs intégré au serveur OpenID Connect sous le nom "UserInfos".
On notera que cette obligation autorisera la bonne pratique de saisie unique des données.
Elle permettra également de tracer avec exactitude l’utilisation des données confidentielles.
Enfin, il sera possible de répondre de façon précise et exhaustive aux obligations du RGPD, notamment à une demande d’information de la part d’un utilisateur sur la détention et l’emploi de ses données personnelles.

3. On se limitera aux applications qui interrogent les serveurs de ressources confidentielles depuis un serveur privé de l’entité.
En effet, toutes les configurations d’applications, de serveurs et de ressources ne permettent pas d’identifier avec certitude une application cliente, et donc de distinguer les applications malveillantes. Sauf à admettre un risque de compromission.
Ce principe, en apparence anodin, impose en réalité une grande contrainte dans les flux d’authentification à utiliser selon la configuration des applications et des utilisateurs. Certaines configurations devront être interdites, sans céder à la commodité ou à la popularité de solutions admises par le grand public. Pour approfondir le sujet, voyez : Authentifier l’application.

Pour une meilleure sécurité, l’application devra être de type "web" et mettre en œuvre le flux d’autorisation avec code ( Authorization Code Grant ).

Extension OpenID Connect pour phpBB

  publié le par DnC

Un client OIDC pour phpBB. Permet à phpBB d’utiliser OpenID Connect au lieu de l’authentification classique. (Attention : à ne pas confondre avec les extension pour OpenID !)

Les développeurs d’application phpBB pourront faciliter l’intégration des forums dans leurs autres applications Web en permettant l’accès des utilisateurs avec une inscription unique configurée sur le serveur OIDC (Single Sign-On, SSO).

OAuthSD vous offre plus que l’inscription unique. La connexion unique (Single Login-In, SLI) : une fois connecté à une application, vous serez connecté de manière transparente aux autres (à condition bien sûr qu’elles soient enregistrées sur le serveur).

Prérequis

- phpBB 3.x
- système Linux + PHP 5.6 ou 7.1.
- Attention ! le fonctionnement d’OIDC exige une application cliente installée sur un serveur sécurisé (protocole https://).

Inscription sur le serveur OAuthSD

L’application recevant l’extension doit être enregistrée sur le serveur OAuthSD [1].
La procédure est décrite ici : OpenID Connect : Lier une application cliente au serveur OAuthSD.

En suivant cette procédure :
- vous vous inscrirez en tant qu’auteur sur le serveur,

- vous enregistrerez votre application. L’enregistrement se fait ici : Inscrire une application cliente.
Vous devrez :

  • configurer l’adresse de retour (Redirect URI) sous la forme suivante : https://mon_application.com/ucp.php?mode=login&login=external&oauth_service=oauthsd
  • Inscrire les scopes "openid sli".

Notez les valeurs de "Client id" (qui correspondra à "Key" dans phpBB) et "Client secret".

Installation et configuration sur phpBB

1. Le client OIDC pour phpBB s’installe comme toute extension de phpBB :
- télécharger l’extension OIDC client for phpBB (se trouve aussi sur GitHub),
- décompresser l’archive,
- installer le code dans /ext/bdegoy/oauthsd,
- dans phpBB, installez l’extension "DnC OIDC client for OAuthSD" (section Customize -> Extension Manager).

2. Installez le service OAuthSD pour Lusitanian / PHPoAuthLib :
phpBB utilise la bibliothèque Lusitanian / PHPoAuthLib pour mettre en œuvre le SSO avec OIDC. Nous devons compléter cette bibliothèque avec le service Oauthsd. Le service OAuthSD est fourni avec le fichier ... /oauthsd-phpbb/OAuth/OAuth2/Service/Oauthsd.php qui doit être copié dans le répertoire .../vendor/lusitanian/oauth/src/OAuth/OAuth2/Service/.

3. dans phpBB, section Client Communication -> Authentication, sélectionnez la méthode d’authentification Oauth :

- Entrez les paramètres pour OAuthSD :

- puis actionnez le bouton "Submit".

Si tout s’est bien passé, un bouton "OAuthSD" doit apparaître dans la page de Connexion :

Notre prototype est visible à l’URL : https://phpbb.dnc.global/.

Notes :
- Le bouton "OAuthSD" n’apparait pas partout où la connexion est possible. Par exemple, on s’attendrait à voir le bouton sur la page de connexion à l’administration, mais ce n’est pas le cas. Cela dépend de phpBB [2], pas de l’extension.

phpBB : Tentative de connexion avec un compte inconnu du forum

Si on tente de se connecter avec un login (un "Nom d’utilisateur" dans le vocabulaire phpBB) qui n’est pas enregistré sur le forum, phpBB signale l’erreur :

il est proposé de :
- lier le compte à un compte existant : Entrer le Nom d’utilisateur et le Mot de passe puis actionner "Connexion" [3]
- créer un nouveau compte : Actionnez le bouton "S’enregistrer".

Notes :
- Seul le login (le Nom d’utilisateur) doit être identique des deux côtés, le mot de passe est propre à chaque système.
- On pourrait penser à une autre solution : inscrire le compte phpBB sur le serveur d’authentification. Mais cela irait à l’encontre du principe de connexion unique (SSO) : si un utilisateur a une inscription sur OAuthSD, c’est pour pouvoir se connecter à différents systèmes avec les mêmes identifiants.

Feuille de route

- Paramétrer l’URL du serveur OIDC afin de pouvoir choisir le serveur d’authentification. Cela devrait conduire à un client OpenID Connect universel.
- Essayer d’agir sur phpBB pour mieux intégrer l’authentification. Par exemple, l’insertion du bouton d’authentification sur la page de connexion ACL.

En conclusion ...

Cette inscription d’une application PhpBB montre qu’OAuthSD est bien un serveur d’authentification tout aussi accessible que les grandes plateformes. Mais avec OAuthSD nous vous offrons beaucoup plus : le moyen de protéger vos données et de protéger les visiteurs de vos sites à l’aide d’un serveur de votre propriété.

Notons que cette utilisation qui est faite d’OpenID Connect réduit le point de vue au SSO. OpenID Connect, c’est énormément plus que le SSO, qui n’en est qu’un aspect mineur.

OpenID Connect, c’est :
- la protection des mots de passe,
- la sécurisation des échanges entre applications réparties dans le Cloud,
- et, avec OAuthSD, la Connexion unique (Single Login-In, SLI)
etc.

Voyez :

https://area51.phpbb.com/docs/dev/3.2.x/extensions/tutorial_authentication.html

 On GitHub
https://github.com/bdegoy/oauthsd-phpbb
Issues (Bug reports or feature request) :
https://github.com/bdegoy/oauthsd-phpbb/issues

Notes

[1Dans l’état actuel du développement, l’extension ne permet que la connexion à ce serveur OAuthSD, cela n’est donc envisageable que dans une phase de développement. DnC propose à ses clients de posséder leur propre serveur OIDC afin de bénéficier de toute la sécurité offerte par un système d’authentification privé.

[2La connexion à l’aide d’un système extérieur n’est gérée que dans le script ucp.php (User Control Panel) mais pas dans acp.php (Admin Control Panel).

[3Si, ce faisant, vous liez l’utilisateur Oauth à un utilisateur phpBB qui avait déjà été lié, vous obtenez une erreur SQL du genre "Duplicate entry ’2-oauthsd’ for key ’PRIMARY’ [1062]", où "2" est l’ID de l’utilisateur phpBB . C’est un bug de phpBB qui devrait auparavant effacer l’entrée user_id = 2 dans la table phpbb_oauth_account. Si on efface manuellement cette entrée, on peut terminer l’opération.

Extension pour Wordpress "OpenID Connect Generic Client"

  publié le par DnC

Il existe différentes extensions de client OpenID Connect pour WordPress. Nous présentons ici OpenID Connect Generic Client de daggerhart qui nous parait complète, bien construite et bien documentée.

En cours de développement : un fork incluant le monitoring et le management de la session.


Installation

 L’extension peut être trouvée sur GitHub :
- https://github.com/daggerhart/openid-connect-generic,
- https://github.com/bdegoy/openid-connect-generic (notre fork).
Le plugin s’installe comme toute extension, de façon classique ou avec Composer. Voyez la documentation.

Notes :
- La source Github est mieux tenue à jour que celle du site WP (version testée jusqu’à 5.1.1 en mai 2019).

Inscription de l’application sur le serveur OAuthSD

Il s’agit des étapes 1 et 2 de la procédure décrite ici : OpenID Connect : Lier une application cliente au serveur OAuthSD.

S’inscrire en tant qu’auteur

En suivant cette procédure vous commencerez par vous inscrire en tant qu’auteur sur le serveur OAuthSD.

Inscrire l’application
Vous devez désigner votre application WordPress comme client du serveur OpenID Connect. Pour cela, accédez à l’URL : https://oa.dnc.global/web/?page=cre... :

- Client Id : Entrez un nom court, sans espace ni caractère spécial autre que ’_’. Ce n’est lié à aucune désignation de votre application.

- Client secret (obligatoire) : une courte chaîne assimilable à un mot de passe fort. Ce code doit rester secret.

- Redirect uri (obligatoire) : URI de retour à l’application cliente. Si l’URL de votre application WP est "http://votre_application.ext", inscrivez "http://votre_application.ext/wp-admin/admin-ajax.php?action=openid-connect-authorize.

- Grant Type (obligatoire) : sélectionnez "Authorization Code".

- Scopes (obligatoire) : Inscrivez "openid profile email".

Configuration de WP

Il s’agit de l’étape 3 de la procédure.

Accédez au menu Réglages -> OpenID Connect Client.

Votre application Wordpress doit être configurée comme ci-après, où Client ID et Client Secret Key sont les valeurs inscrites précédemment sur le serveur :

Inscription d’un utilisateur sur OAuthSD

Le principe de l’inscription unique (Single Sign On, SSO) permet à un utilisateur de se connecter à différentes applications avec les mêmes identifiants (login, mot de passe ...).
Il faut donc d’abord inscrire un utilisateur sur OAuthSD (ou utiliser un utilisateur OAuthSD existant). Faire l’inverse (créer l’utilisateur sur votre application WP et l’inscrire sur le serveur OpenID Connect) irait à l’encontre du principe.

L’inscription d’un utilisateur se fait ici : https://oa.dnc.global/web/spip.php?...

Création de l’utilisateur par WP

Si l’utilisateur n’existe pas du côté WP, l’extension le créera.

Notes :
- Si on tente de se loger avec un identifiant de l’utilisateur OIDC qui existe déjà pour un utilisateur WP, le plugin crée un utilisateur WP avec un identifiant incrémenté ; par exemple si le login est "admin", l’utilisateur WP d’identifiant "admin2" sera créé avec un rôle "abonné". Il appartiendra à l’administrateur du site WP de modifier éventuellement ce rôle.
- Dans le cas où l’e-mail de l’utilisateur OpenID existe déjà pour un utilisateur WP différent, c’est OpenID qui est maître : le plus logique sera de changer l’identifiant de cet utilisateur WP pour celui de l’utilisateur OpenID (puisque l’e-mail désigne une personne de façon non ambiguë).

Erreurs provenant de la configuration

- Missing state - La plupart du temps, cette erreur provient du fait que l’utilisateur a été trop long pour s’identifier, ou est retourné sur un formulaire ancien. Il suffit de recommencer.

- User claim incomplete - Se produit quand la liste des déclarations (claims) retournées par Userinfo, en fonction des portées définies pour l’application (scopes) ne contient pas celles qui sont définies dans le format.
Par exemple :

  • l’application a été configurée sans le scope email (champ de configuration OpenID Scope) et le format Email Formatting est email.
  • Display Name Formatting a été fixé à name alors que ce champ est null dans le serveur OpenID pour l’utillisateur considéré et que le nom de l’utilisateur figure dans la déclaration family_name.

- Failed user creation - Se produit le plus souvent quand WP tente de créer un nouvel utilisateur et que l’e-mail déclaré par l’utilisateur OIDC existe déjà du côté de WP.

- redirect_uri_mismatch : The redirect URI provided is missing or does not match - Cette erreur est générée par le serveur OAuthSD en réponse à la requête au contrôleur Authorize. Elle se produit lorsque l’URL de retour (Redirect URI) enregistrée sur le serveur OAuthSD n’est pas conforme à ce qui est indiqué en bas du formulaire Réglages -> OpenID Connect - Generic Client. En particulier, OAuthSD impose le protocole de transfert sécurisé (https).

- ERREUR : les cookies sont bloqués ou ne sont pas reconnus par votre navigateur. Vous devez activer les cookies pour utiliser WordPress. - Il est probable que vous ayez omis ’https://’ dans la barre d’adresse.

Erreur CORS (visible dans le debugger du navigateur)
C’est une erreur très courante que de lancer l’application de test avec http:// au lieu de https://. Cela peut également conduirte à une erreur CORS :
"(index):1 Access to XMLHttpRequest at ’https://oa.dnc.global/authorize?response_type=code&client_id=wpdemo&user_id=wpadmin&state=124979a12478&scope=openid&prompt=none’ from origin ’http://wp.dnc.global’ has been blocked by CORS policy : The ’Access-Control-Allow-Origin’ header has a value ’https://wp.dnc.global’ that is not equal to the supplied origin."

Démonstration

https://wp.dnc.global/2019/05/11/ex... est un démonstrateur d’une application WP liée à OAuthSD avec cette extension.

Monitoring : développement en cours

Nous développons un fork qui inclut le monitoring.
 Le code est visible ici : https://github.com/bdegoy/openid-co.... Le fonctionnement est semblable à ce qui a été décrit pour Plugin OpenID Connect Client pour SPIP.

Plugin OpenID Connect Client pour SPIP

  (publié initialement le jeudi 3 janvier 2019) par DnC

Un client OIDC pour SPIP. Permet à une application SPIP de proposer l’authentification avec OpenID Connect, à côté de l’authentification classique.

Les développeurs d’application SPIP pourront renforcer la protection de l’accès aux fonctionnalités réservées et à l’espace privé en limitant l’accès aux seules personnes enregistrées sur le serveur OIDC configuré [1].

Prérequis

- v 1.0.3 : SPIP 3.0, 3.1 ; v 1.0.4 : SPIP 3.2,
- système Linux + PHP 5.6 ou 7.1,
- ionCube PHP Loader installé sur le serveur de l’application SPIP (IonCube est installé par défaut sur la plupart des configurations Linux).
- Attention ! le fonctionnement d’OIDC exige une application cliente installée sur un serveur sécurisé (protocole https://).

Inscription de l’application sur le serveur OAuthSD

L’application recevant le plugin doit être enregistrée en tant que client du serveur OAuthSD.
La procédure est décrite ici : OpenID Connect : Lier une application cliente au serveur OAuthSD.

En suivant cette procédure :
- vous vous inscrirez en tant qu’auteur sur le serveur OAuthSD,

- vous enregistrerez votre application. L’enregistrement se fait ici : Inscrire une application cliente.
Vous devrez :

  • configurer l’adresse de retour (Redirect URI) sous la forme suivante : https://mon_application.com/?action=auth
  • Inscrire au moins le scope "openid".

- l’installation et la configuration du plugin répond à l’étape 3 :

Installation et configuration du plugin

 Le plugin se trouve sur GitHub :
- https://github.com/bdegoy/oidcclient

Le plugin s’installe selon la procédure d’installation manuelle de tout plugin SPIP :

  • transférer le répertoire oidcclient dans le répertoire plugins,
  • activer le plugin dans l’administration.

- ouvrir le formulaire de configuration et indiquer l’ID de l’application cliente et son secret fournis lors de l’enregistrement de l’application sur le serveur OAuthSD :

Inscription en tant qu’utilisateur OpenID Connect

Pour enregistrer votre application, vous vous êtes inscrit sur ce serveur en tant qu’auteur. Pour utiliser le SSO OpenID Connect [2], vous devez vous inscrire en tant qu’utilisateur sur un serveur d’authentification compatible. Les deux inscriptions sont distinctes.

Si vous n’êtes pas déjà inscrit sur OAuthSD, vous devez vous s’inscrire sur ce serveur en tant qu’utilisateur.

Il faut ensuite établir une correspondance entre OpenID Connect et SPIP.

Correspondance entre l’utilisateur OpenID Connect et l’auteur (l’utilisateur) SPIP

Si vous tentez la connexion OpenID Connect avec un utilisateur inconnu de l’application, vous obtiendrez un message d’erreur comme celui-ci :

Il faut que l’utilisateur OpenID Connect soit lié à un compte d’auteur de l’application SPIP pour que celle-ci accepte la connexion. Ceci est réalisé en inscrivant le login OIDC dans le champ oidc de la fiche de l’auteur.

Bien entendu, pour faire cette modification, vous devrez fournir le mot de passe du compte d’auteur [3] :

Notes :
- On pourrait imaginer de créer un compte SPIP avec le même login, mais cela n’est pas nécessaire.
- On pourrait également penser à une autre solution : créer un nouvel utilisateur OpenID Connect avec le login SPIP ; Mais cela irait à l’encontre du principe de connexion unique (SSO) : si un utilisateur a une inscription sur OAuthSD, c’est pour pouvoir se connecter à différents systèmes avec les mêmes identifiants. La bonne solution est donc de lier un auteur SPIP à un compte OIDC pré-existant, non l’inverse.
- Dans certaines applications, il peut être opportun de créer automatiquement l’utilisateur manquant. Il existe en développement une version du plugin réalisant cela. Cette façon de procéder est sûre si l’inscription préalable de l’utilisateur sur le serveur OIDC est suffisamment contrôlée au regard des exigences de sécurité de l’organisation.

Une fois liés les utilisateurs OpenID Connect et SPIP, le formulaire de connexion fait apparaître le lien et propose la connexion avec OIDC :

Dans cette situation, il reste possible de se connecter "normalement" en actionnant le lien "Ne pas utiliser OIDC, utiliser un mot de passe".

Notes :
- Un seul utilisateur OpenID Connect peut-être lié à un compte d’auteur de l’application SPIP. De même, un seul compte d’auteur de l’application SPIP peut être lié à un même utilisateur OpenID Connect.
- Seul les logins servent à établir la correspondance, les mots de passe sont propres à chaque système.

Connexion à l’application SPIP

Le plugin modifie le cadre de connexion de SPIP en ajoutant la possibilité d’utiliser OpenID Connect. La méthode classique reste disponible :

Entrez votre email ou votre signature (pseudo) puis :

Pour vous identifier avec OpenID Connect, assurez-vous que le champ "Mot de passe" est vide et actionnez le bouton "Valider".

Pour vous connecter avec la méthode standard, entrez le mot de passe d’auteur (ou utilisateur) SPIP avant d’appuyer sur le bouton "Valider".

Notes :
- Le plugin ajoute ’oidcclient’ à la liste des méthodes disponibles (variable $GLOBALS[’liste_des_authentifications’]). SPIP tente les différentes méthodes d’authentifications enregistrées en commençant par la méthode standard. En cas d’échec, SPIP passe à la méthode suivante.
- Si vous vous connectez avec la méthode standard, et que votre login ou mot de passe est faux, vous serez redirigé vers la connexion avec OpenID Connect. Dans cette situation, la fonctionnalité SLI pourrait aboutir à une connexion automatique.
- Si votre navigateur vous propose de mémoriser les identifiants de connexion, il est inutile de le faire : le mot de passe vu par le navigateur est crypté et change à chaque fois.

Monitoring

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. Il permet notamment de visualiser l’état de connexion de l’utilisateur :

icône Signification
La connexion de l’utilisateur final n’est pas établie avec le serveur OIDC. Notez que l’utilisateur peut s’être connecté à l’application à l’aide de la méthode de connexion classique de SPIP.
L’utilisateur final et l’application sont connectés par OIDC.
Erreur générale, traduit malheureusement de multiples situations :
- perte de communication Internet,
- défaut de configuration du plugin,
- défaut d’inscription de l’application cliente sur le serveur OAuthSD,
- inscription de l’application sur un autre serveur, non compatible OAuthSD,
- login inconnu du serveur OAuthSD (l’utilisateur final n’est pas inscrit sur ce serveur),
- etc.
Le message peut éventuellement être plus explicite : "authentication error", ...
Notons que l’icône erreur peut apparaître brièvement au changement de page.

Le Monitoring permet également de manager la session OIDC et notamment d’effectuer la déconnexion globale (SLO).
En cliquant sur l’icône, des popups apparaissent selon l’état de connexion :

Une fonctionnalité du monitoring consiste à alerter l’utilisateur de la fin imminente de la session OIDC et lui permettre de la prolonger :

Notons que le monitoring a besoin pour fonctionner d’être appelé à partir d’un bloc div dans toutes les pages de votre application, par exemple dans le pied de page :

SPIP

[(#REM) Monitoring OAuthSD]
<div id="oidc_connexion">
   <INCLURE{fond=inclure/oidc_monitor, env} />
</div>

Pour plus d’information sur le monitoring voyez :
- Monitoring de l’état de l’authentification et SLO
- Avertir de la fin de session OIDC et la prolonger

Informations sur le serveur et la connexion OIDC

Un clic sur le point d’interrogation fait apparaître une fenêtre popup donnant les informations sur la connexion au serveur OIDC :

- Serveur : l’URL du serveur OIDC, cliquez pour atteindre le site support du serveur.
- Client ID : l’ID de l’application courante sur le serveur.
- Login SPIP : l’identifiant de connexion de l’utilisateur à l’application SPIP. Nul si l’utilisateur n’est pas connecté.
- Login OIDC : l’identifiant d’enregistrement de l’utilisateur sur le serveur OIDC (peut-être différent du précédent). Nul si l’utilisateur n’est pas connecté.
- T. session restant : la durée de vie restante de la session OIDC.
- Délai de réponse : durée de cycle de la dernière interrogation du contrôleur Authorize. Un temps anormalement long (supérieur à la seconde) peut traduire une connexion de données lente, une erreur de communication ou une surcharge du serveur OIDC. Si vous pensez que votre connexion de données est bonne, et si une durée anormalement longue persiste, contactez l’administrateur du serveur.

Commentaires et avertissements :

- Ce plugin n’est pas un remplacement ou un équivalent du plugin OpenID.

- Il reprend cependant le principe du plugin OpenID qui consiste à donner le choix entre la connexion avec OpenID Connect ou la méthode login/mot de passe classique. Merci aux auteurs d’OpenID à qui a été emprûntée la structure générale du plugin.

- OpenID Connect (OIDC) ne doit pas être confondu avec OpenID. La connexion automatique avec un identifiant OpenID universel n’existe pas dans le standard OIDC. OIDC offre une sécurité supérieure en permettant de construire un système propriétaire dans lequel les applications clientes et les serveurs, ainsi que les utilisateurs, sont limités à un ensemble bien identifié.

- Un utilisateur qui souhaite utiliser OIDC pour s’identifier doit être enregistré sur un serveur OIDC que lui a désigné le concepteur de l’application et dont l’URL est configurée dans l’administration de ce plugin.

- Un concepteur d’application qui utilise ce plugin doit enregistrer l’application en tant que client sur un serveur OpenID Connect.

Si vous voulez le faire sur ce serveur OAuthSD, il faut vous inscrire comme auteur et administrateur d’applications puis enregistrer l’application.

- Dans l’état actuel du développement, le plugin n’a été testé qu’en relation avec le serveur OauthSD (sur ce site web).

- Ce site web implémente un démonstrateur d’OAuthSD ; le serveur peut être indisponible et les données enregistrées effacées à tout moment.

- Aucune garantie d’aucune sorte n’est attachée à l’utilisation de ce serveur, des plugins, du code et de la documentation fourni par DnC.

- Le plugin est distribué sous licence GPL3.

- une partie sensible du source du plugin n’est pas fournie pour des raisons de sécurité.

FAQ

Erreur :
Le login OIDC xxx n’est pas lié à un compte de cette application.

Cette erreur apparaît après que vous avez validé le formulaire d’identification. Elle signifie que le login que vous avez indiqué dans ce formulaire n’est pas un login d’auteur de l’application SPIP à laquelle vous avez tenté de vous connecter. Reportez-vous au paragraphe "Correspondance entre l’utilisateur OpenID Connect et l’auteur (l’utilisateur) SPIP " ci-dessus.

Un conseil : faites simple, créez le login SPIP ou modifiez-le de façon à ce qu’il soit identique au login OIDC.
Ne créez pas un nouvel utilisateur OpenID Connect avec le login SPIP, cela est contraire au principe même de l’inscription unique (Single Sign On, SSO).

Question :
Quelle différence entre le lien "Se déconnecter" et "Clore la session OpenID Connect" ?

Cela dépend de la façon dont le lien "Se déconnecter" a été écrit par le développeur de l’application SPIP :
- si le lien contient le paramètre d’URL "logout=public", les deux méthodes sont équivalentes et conduiront à la déconnexion globale : toutes les applications actuellement connectées pour l’utilisateur authentifié seront déconnectées et la session OpenID Connect sera détruite ;
- si le lien contient le paramètre d’URL "logout=local", seule l’application courante sera déconnectée, la session OpenID Connect étant préservée.

Notes :
- Le plugin surcharge la fonction action_logout_dist(). Par défaut, toute utilisation de cette fonction, sans préciser le paramètre d’URL "logout=local" générera une demande de déconnexion vers OIDC.
- Il est peut-être préférable de na pas préciser "logout=local" afin de provoquer systématiquement la déconnexion globale, ce qui va dans le sens de la sécurité, particulièrement si une application est restée connectée sur un mobile ou une autre station de travail.

Question :
Et tout cela fonctionne avec un mobile ?
Oui, bien sûr, et pourquoi pas ?
En particulier, le mobile obéira bien à une déconnexion globale venant d’une autre application.

Question :
Est-ce que fermer le navigateur suffit à provoquer une déconnexion globale ?
Non.
Il faut explicitement clore la session OIDC.
D’ailleurs, si vous relancez le navigateur sur l’application que vous venez de quitter alors qu’elle était connectée, elle sera aussitôt reconnectée.
Il en est de même si on se contente de fermer l’onglet du navigateur dans lequel l’application était lancée et connectée.

Notes

[1Pour l’instant, ce plugin n’a été validé que sur le serveur OAuthSD (ce serveur).

[2Ne pas confondre avec OpenID

[3Y compris si l’opération est effectuée par l’administrateur, afin de protéger parfaitement les intérêts de l’utilisateur.

Implémentation du monitoring avec Javascript : exemples pour SPIP et WordPress

  publié le par DnC

Implémentation du monitoring dans le plugin OpenID Connect client pour SPIP

Le site oa.dnc.global utilise ce plugin (il est client OIDC, le serpent se mord la queue !). Vous pouvez donc voir une étiquette en haut à gauche de la fenêtre qui traduit l’état du jeton d’accès lié à la connexion courante :

icône Signification
La connexion de l’utilisateur final n’est pas établie avec le serveur OIDC. Notez que l’utilisateur peut s’être connecté à l’application à l’aide de la méthode de connexion classique de SPIP.
L’utilisateur final et l’application sont connectés par OIDC.
Erreur générale, traduit malheureusement de multiples situations :
- perte de communication Internet,
- défaut de configuration du plugin,
- défaut d’inscription de l’application cliente sur le serveur OAuthSD,
- inscription de l’application sur un autre serveur, non compatible OAuthSD,
- login inconnu du serveur OAuthSD (l’utilisateur final n’est pas inscrit sur ce serveur),
- etc.
Le message peut éventuellement être plus explicite : "authentication error", ...

Le Monitoring permet également de manager la session OIDC et notamment d’effectuer la déconnexion globale (SLO).
En cliquant sur l’icône, des popups apparaissent selon l’état de connexion.

Exemple du code Javascript (avec jQuery) utilisé pour SPIP. Le plugin insère ce code en pied de chaque page.

[SPIP]

  1. /**
  2. * Plugin OpenID Connect client pour SPIP
  3. * OIDC Client Monitoring
  4. * Interroge le contrôleur OIDC Authorize avec prompt=none pour afficher l'état de connexion.
  5. * Voir également action_logout() dans oidcclient_options.php.
  6. * Auteur : B.Degoy DnC
  7. * Copyright (c) 2019 B.Degoy
  8. * Licence GPL v3.0
  9. */
  10.  
  11.     // OIDC Client Monitoring
  12.  
  13.     <?php
  14.     include_spip('inc/oidcclient_configuration'); ?>
  15.  
  16.     $(document).ready(function() {
  17.  
  18.         // Ajouter l'étiquette si elle n'existe pas
  19.         if($('#oidc').length === 0){
  20.             $('<div id="oidc">&nbsp;OIDC&nbsp;</div>   ')
  21.             .appendTo('nav')
  22.             .css('position','absolute')
  23.             .css('top','12px')
  24.             .css('left','3px')
  25.             .css('color','white')
  26.             .css('padding','3px');    
  27.         }
  28.  
  29.         var login = "<?php echo @$GLOBALS['visiteur_session']['login']; ?>";
  30.         var state = "<?php echo @$GLOBALS['visiteur_session']['state']; ?>";
  31.  
  32.         if ( login !== "" ) {
  33.             // Si on est logé localement, surveiller qu'on l'est également sur OIDC
  34.             setInterval(function(){
  35.                 $.ajax({
  36.                     type : "get",
  37.                     url : "<?php echo OIDC_AUTHORIZATION_ENDPOINT; ?>",
  38.                     data : { 'response_type' : 'code',
  39.                         'client_id' : "<?php echo OIDC_CLIENT_ID; ?>",
  40.                         'user_id' : login,
  41.                         'state' : state,
  42.                         'scope' : 'openid',
  43.                         'prompt' : 'none',
  44.                     },
  45.                     statusCode : {
  46.                         401 : function(){
  47.                             // Non authentifié sur OIDC, déconnecter localement
  48.                             var url_logout_public = "<?php echo '?action=logout&logout=local&url=' . $GLOBALS['REQUEST_URI']; ?>";
  49.                             window.location.replace(url_logout_public);
  50.                         },
  51.                         200 : function (){
  52.                             // Signaler la connexion
  53.                             $('#oidc').css('background-color', '#8f8');
  54.                             $('#oidc').text(' OIDC ');
  55.                         },
  56.  
  57.                     },
  58.                     error : function(obj,textStatus,errorThrown){
  59.                         // Signaler qu'on ne sait pas
  60.                         $('#oidc').css('background-color', 'red');
  61.                         $('#oidc').text(textStatus + ' ' + errorThrown);
  62.                     }
  63.                 });    
  64.             }, 2000);
  65.  
  66.         } else {
  67.             // Signaler la déconnexion
  68.             $('#oidc').css('background-color', 'orange');
  69.             $('#oidc').text(' OIDC ');
  70.         }
  71.  
  72.     });

Télécharger

Implémentation du monitoring dans une extension Wordpress

 Le plugin est disponible sur Github.

Le code Javascript est inséré dans un hook appelé par la fonction bootstrap() de l’extension :
[PHP]

  1. /**
  2.     * OAuthSD project https://oa.dnc.global
  3.     * OAuthSD OIDC plugin for WordPress
  4.     * Insert monitoring code in footer
  5.     * dnc1
  6.     * Author : bdegoy DnC https://degoy.com
  7.     * copyright (c) 2019 B.Degoy DnC
  8.     * Licence GPL v3.0
  9.     */
  10.  
  11.     function insert_monitoring() {
  12.      
  13.         // Enqueue some jQuery UIs
  14.         wp_enqueue_script('jquery-ui-dialog'); // from WP core
  15.         // get registered script object for jquery-ui
  16.         global $wp_scripts;
  17.         $ui = $wp_scripts->query('jquery-ui-core');
  18.         // load the Smoothness theme from Google CDN  
  19.         $protocol = is_ssl() ? 'https' : 'http';
  20.         $url = "$protocol://ajax.googleapis.com/ajax/libs/jqueryui/{$ui->ver}/themes/smoothness/jquery-ui.min.css";
  21.         wp_enqueue_style('jquery-ui-smoothness', $url, false, null);
  22.            
  23.         // OIDC Client Monitoring
  24.         $thisuri = $_SERVER['REQUEST_URI'];
  25.         $absolutepath_this_plugin = plugin_dir_path( __FILE__ );
  26.         $url_this_plugin = plugins_url('', dirname(__FILE__) ) . "/openid-connect-generic";
  27.         $state = md5(wp_get_session_token());
  28.        
  29.         // Server URLs
  30.         $settings = $this->settings;
  31.         $url_endpoint_login = $settings->endpoint_login;
  32.         $url_endpoint_token = $settings->endpoint_token;
  33.         $url_endpoint_userinfo = $settings->endpoint_userinfo;
  34.         $url_endpoint_logout = $settings->endpoint_end_session;
  35.         $parts = parse_url($url_endpoint_login);
  36.         $url_server = $parts['scheme'] . '://' . $parts['host'];
  37.        
  38.         // OIDC user
  39.         $clientID = $settings->client_id;
  40.         // $userID = ???
  41.        
  42.         // WP user
  43.         $user = get_user_by('id', get_current_user_id());
  44.         $login = $user->user_login;
  45.         $nom_auteur =$user->display_name;
  46.        
  47.         // for info popup
  48.         $infos =
  49.         '<br/>' . __('oidcclient:serveur_url','openid-connect-generic') . ' : <a href="' . $url_server . '">' . $url_server . '</a>' .
  50.         '<br/>' . __('oidcclient:client_id','openid-connect-generic') . ' : ' . $clientID .
  51.         '<br/>' . __('oidcclient:login_wp','openid-connect-generic') . ' : ' . $login .
  52.         //'<br/>' . __('oidcclient:login_oidc_court','openid-connect-generic') . ' : ' .  $userID .
  53.         '<br/>' . __('oidcclient:nom_auteur','openid-connect-generic') . ' : ' .  $nom_auteur;
  54.        
  55.         // labels and messages
  56.         $msg_session_connected_no = __('oidcclient:session_connected_no','openid-connect-generic');
  57.         $msg_session_connected_yes = __('oidcclient:session_connected_yes','openid-connect-generic');
  58.         $msg_session_connected_error = __('oidcclient:session_connected_error','openid-connect-generic');
  59.         $msg_session_open = __('oidcclient:session_open','openid-connect-generic');
  60.         $msg_session_extend = __('oidcclient:session_extend','openid-connect-generic');
  61.         $msg_session_close = __('oidcclient:session_close','openid-connect-generic');
  62.         $msg_session_expires = __('oidcclient:session_expires','openid-connect-generic');
  63.         $lbl_yes = __('oidcclient:item_yes','openid-connect-generic');
  64.         $lbl_no = __('oidcclient:item_no','openid-connect-generic');
  65.         $lbl_t_session_restant = __('oidcclient:t_session_restant','openid-connect-generic');
  66.         $lbl_delai_reponse = __('oidcclient:delai_reponse','openid-connect-generic');
  67.         $lbl_infos_titre = __('oidcclient:lbl_infos_titre','openid-connect-generic');
  68.        
  69.         // link to OIDC login                                                                                        
  70.         $link_login = $this->client_wrapper->get_authentication_url();
  71.        
  72.         // link to logout page
  73.         $url_logout = esc_url($url_this_plugin . "/oidc_logout.php?url=" . $_SERVER['REQUEST_URI']);
  74.    
  75.         echo <<<JSCODE
  76.            
  77. <script type="text/javascript">
  78.    
  79. (function($) {
  80.  
  81.     var login = "$login";
  82.     var timeleft = 0;
  83.     var connected = 0;
  84.     var connectionMsg = '';
  85.     var interval = null;
  86.     var pollperiod = 60000;
  87.     var tagappendto = '#content';
  88.     var tagtop = '120px';
  89.     var tagleft = '16px';
  90.     var responseDelay = 'Unk';
  91.  
  92.     $(document).on('ready',function(){
  93.  
  94.         // Add OIDC labels
  95.         if($('#oidc').length === 0){
  96.             $('<div id="oidc"><span id="oidctag">&nbsp;OIDC&nbsp;</span><span id="oidcinfo">&nbsp;?&nbsp;</span></div>')
  97.             .appendTo(tagappendto);
  98.             //
  99.             $('#oidc')
  100.             .css('position','absolute')
  101.             .css('top',tagtop)
  102.             .css('left',tagleft);
  103.             //
  104.             $('#oidctag')
  105.             .css('color','white')
  106.             .css('padding','3px')
  107.             .css('z-index','10000')
  108.             .on('click', function(){
  109.                 switch (connected) {
  110.                     case 0 :
  111.                         connectionMsg = "$msg_session_connected_no";
  112.                         SessionOpenDialog(connectionMsg);
  113.                         break;
  114.                     case 1 :
  115.                         connectionMsg = "$msg_session_connected_yes";
  116.                         SessionCloseDialog(connectionMsg);
  117.                         break;
  118.                     default :
  119.                     case -1 :
  120.                         connectionMsg = "$msg_session_connected_error";
  121.                         break;
  122.                 };
  123.             });
  124.             //
  125.             $('#oidcinfo')
  126.             .css('color','white')
  127.             .css('padding','3px')
  128.             .css('z-index','10001')
  129.             .css('background-color','#09f')
  130.             .on('click', function(){
  131.                 $('<div></div>').appendTo('body')
  132.                 .html('<div><h6>$infos<br/>$lbl_t_session_restant : ' + timeleft + ' s<br/>$lbl_delai_reponse : ' + responseDelay + ' ms</h6></div>')
  133.                 .dialog({
  134.                     modal: true, title: "$lbl_infos_titre", zIndex: 10000, autoOpen: true,
  135.                     width: 'auto', resizable: false,
  136.                     close: function (event, ui) {
  137.                         $(this).remove();
  138.                         interval = setInterval(pollOidc,pollperiod);
  139.                         }
  140.                     });
  141.                 }
  142.             );            
  143.         }
  144.  
  145.         // If user is logged locally, verify the OIDC session is valid.  
  146.         if ( login !== "" ) {
  147.             pollOidc();
  148.             interval = setInterval(pollOidc,pollperiod);
  149.  
  150.         } else {
  151.             connected = 0;
  152.             // Show not OIDC connected.
  153.             $('#oidctag').css('background-color', 'orange');
  154.             $('#oidctag').text(' OIDC ');
  155.         }
  156.  
  157.         function SessionCloseDialog(message) {    //[dnc28d]
  158.             clearInterval(interval);
  159.             $('<div></div>').appendTo('body')
  160.             .html('<div><h6>'+message+'?</h6></div>')
  161.             .dialog({
  162.                 modal: true, title: "$msg_session_close", zIndex: 10000, autoOpen: true,
  163.                 width: 'auto', resizable: false,
  164.                 buttons: [
  165.                     {
  166.                         text: "$lbl_yes",
  167.                         click: function () {
  168.                             // Close the OIDC session.
  169.                             window.location.replace("$url_logout");
  170.                             $(this).dialog("close");
  171.                         }
  172.                     },{
  173.                         text: "$lbl_no",
  174.                         click: function () {                                                              
  175.                             $(this).dialog("close");
  176.                             interval = setInterval(pollOidc,pollperiod);
  177.                         }
  178.                     }
  179.                 ],
  180.                 close: function (event, ui) {
  181.                     $(this).remove();
  182.                     interval = setInterval(pollOidc,pollperiod);
  183.                 }
  184.             });
  185.         };
  186.  
  187.         function SessionOpenDialog(message) {    //[dnc28d]
  188.             clearInterval(interval);
  189.             $('<div></div>').appendTo('body')
  190.             .html('<div><h6>'+message+'?</h6></div>')
  191.             .dialog({
  192.                 modal: true, title: "$msg_session_open", zIndex: 10000, autoOpen: true,
  193.                 width: 'auto', resizable: false,
  194.                 buttons: [
  195.                     {
  196.                         text: "$lbl_yes",
  197.                         click: function () {
  198.                             // Se connecter
  199.                             window.location.replace("$link_login");
  200.                             $(this).dialog("close");
  201.                         }
  202.                     },{
  203.                         text: "$lbl_no",
  204.                         click: function () {                                                                
  205.                             $(this).dialog("close");
  206.                             interval = setInterval(pollOidc,pollperiod);
  207.                         }
  208.                     }
  209.                 ],
  210.                 close: function (event, ui) {
  211.                     $(this).remove();
  212.                     interval = setInterval(pollOidc,pollperiod);
  213.                 }
  214.             });
  215.         };
  216.  
  217.         function ExtendDialog(message) {    //[dnc28d]
  218.             if ( !$("#extenddialog").size() ) {  //[dnc28f]
  219.                 clearInterval(interval);
  220.                 $('<div id="extendeddialog"></div>').appendTo('body')
  221.                 .html('<div><h6>'+message+'?</h6></div>')
  222.                 .dialog({
  223.                     modal: true, title: "$msg_session_extend", zIndex: 10000, autoOpen: true,
  224.                     width: 'auto', resizable: false,
  225.                     buttons: [
  226.                         {
  227.                             text: "$lbl_yes",
  228.                             click: function () {
  229.                                 // Extend session
  230.                                 $.ajax({
  231.                                     type : "get",
  232.                                     url : "$url_endpoint_login",
  233.                                     data : { 'response_type' : 'code',
  234.                                         'client_id' : "$clientID",
  235.                                         'user_id' : login,
  236.                                         'state' :  "$state",
  237.                                         'scope' : 'openid sli',
  238.                                     }
  239.                                 });
  240.                                 $(this).dialog("close");
  241.                                 interval = setInterval(pollOidc,pollperiod);
  242.                             }
  243.                         },{
  244.                             text: "$lbl_no",
  245.                             click: function () {                                                                
  246.                                 $(this).dialog("close");
  247.                                 interval = setInterval(pollOidc,pollperiod);
  248.                             },
  249.                         }
  250.                     ],
  251.                     close: function (event, ui) {
  252.                         $(this).remove();
  253.                         interval = setInterval(pollOidc,pollperiod);
  254.                     },
  255.                 });
  256.             }
  257.         };
  258.  
  259.         // Test OIDC connection.
  260.         function pollOidc(){
  261.             connected = -1;
  262.             var d = new Date();
  263.             var timeStart = d.getTime();
  264.             var timeStop = 0;
  265.             $.ajax({
  266.                 type : "get",
  267.                 url : "$url_endpoint_login",
  268.                 data : { 'response_type' : 'code',
  269.                     'client_id' : "$clientID",
  270.                     'user_id' : login,
  271.                     'state' :  "$state",
  272.                     'scope' : 'openid',
  273.                     'prompt' : 'none',
  274.                 },
  275.                 statusCode : {
  276.                     401 : function(){
  277.                         connected = 0;
  278.                         var d = new Date();
  279.                         timeStop = d.getTime();
  280.                         // Not (or no longer) connected on OIDC, disconnect locally
  281.                         window.location.replace("$url_logout" +"&logout=local");
  282.                     },
  283.                     200 : function ( data, textStatus, jqXHR){
  284.                         connected = 1;
  285.                         var d = new Date();
  286.                         timeStop = d.getTime();
  287.                         // Show OIDC connected
  288.                         $('#oidctag').css('background-color', '#8f8');
  289.                         $('#oidctag').text(' OIDC ');
  290.                         timeleft = data['timeleft'];
  291.                         if ( timeleft < 600 ) {  //[dnc28d]
  292.                             // Approaching OIDC session end.
  293.                             clearInterval(interval);
  294.                             ExtendDialog("$msg_session_expires");
  295.                             interval = setInterval(pollOidc,pollperiod);
  296.                         }
  297.                     },
  298.                 },
  299.                 error : function(obj,textStatus,errorThrown){
  300.                     connected = -1;
  301.                     // Show error (OIDC state is unknown)
  302.                     $('#oidctag').css('background-color', 'red');
  303.                     $('#oidctag').text(textStatus + ' ' + errorThrown);
  304.                 },
  305.                 complete : function ( data, textStatus, jqXHR){
  306.                     if ( timeStop && timeStart ) {
  307.                         responseDelay = timeStop - timeStart;      
  308.                     } else {
  309.                         responseDelay = 'Unk';
  310.                     }
  311.                 },
  312.             });    
  313.         }
  314.  
  315.     });
  316. })( jQuery );
  317.  
  318. </script>
  319.  
  320. JSCODE
  321. ;
  322.    
  323. } //function

Télécharger

Notons qu’il faut insérer un bloc div en bas de page, ce qui est fait au moyen d’une action dans la fonction bootstrap en même temps que le hook de la fonction précédente :
[PHP]

  1. //OIDC Monitoring
  2.         wp_enqueue_script("jquery");
  3.         add_action('wp_footer', array( $plugin, 'insert_monitoring'), 5);

Télécharger

Service OAuthSD pour Lusitanian/PHPoAuthLib

  publié le par DnC

La bibliothèque Lusitanian/PHPoAuthLib fournit un support oAuth en PHP et est très facile à intégrer à tout projet nécessitant un client OAuth 2.

Cet article présente le code du service OAuthSD à inclure dans la librairie.


Si la classe Oauthsd n’est pas fournie par un plugin ou une extension, le code ci-dessous doit être inséré dans la librairie à l’emplacement et sous le nom indiqué :

.../vendor/lusitanian/oauth/src/OAuth/OAuth2/Service/Oauthsd.php

Notes :
- Le scope openid indique que nous utilisons la couche OpenID Connect.
- ’response_type’ => ’code’ indique que nous utilisons le flux Authorization code.
- Le scope "sli" permet (notamment) la connexion unique.
- Pour une utilisation en simple SSO, il est inutile de risquer de dévoiler les données de l’utilisateur avec le scope profile. En supprimant le scope profile, OAuthSD utilisera le scope par défaut basic, et seul le champ sub (subject) sera retourné par la demande d’authentification.

PHP

  1. <?php
  2. /**  
  3. * OpenID Connect Authentication with Oauthsd
  4. * @link     https://oa.dnc.global
  5. * Oauthsd.php OAuth service for the Lusitanian PHPoAuthLib
  6. * @link     https://github.com/Lusitanian/PHPoAuthLib
  7. * @author   Bertrand Degoy bertrand@degoy.com
  8. * @copyright (c) 2018 B.Degoy DnC https://degoy.com
  9. * @license  http://www.opensource.org/licenses/mit-license.html MIT License
  10. */
  11.  
  12. //dnc4
  13.  
  14. namespace OAuth\OAuth2\Service;
  15.  
  16. use OAuth\OAuth2\Token\StdOAuth2Token;
  17. use OAuth\Common\Http\Exception\TokenResponseException;
  18. use OAuth\Common\Http\Uri\Uri;
  19. use OAuth\Common\Consumer\CredentialsInterface;
  20. use OAuth\Common\Http\Client\ClientInterface;
  21. use OAuth\Common\Storage\TokenStorageInterface;
  22. use OAuth\Common\Http\Uri\UriInterface;
  23.  
  24.  
  25. class Oauthsd extends AbstractService
  26. {
  27.  
  28.     /**
  29.     * Available scopes (will be tested by AbstractService).
  30.     * Might be larger than those defined by client application.
  31.     * @link https://oa.dnc.global/-Sujets-communs-.html#definitionetgestiondesscopesdansOAuthsd
  32.     * @see  #attributes
  33.     */
  34.     const SCOPE_OPENID           = 'openid';
  35.     const SCOPE_PROFILE          = 'profile';
  36.     const SCOPE_EMAIL            = 'email';
  37.     const SCOPE_ADDRESS          = 'address';
  38.     const SCOPE_PHONE            = 'phone';
  39.     const SCOPE_SLI              = 'sli';
  40.  
  41.     public function __construct(
  42.         CredentialsInterface $credentials,
  43.         ClientInterface $httpClient,
  44.         TokenStorageInterface $storage,
  45.         $scopes = array(),
  46.         UriInterface $baseApiUri = null
  47.     ) {
  48.  
  49.         $scopes = array_merge($scopes, array(openid, sli, profile));   // profile ???
  50.  
  51.         parent::__construct($credentials, $httpClient, $storage, $scopes, $baseApiUri);
  52.  
  53.         if (null === $baseApiUri) {
  54.             $this->baseApiUri = new Uri('https://oa.dnc.global/');
  55.         }
  56.     }
  57.  
  58.     /**
  59.     * Override abstract function in order to provide required parameters in authorization request.
  60.     * State is required by OAuthSD
  61.     * Scopes :
  62.     * openid is required by OpenID Connect, sli is particular to OAuthSD, basic is enough for phpBB.
  63.     * @link https://oa.dnc.global/-Sujets-communs-.html#definitionetgestiondesscopesdansOAuthsd
  64.     */
  65.     public function getAuthorizationUri(array $additionalParameters = array())
  66.     {
  67.         $parameters = array_merge(
  68.             $additionalParameters,
  69.             array(
  70.                 //'type'          => 'web_server',
  71.                 'client_id'     => $this->credentials->getConsumerId(),
  72.                 'redirect_uri'  => $this->credentials->getCallbackUrl(),
  73.                 'response_type' => 'code',
  74.                 'scope'         => 'openid sli',    // do not mention basic.      
  75.             )
  76.         );
  77.  
  78.         if (!isset($parameters['state'])) {
  79.             $parameters['state'] = $this->generateAuthorizationState();
  80.         }
  81.         $this->storeAuthorizationState($parameters['state']);
  82.  
  83.         // Build the url
  84.         $url = clone $this->getAuthorizationEndpoint();
  85.         foreach ($parameters as $key => $val) {
  86.             $url->addToQuery($key, $val);
  87.         }
  88.         return $url;
  89.     }
  90.  
  91.     /**
  92.     * {@inheritdoc}
  93.     */
  94.     public function getAuthorizationEndpoint()
  95.     {
  96.         return new Uri('https://oa.dnc.global/authorize');
  97.     }
  98.  
  99.     /**
  100.     * {@inheritdoc}
  101.     */
  102.     public function getAccessTokenEndpoint()
  103.     {
  104.         return new Uri('https://oa.dnc.global/token');
  105.     }
  106.  
  107.     /**
  108.     * {@inheritdoc}
  109.     */
  110.     protected function getAuthorizationMethod()
  111.     {
  112.         return static::AUTHORIZATION_METHOD_HEADER_BEARER;        // ou AUTHORIZATION_METHOD_QUERY_STRING ???
  113.     }
  114.  
  115.     /**
  116.     * {@inheritdoc}
  117.     */
  118.     protected function parseAccessTokenResponse($responseBody)
  119.     {
  120.         $data = json_decode($responseBody, true);
  121.  
  122.         if (null === $data || !is_array($data)) {
  123.             throw new TokenResponseException('Unable to parse response.');
  124.         } elseif (isset($data['message'])) {
  125.             throw new TokenResponseException('Error in retrieving token: "' . $data['message'] . '"');
  126.         } elseif (isset($data['name'])) {
  127.             throw new TokenResponseException('Error in retrieving token: "' . $data['name'] . '"');
  128.         }
  129.  
  130.         $token = new StdOAuth2Token();
  131.         $token->setAccessToken($data['access_token']);
  132.         $token->setLifeTime($data['expires_in']);
  133.  
  134.         if (isset($data['refresh_token'])) {
  135.             $token->setRefreshToken($data['refresh_token']);
  136.             unset($data['refresh_token']);
  137.         }
  138.  
  139.         unset($data['access_token']);
  140.         unset($data['expires_in']);
  141.  
  142.         $token->setExtraParams($data);
  143.  
  144.         return $token;
  145.     }
  146.  
  147.     /**
  148.     * {@inheritdoc}
  149.     */
  150.     public function requestAccessToken($code, $state = null)
  151.     {
  152.         if (null !== $state) {
  153.             $this->validateAuthorizationState($state);
  154.         }
  155.  
  156.         $bodyParams = array(
  157.             'code'          => $code,
  158.             'client_id'     => $this->credentials->getConsumerId(),
  159.             'client_secret' => $this->credentials->getConsumerSecret(),
  160.             'redirect_uri'  => $this->credentials->getCallbackUrl(),
  161.             'grant_type'    => 'authorization_code',
  162.         );
  163.  
  164.         $responseBody = $this->httpClient->retrieveResponse(
  165.             $this->getAccessTokenEndpoint(),
  166.             $bodyParams,
  167.             $this->getExtraOAuthHeaders()
  168.         );
  169.        
  170.         $token = $this->parseAccessTokenResponse($responseBody);
  171.         $this->storage->storeAccessToken($this->service(), $token);
  172.  
  173.         return $token;
  174.     }
  175. }

Télécharger

Feuille de route

Dans une organisation fermée (corporate realm), on souhaite posséder son propre serveur OIDC et obtenir des données Userinfo selon un format spécifique, généralement en utilisant une base user existante.
Pour cela, il faudrait :
- une fonction setter pour l’URL du serveur OIDC afin de pouvoir choisir le serveur d’authentification.
- une fonction setter pour configurer les données de Userinfo.
- cela devrait conduire à un client OpenID Connect universel. Tenter alors un pull request vers lusitanian ?

Applications natives : un exemple avec Windev

  publié le par DnC

Nous donnons ici un exemple d’une application Windows native réalisée avec Windev qui utilise l’authentification OpenID Connect.

Cette application met en oeuvre le flux d’autorisation avec code (Authorization Code Grant) d’OpenID Connect en passant par l’interface de bouclage (loopback) du réseau local.

Redirection par l’interface de bouclage du réseau

C’est l’une des trois méthodes décrites dans cet article : OAuth 2.0 (et OpenID Connect) pour les applications natives.

Traduction d’extraits de rfc8252 :

Les applications natives capables d’ouvrir un port sur l’interface de bouclage du réseau peuvent utiliser l’interface de bouclage du réseau pour recevoir la redirection OAuth.

Les URI de redirection de bouclage utilisent le schéma HTTP et sont construits avec l’adresse IP littérale de l’interface de bouclage et le port sur lequel le client écoute. C’est-à-dire http://127.0.0.1:{port‹/{path} pour IPv4 et http: // [:: 1]: {port} / {chemin} pour IPv6.

Exemple avec Windev WD OAuth

Depuis la version 24, Windev permet de réaliser une authentification via le protocole OpenID Connect [1] avec la composante wd240ggl.dll. Voir : Fonction AuthIdentifie.

Cette implémentation répond aux éléments de spécification présentés ci-dessus en employant comme il se doit le flux Authorization Grant (et non un flux implicite), en passant par l’interface de bouclage localhost, ainsi qu’un navigateur extérieur à l’application.

Nous avons modifié l’exemple didactique (WINDEV) WD OAuth pour intégrer une nouvelle classe OAuthSDLogin. Cette classe est absolument identique (au paramétrage près) à la classe GoogleLogin, ce qui prouve la totale compatibilité du serveur OAuthSD.

Ceci permet d’affirmer que toute application assurant l’authentification avec Google OpenID Connect pourra fonctionner avec OAuthSD, pourvu que les paramètres de connexion au serveur d’authentification puissent être modifiés pour adopter ceux d’OAuthSD.

WINDEV

  1. // Classe OAuthSDLogin
  2.         // Gère l'authentification OpenID Connect via OAuthSD. Voir : https://oa.dnc.global/86
  3.         OAuthSDLogin est une Classe
  4.                 hérite de OAuthAppLogin
  5. FIN
  6.  
  7. PROCÉDURE Constructeur()
  8.  
  9. // Appel du constructeur OAuthAppLogin
  10. Constructeur OAuthAppLogin("OAuthSD")
  11.  
  12. // Décrit l'application OAuth
  13. // La clé et le code secret sont inscrits sur le serveur OAuthSD au moment de l'enregistrement de l'application.
  14. m_oServiceOAuth..ClientID = "WindevOAuthSDLogin"                // 'Client ID'
  15. m_oServiceOAuth..ClientSecret = "uhkdfk7bUXWq!"         // 'Client secret'
  16.  
  17.  
  18. dbgVérifieDifférence(m_oServiceOAuth..ClientID, "", "ClientID non renseigné")
  19. dbgVérifieDifférence(m_oServiceOAuth..ClientSecret, "", "ClientSecret non renseigné")
  20.  
  21. // Le flux OAuth 2.0 requiert des 'Scopes'
  22. // "Space-delimited set of scope strings."
  23. m_oServiceOAuth..Scope = "openid profile sli"
  24. // Le flux OAuth 2.0 nécessite deux points d'accès :
  25. // - un point d'accès pour authentifier l'utilisateur
  26. // - un point d'accès pour l'obtention d'un 'AccessToken'
  27. m_oServiceOAuth..URLAuth = "https://oa.dnc.global/authorize"
  28. m_oServiceOAuth..URLToken = "https://oa.dnc.global/token"
  29. m_oServiceOAuth..ParamètresSupplémentaires = "response_type=code"             //
  30.  
  31. // L'URL de callback vers localhost permet à l'application de recevoir le code d'autorisation.
  32. m_oServiceOAuth..URLRedirection = "http://localhost:9000/"
  33.  
  34.  
  35. PROCÉDURE Destructeur()
  36.  
  37.  
  38.  
  39. // Résumé : Lit le profil utilisateur
  40. // Syntaxe :
  41. //[ <Résultat> = ] LectureProfilUtilisateur ()
  42. //
  43. // Paramètres :
  44. //      Aucun
  45. // Valeur de retour :
  46. //      OAuthWebUser : Informations sur l'utilisateur
  47. //
  48. PROCÉDURE VIRTUELLE PROTÉGÉE LectureProfilUtilisateur()
  49.  
  50. clInfo          est un OAuthWebUser
  51. vInfo           est un Variant
  52. oRequête       est une httpRequête
  53. oRéponse       est une httpRéponse
  54.  
  55. oRequête..URL          = "https://oa.dnc.global/userinfo"
  56. oRequête..AuthToken = m_oToken
  57. oRequête..Méthode     = httpGet
  58.  
  59. // Exécute la requête permettant d'obtenir les informations du profil de l'utilisateur connecté
  60. oRéponse <- HTTPEnvoie(oRequête)
  61. SI PAS ErreurDétectée _ET_ PAS oRéponse..CodeEtat >= 400 ALORS
  62.        
  63.         // Décode la réponse JSON contenu dans le message
  64.         vInfo = JSONVersVariant(oRéponse..Contenu)
  65.        
  66.         // Recopie les données lues
  67.         // Attention, les chaînes sont au format ANSI / UTF-8
  68.         // Pour les afficher, il est nécessaire d'utiliser AnsiVersUnicode(<chaîne>, alphabetUTF8)
  69.         clInfo.ID                       = vInfo.id
  70.         clInfo.Nom                      = vInfo.name
  71.         clInfo.NomAffiché      = vInfo.given_name
  72.         clInfo.Image            = vInfo.picture
  73.         clInfo.Email            = vInfo.email
  74. FIN
  75.  
  76. RENVOYER clInfo

Télécharger

Dans le paramétrage de la demande d’autorisation, noter :
- m_oServiceOAuth..Scope = "openid profile sli" : le scope openid entraîne l’utilisation du protocole OpenID Connect.
- m_oServiceOAuth..ParamètresSupplémentaires = "response_type=code" qui entraîne la mise en oeuvre du flux Authorization Code Grant.

Comme d’habitude, le login est "bebert", le mot de passe "012345678"

Après login, l’exemple effectue l’échange des jetons puis une requête userinfo, dont le résultat, guère impressionnant, est cependant probant du bon fonctionnement :

On peut également vérifier le bon fonctionnement de la connexion unique (SLI) avec cette application.

Notes

[1PC Soft nomme "OpenID" ce qui est en fait OpenID Connect.