Accueil > OpenID Connect OAuth Server par DnC > Développer > Sujets communs à Oauth 2 et OpenID Connect

Envoi d’une requête à un serveur de ressource (RS)

  publié le par DnC

Dans cet article, nous traitons de l’accès à une ressource protégée de type HTTP REST, avec les bonnes pratiques.

Envoi d’une requête à une ressource protégée

Il y a trois méthodes pour l’envoi d’une requête à une ressource protégée : une bonne, une acceptable et une mauvaise :

Ce qui suit est en partie traduit de la RFC 6750, section 2

2. Requêtes authentifiées

Cette section définit trois méthodes d’envoi de jetons d’accès aux serveurs de ressources. Les clients NE DOIVENT PAS utiliser plus d’une méthode pour transmettre le jeton dans chaque requête.

2.1. Demande d’autorisation par Champ d’en-tête (la bonne méthode)

Lors de l’envoi du jeton d’accès par l’en-tête de la requête (Request Header) dans le champ "Authorization" défini par HTTP / 1.1 [RFC2617], le client utilise le schéma d’authentification "Bearer" pour transmettre le jeton d’accès.
Par exemple :

    GET / resource HTTP / 1.1
    Host: oa.dnc.global
    Authorization: Bearer mF_9.B5f-4.1JqM

Exemples de code :

PHP

  1.     $h = curl_init('https://oa.dnc.global/userinfo');
  2.     curl_setopt($h, CURLOPT_RETURNTRANSFER, true);
  3.     curl_setopt($h, CURLOPT_TIMEOUT, 10);
  4.     curl_setopt($h, CURLOPT_HTTPHEADER,
  5.                         array('Authorization: Bearer '
  6.                         . $access_token));
  7.     $res = curl_exec($h);    

Télécharger

SPIP

  1.     $url = "https://oa.dnc.global/userinfo";
  2.     $options = array(
  3.          'methode' => 'GET',
  4.         'datas' => 'Authorization: Bearer ' . $access_token,
  5.     );
  6.     $res = recuperer_url($url, $options);

Télécharger

Les clients DOIVENT faire des requêtes authentifiées avec un jeton Bearer en utilisant le champ d’en-tête de demande « Authorization » avec le schéma d’autorisation du protocole HTTP « Bearer ». Les serveurs de ressources DOIVENT supporter cette méthode.

2.2. Paramètres form-urlencoded dans le corps (une méthode acceptable sous conditions)

Lors de l’envoi du jeton d’accès dans le corps d’entité de requête HTTP, le Client ajoute le jeton d’accès au corps de requête en Paramètre "access_token". Le client NE DOIT PAS utiliser cette méthode à moins que toutes les conditions suivantes ne soient remplies :
- L’entête d’entité de requête HTTP inclut le champ d’en-tête "Content-Type" "application / x-www-form-urlencoded".
- Le corps de l’entité suit les exigences d’encodage du type de contenu "Application / x-www-form-urlencoded" tel que défini par HTML 4.01 [W3C.REC-html401-19991224].
- Le corps de l’entité de demande HTTP est mono-partie.
- Le contenu codé dans le corps de l’entité DOIT se composer entièrement de caractères ASCII [USASCII].
- La méthode de requête HTTP suit la sémantique définie pour le corps de requête. En particulier, cela signifie que la méthode "GET" NE DOIT PAS être utilisée.

Le corps de l’entité PEUT inclure d’autres paramètres spécifiques aux requêtes, auquel cas le paramètre "access_token" DOIT être correctement séparé des paramètres spécifiques à la requête en utilisant le caractère "&" (ASCII Code 38).

Par exemple, le client effectue la requête HTTP suivante en utilisant la sécurité des couches de transport :

    POST / ressource HTTP / 1.1
    Host: oa.dnc.global
    Content-Type: application/ x-www-form-urlencoded
    access_token = mF_9.B5f-4.1JqM

La méthode "application/ x-www-form-urlencoded" NE DOIT PAS être utilisée sauf dans les contextes d’application où les navigateurs participants n’ont pas accès au champ d’en-tête de demande "Authorization". Les serveurs PEUVENT soutenir cette méthode.

Exemple de code :
PHP

  1.     $data2 = array(
  2.         'access_token' => $access_token,
  3.     );
  4.     $h = curl_init($userinfo_endpoint);
  5.     curl_setopt($h, CURLOPT_RETURNTRANSFER, true);
  6.     curl_setopt($h, CURLOPT_TIMEOUT, 10);
  7.     curl_setopt($h, CURLOPT_POST, true);
  8.     curl_setopt($h, CURLOPT_HTTPHEADER,
  9.                 array('Content-Type: application/x-www-form-urlencoded'));    
  10.     curl_setopt($h, CURLOPT_POSTFIELDS, http_build_query($data2));
  11.     $res = curl_exec($h);

Télécharger

2.3. Paramètres de requête dans l’URI (la mauvaise méthode)

Lors de l’envoi du jeton d’accès dans l’URI de requête HTTP, le client ajoute le jeton d’accès au composant de requête de l’URI tel que défini par "Uniform Resource Identifier (URI) : Syntaxe générique" [RFC3986], en utilisant le paramètre "access_token".

Par exemple, le client effectue la requête HTTP suivante en utilisant la sécurité des couches de transport :

    GET /resource?access_token=mF_9.B5f-4.1JqM HTTP / 1.1
    Host: oa.dnc.global

...

Les clients utilisant la méthode URI Query Parameter DEVRAIENT également envoyer un entête Cache-Control contenant l’option "no-store". Les réponses de succès (status 2XX) du serveur à ces demandes Serveur DOIVENT contenir un entête Cache-Control avec l’option "private".

En raison des faiblesses de sécurité associées à la méthode URI (Voir la section 5), y compris la forte probabilité que l’URL contenant le jeton d’accès soit enregistrée, elle NE DOIT PAS être utilisée à moins qu’il soit impossible de transporter le jeton d’accès dans le champ d’en-tête de requête "Authorization" ou dans le corps d’entité de la requête HTTP. Les serveurs de ressources PEUVENT supporter cette méthode.

Cette méthode est incluse pour documenter une utilisation possible ; son utilisation n’est pas recommandée en raison de ses déficiences en matière de sécurité (voir section 5), mais également parce qu’elle utilise un nom de paramètre de requête réservé, ce qui est contraire aux meilleures pratiques de l’espace de noms URI, définies par « Architecture of the World Wide Web, Volume One" [W3C.REC-webarch-20041215].

Voir aussi :
- Validation du jeton d’accès par interrogation du serveur d’autorisation (Introspection)
- Contrôle d’accès HTTP (CORS)

Recommandation de sécurité

Les ressources protégées qui reçoivent un jeton doivent toujours le vérifier. Elles devraient le faire par Introspection, et devraient toujours transmettre le paramètre ’requester_ip’ pour éviter de répondre à un malware.

Portée de l’autorisation (Scope)

  publié le par DnC

Les Scopes définissent l’étendue des droits (les actions permises sur les données de la ressource protégée) attachés au jeton d’accès qui est transmis par l’application cliente aux applications tierces (Serveurs de ressources). Celles-ci énumèrent les scopes attachés à l’autorisation ; elles ont la responsabilité d’agir en conséquence pour permettre ou non l’accès de l’application cliente aux données protégées.

Il est important de noter que les droits portés par un scope ne dépendent que de la façon dont l’application tierce en tient compte (ou non). Autrement dit, l’application tierce doit être une application de confiance, parfaitement identifiable par l’utilisateur final. De même, l’utilisateur final doit être authentifié. OAuth Server by DnC s’attache à apporter des solutions innovantes pour la qualification des applications et des utilisateurs, ce qui constitue une grande part de sa valeur ajoutée.

Définition

L’attribut "scope" est défini dans la section 3.3 de la [RFC6749].

L’attribut "scope" est une liste de valeurs sensibles à la casse, délimitées par des espaces, indiquant la portée requise du jeton d’accès pour accéder à la ressource demandée.

Les valeurs "scope" sont définies par l’implémentation ; il n’y a pas de registre centralisé pour eux ; les valeurs autorisées sont définies par le serveur d’autorisation.

L’ordre des valeurs "scope" n’est pas significatif.

Dans certains cas, la valeur "scope" sera utilisée lors de la demande d’un nouveau jeton d’accès avec une étendue d’accès suffisante pour utiliser la ressource protégée.

L’utilisation de l’attribut "scope" est OPTIONNELLE.

L’attribut "scope" NE DOIT PAS apparaître plus d’une fois.

Deux cas de figure

La norme OAuth 2.0 (RFC 6749) distingue deux cas de figure dans lesquels les scopes seront utilisés différemment :

1. L’utilisateur final est propriétaire des données protégées, et c’est donc lui qui autorise l’accès à ses données par les applications clientes tierces. Pour cela il sélectionne les scopes parmi ceux qui lui sont présentés au moment de la procédure d’autorisation. Le serveur de ressource ne doit permettre l’accès qu’aux ressources autorisées en fonction des scopes sélectionnés. [1].

2. L’application cliente est elle-même propriétaire des données situées sur les serveurs de ressources : elle autorise l’utilisateur final à accéder à ces données en son nom propre. Dans ce cas, les scopes sont définis par l’application cliente [2]. Il est important de considérer que cela donne à l’application cliente le moyen de moduler les autorisations selon le statut ou le profil de l’utilisateur, ce qui est un mécanisme très puissant puisque cette modulation de l’autorisation est propagée aux applications tierces.

Voyez également Identity by DnC qui apporte une solution innovante pour moduler les scopes selon le profil des utilisateurs.

La norme OAuth 2.0 ne donne pas un jeu de scopes prédéfini

OAuth définit les scopes et les mécanismes attachés de façon très générale. Rappelons que OAuth sert de base à différents protocoles qui en font un usage différent selon les applications.

Un jeu de scopes peut être défini à la fois pour segmenter un ensemble de données en sous-ensembles (diviser un profil en identité, adresse postale, données personnelle etc.), ou pour définir les actions (lecture, écriture, suppression etc.) permises sur les données, ou ces deux usages simultanément.
On voit qu’ils dépendent de la nature des ressources et de l’utilisation que pourraient en faire les applications clientes. Les scopes peuvent donc être très variés.

Un serveur OAuth suivant la norme OAuth 2.0 (comme c’est le cas de OAuth Server by DnC) doit donc être totalement transparent à leur définition et à leur usage :
- les scopes sont définis par le concepteur du serveur de ressources,
- c’est au serveur de ressource d’assurer le respect des autorisations définies par les scopes accompagnant le jeton d’accès au moment de la requête,
- les scopes qu’une application cliente peut utiliser sont librement définis au moment de son inscription sur le serveur d’autorisation.

Ceci a des conséquences très importantes :

- Soit les scopes ont une définition très générale, et le serveur de ressource peut répondre à des requêtes en provenance d’application clientes qui lui sont totalement étrangères,

- Soit les scopes ont une signification plus précise, et les serveurs de ressources comme les applications clientes doivent avoir une connaissance mutuelle. Cela peut se produire dans le cadre d’un protocole particulier, dans un ensemble particulier d’applications ou dans un espace contrôlé par une organisation (corporate realm).

OpenID Connect : une définition ouverte des scopes

OpenID Connect est un protocole d’authentification construit sur OAuth.

Concernant les scopes, OpenID connect définit un jeu de scopes standards, adaptés à la fonction d’authentification de l’utilisateur final, mais permet également l’utilisation de scopes particuliers.

Notes

[1C’est souvent beaucoup plus simple que cela : l’application signale les scopes qu’elle demande, et l’utilisateur final termine la procédure si les données réclamées (claims) lui conviennent, ou il abandonne.

[2Ou plus généralement "par l’implémentation". En effet, le concepteur de l’application cliente définit les scopes en fonction de ce qu’elle attend et de ce qu’elle en fait. Mais les scopes autorisés pour une application cliente peuvent être utilisés ou non, assortis éventuellement d’un paramètre, en fonction des droits que l’on veut accorder à l’utilisateur final. Ceci peut aussi bien être déterminé par le serveur d’authentification au moment où l’on crée le jeton, en fonction de conditions extérieures à l’application cliente.

Définition des scopes et généralités sur leur utilisation par les applications

  (publié initialement le mercredi 6 décembre 2017) par DnC

La portée de l’autorisation est définie par les Scopes. C’est sans doute le point le plus ouvert du standard et donc un concept difficile à maîtriser pour le développeur d’une application utilisant OpenID Connect.

La possibilité de moduler les portées d’autorisation en fonction de l’application cliente et de l’utilisateur final est une fonctionnalité exceptionnellement intéressante d’OpenID Connect.

On voit souvent en OIDC un simple SSO, mais c’est passer à côté de toute la puissance du dispositif : le jeton d’identité JWT regroupe, de façon indissociable et infalsifiable, l’identité de l’utilisateur final, celle de l’application cliente et les portées d’autorisation.

OpenID Connect définit les scopes en de multiples endroits de la documentation d’OAuth 2.0, sans pourtant donner à ce sujet la synthèse qu’il mérite. La bibliothèque OAuth2 Server Library for PHP développée par Brent Shaffer met en oeuvre les scopes sans que l’auteur en présente la philosophie. Cependant, l’analyse du code de cette bibliothèque permet de compléter la spécification, de dégager les principales définitions et de proposer un concept d’utilisation.

Quatre niveaux de définition des scopes

1. Les Scopes pris en charge par le serveur (Supported scopes)

En plus des scopes réservés et non-réservés définis par OpenID Connect, l’administrateur définit les scopes particuliers utilisés par les applications. Ils sont inscrits dans le fichier de configuration du serveur (variable $supportedScopes).

Le scope ’basic’ est également inscrit dans la configuration en tant que scope par défaut.

Seuls ces scopes peuvent être utilisés, le contrôleur Authorize rejettant toute requête comprenant un scope absent de cette liste.

2. Les Scopes réservés (Reserved Scopes)

Les Scopes réservés jouent un rôle particulier [1]. Ils sont essentiellement utilisés par le serveur pour modifier le comportement des flux.

- openid : ce scope est obligatoire pour que l’application puisse fonctionner avec OpenID Connect. S’il est le seul scope demandé, l’identification de l’utilisateur final se réduira au claim sub.

- offline_access : Les demandes d’accès d’OpenID Connect incluent le jeton d’actualisation uniquement si la portée offline_access a été demandée et accordée.

- sli : ce scope est obligatoire pour que l’application accepte l’identification unique (Single Login Identification, SLI) et la ré-authentification silencieuse (Silent Re-Authentication, SRA) telles que nous les avons implémentées dans OAuthSD.

- kerberos : ce scope autorise la création d’une session SLI d’OAuthSD à partir de l’authentification avec Kerberos.

Notes :
- Les deux premiers scopes sont définis par le standard OAuth 2.0, tandis que les deux derniers sont introduits par OAuthSD.
- Les scopes réservés ne donnent pas lieu à consentement de la part de l’utilisateur final, à l’exception du scope offline_access.

3. Les Scopes disponibles pour une application (Available scopes)

Ces scopes sont définis au moment de l’inscription d’une application sur le serveur. Ils comprennent :
- tout ou partie des scopes réservés,
- des scopes non-réservés (Non Reserved Scopes), qui sont définis par OpenID Connect ou définis à l’initiative du concepteur de l’application [2]

Par la suite, une application ne pourra utiliser d’autres scopes que ceux qui auront été inscrits ainsi. En particulier, le contôleur Authorize vérifie dès le début du processus que les scopes demandés dans la requête figurent bien dans la liste des Scopes disponibles pour l’application (Available scopes).

4. Les portées accordées (Granted scopes)

Une application transmet les scopes dans la demande d’autorisation (appel au point de terminaison Authorize). Ils doivent figurer dans la liste des Scopes disponibles pour une application.

Certains de ces scopes sont des demandes d’autorisation (Requested scopes). Une bonne compréhension du sujet nécessite de distinguer deux positions de l’utilisateur final par rapport au propriétaire des données, comme défini par la norme OAuth 2.0 (voir Portée de l’autorisation (Scope)). En résumé, il faut considérer les scopes selon deux cas de figure [3] :
- soit les données protégées sont la propriété de l’utilisateur, et les scopes doivent être compris comme une demande d’accès de l’application à ces données : l’application devra obtenir l’accord de l’utilisateur pour poursuivre,
- soit l’application (au sens large) "possède" les données protégées, et les scopes doivent être compris comme une portée accordée par l’application.

Dans le deuxième cas (si les données appartiennent à l’application) une application peut moduler les privilèges accordés à l’utilisateur en fonction du profil de ce dernier qui est traduit par les scopes.

Si l’application ne prévoit pas de moduler les privilèges accordés, le concepteur peut utiliser les scopes autorisés ou le scope par défaut.

Une fois l’authentification réalisée (et, si nécessaire, les scopes ayant été acceptés par l’utilisateur final), les scopes deviennent des portées accordées (granted scopes).

OAuthSD : scope "privileges"

Ce scope est particulier à OAuthSD dans le cadre des flux d’OpenID Connect.

Le scope"privileges" comporte les déclarations (claims) "scope" et "profile" [4] qui correspondent aux champs éponymes de la table users.

Il revient à une application tierce de déterminer les valeurs à attribuer au claim "scope". Pour cela, OAuthSD offre deux techniques d’interface :
- Incorporer au jeton JWT des déclarations supplémentaires,
- Utiliser un service HTTP Rest.

Si une application présente le scope "privileges" au contrôleur Authorize, les déclarations "profile" et "scope" seront incorporées aux données UserInfos. Dans cette optique, il s’agit d’une portée accordée (Granted Scope) au même titre que les portées "basic", "email" etc. qui contrôlent l’accès aux données de la table users.

En plus de ce comportement standard, ces déclarations seront incorporées à la charge utile du jeton d’identité.
Ainsi l’identité de l’application, de l’utilisateur final et de ses portées d’autorisation est parfaitement établi par la signature du jeton JWT.
 [5]

Utilisation du scope "privileges" claim "scope" pour l’attribution de privilèges dans les applications

OAuthSD regroupe les informations sur l’utilisateur final dans la table users. Le champ ’scope’ est du type texte et peut donc accueillir toute sorte de données pour décrire le profil de l’utilisateur ou donner la liste de ses privilèges. Par exemple, on pourra créer un objet de données ’profiler’ et le stocker au format Json.

En complément de cette technique, OAuthSD permet d’Incorporer au jeton JWT des déclarations supplémentaires.

Rappelons que le serveur d’authentification est transparent par rapport aux portées (scopes) non réservées et aux déclarations (claims) "non standard" : c’est à la ressource protégée de définir ce qui doit en être fait en fonction d’une application donnée.

Notes

[1Tellement particulier qu’ils n’ont rien à voir avec une portée d’autorisation, mais nous apparaissent plutôt comme un moyen de paramétrer les flux. Il y a peut-être là un défaut conceptuel d’OpenID Connect ?

[2Jusqu’à quel point étendre la définition des scopes ? Nous discutons la question ici : /web/-OpenID-Connect-UserInfo-.html. Facebook définit une centaine de scopes ! Mais, dans la perspective du contrôle de l’accès aux web-services et aux API, l’utilisation des scopes peut aller beaucoup plus loin que la seule modulation de Userinfo. Google fait un usage étendu des scopes pour autoriser l’accès aux différentes API. GitHub utilise les scopes pour moduler les accès des utilisateurs aux différentes fonctionnalités. Il s’agit là d’applications propriétaire et donc d’une implémentation très particulière d’OpenID Connect qui montre deux choses : OpenID connect est beaucoup plus qu’un SSO ; A l’opposé de l’idée d’un système d’authentification universel (permettant l’accès à des applications quelconque avec un seul identifiant), le contrôle de l’accès aux applications et au données ne peut se faire que dans un système propriétaire.

[3Comme prévu par la norme : voir Portée de l’autorisation (Scope).

[4Notez qu’il faut distinguer le claim "profile" et le scope "profile". La définition des scopes est bien embrouillée !

[5Non stable

Demandes de consentement sur les portées d’autorisation (scopes)

  publié le par DnC

Le règlement général sur la protection des données (RGPD) impose une gestion rigoureuse des données personnelles ce qui implique de demander à leur propriétaire l’autorisation d’y accéder.
Quels sont les cas de figure à distinguer ?
Comment gérer les demandes de consentement sur les portées d’autorisation à bon escient, sans lourdeur ni répétition inutile ?


Demande de consentement : différents cas de figure

Dès leur origine, OAuth et OpenID Connect ont été pensés pour protéger les données sensibles appartenant à l’utilisateur final, à qui on demande l’autorisation d’y accéder. Le vocabulaire utilisé et les exemples donnés dans les spécifications sont fortement orientés par cette vision.

Cependant, la réalité des applications ne se limite pas à ce cas de figure : l’utilisateur de l’application peut être distinct du propriétaire des données. Heureusement, OpenID Connect offre un ensemble de techniques qui permettent de s’assurer que l’utilisateur final est bien celui qu’il prétend être et autoriser son accès à des ressources protégées ou des applications qui contiennent des données sensibles, lui appartenant ou non.

Toute la problématique du consentement réside dans la position de l’utilisateur final par rapport au propriétaire des données, comme défini par la norme OAuth 2.0 (voir Portée de l’autorisation (Scope)).

A - L’utilisateur final est connecté à une application qui travaille sur ses données personnelles.

C’est la situation qui a orienté la description d’OAuth et OpenID dès leur origine et qui imprègne fortement OpenID Connect avec les données UserInfo. Celles-ci sont segmentées par portée (scope) de façon à n’accéder qu’aux données nécessaires à un moment donné. La spécification permet de définir de nouveaux scopes et d’étendre les données personnelles.

Dans ce cas, il convient de présenter à l’utilisateur une demande de consentement pour chaque portée d’autorisation [1].

B - L’utilisateur final est connecté à une application à laquelle il fournit des informations personnelles auxquelles des tiers accéderont ultérieurement.

Il s’agit donc d’une application à laquelle des tiers authentifiés peuvent se connecter avec un rôle déterminé, leur permettant de consulter ou exploiter les données personnelles.

Ce cas peut être traité de la façon suivante :

B1 - Il est présenté à l’utilisateur final propriétaire des données un avertissement au moment où il entre dans l’application, libellé en tenant compte des scopes que l’application est susceptible d’utiliser.

B2 - Il est présenté une demande d’autorisation au moment où l’utilisateur introduit ses données personnelles dans l’application.

Nous pourrions mentionner aussi la solution de facilité qui consiste à :

B0 - faire approuver, avant toute utilisation, des conditions générales qui évoquent de façon plus ou moins détaillée les traitements effectués sur les données personnelles. Parfois, cette façon de faire ne distingue même pas le propriétaire des autres utilisateurs. Elle peut également être utilisée pour s’octroyer la dispense d’appliquer le cas A.

L’idéal serait de demander l’autorisation d’accès au cours de l’utilisation de l’application par un tiers : "Bonjour M. Dupont. M. Tournesol souhaite accéder à vos données scope1, scope2 etc. avec l’application Truc."

On pourrait imaginer :

B3 - un échange d’E-mail, avec toute la difficulté liée aux délai de réponse, voire à la non-réponse. On imagine difficilement faire cela au cours de chaque session. Il serait plus praticable de le faire périodiquement, ou lorsqu’une nouvelle application ou un nouvel agent souhaite accéder aux données.

B4 - Une application dans laquelle les deux parties sont connectées. C’est le cas d’une application à laquelle le propriétaire des données se connecte et échange avec un agent (par exemple un chat avec un conseiller) qui a besoin d’accéder aux données personnelles au cours de la conversation. Les demandes de consentement pourraient être présentées en temps réel à l’utilisateur final. C’est parfaitement possible, mais cela ne semble pas une pratique courante. Une difficulté réside dans le fait que l’on donnerait l’impression à l’utilisateur final que l’on s’interdirait d’accéder aux données personnelles en dehors de cette procédure.

La mise en oeuvre des demandes de consentement par OAuthSD

La plupart des cas de figure ci-dessus devront être traités différemment selon l’application. Tel quel, OpenID Connect offre les moyens de gérer ces situations au prix de nombreux aller-retours entre le serveur et l’application cliente, à laquelle il appartiendra de développer la logique.

Un des objectifs d’OAuthSD est de simplifier l’écriture des applications clientes en intégrant le plus possible de fonctionnalités dans le serveur d’authentification/autorisation.
Le cas A est le cas standard auquel OAuthSD répond conformément aux spécifications. Sans prétendre prendre totalement en charge chacun des autres cas présentés ci-dessus, OAuthSD offre des facilités décrites dans ce qui suit.

La gestion "standard" des consentements

La spécification d’OpenID Connect permet de traiter pleinement le cas A et offre des outils pour les autres cas.

Le paramètre d’URL scope détermine quelles sont les portées d’autorisation demandées par l’application cliente.
Seuls les scopes non-réservés, auxquels s’ajoute le scope offline_access, feront l’objet d’une demande de consentement (les autres ont un caractère technique).

Notes :
- La notion de scope est un fourre-tout décrit ici : Définition des scopes et généralités sur leur utilisation par les applications.

La présentation de la demande de consentement dépend du paramètre d’URL prompt présent dans la requête passée au contrôleur Authorize [2].
- Si prompt est égal à ’none’ ou ’login’, aucun consentement n’est demandé à l’utilisateur final.
- Si prompt est omis [3], ou est égal à ’consent’ ou ’login consent’, les demandes de consentement sont présentées à l’utilisateur final.

Une bonne pratique de la gestion des consentements consiste à attendre pour les demander que l’application ait besoin d’accéder aux données correspondantes, et non systématiquement dès l’entrée dans l’application. Ceci se fait en appelant le contrôleur Authorize avec le paramètre prompt = ’consent’.
Cependant, si l’utilisateur n’est pas ou plus connecté à ce moment, il y aura un retour d’erreur.

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

Notes

[1Faut-il demander à l’utilisateur de donner son consentement globalement pour tous les scopes qui lui sont présentés ou lui permettre de les approuver un à un ? Nous pensons que les scopes doivent être présentés à l’approbation juste au moment où l’application cliente a besoin d’accéder aux données correspondantes. A ce moment, refuser un scope parmi une liste de plusieurs entraîne le non fonctionnement de l’application, donc une approbation différenciée n’a pas de sens. Dans le formulaire d’approbation, OAuthSD ne présente donc que l’option d’accepter ou refuser globalement la liste présentée.

[3Ce comportement par défaut peut être modifié dans la configuration.

Contrôle d’accès HTTP (CORS)

  publié le par DnC

Les requêtes HTTP de type multi-origines (Cross-Origin Request) sont des requêtes pour des ressources localisées sur un domaine différent de celui qui est à l’origine de la requête.

C’est exactement la configuration dans laquelle nous nous trouvons quand nous voulons appliquer les protocoles d’authentification à la communication entre applications réparties sur Internet.

Du fait de nombreux abus (attaques de type cross-site request forgery ou CSRF), les requêtes HTTP croisées ont été sujettes à des restrictions pour des raisons de sécurité bien compréhensibles.

Le but de cet article et d’expliquer comment franchir ces restrictions en toute légitimité et sécurité dans le cadre de la mise en oeuvre du service OAuth Server by DnC.

Références :
- Contrôle d’accès HTTP
- Cross-Origin Resource Sharing
- Les attaques de type cross-site request forgery

Différents cas de figure

Comme toujours lorsque nous abordons la question de la communication entre applications réparties sur différents domaines, nous devons d’abord distinguer deux cas :
- les serveurs de ressources protégées (RS) sont interrogés par des applications clientes identifiées à l’avance ; c’est toujours le cas lorsque les applications et les RS appartiennent à une même organisation ;
- les RS n’ont pas une connaissance préalable des applications.

Le premier cas est trivial. L’autorisation des requêtes multi-origines (Cross-Origin Request) [1] pourra être donnée, par exemple si le service est publié par un serveur http Apache ou compatible, dans le fichier .htaccess du RS :

##### CORS pour monsite.com :
SetEnvIf Origin "^http(s)?://(.+\.)?monsite\.com$" origin_is=$0
Header always set Access-Control-Allow-Origin %{origin_is}e env=origin_is

Seul le deuxième cas mérite notre attention.

Autorisation après validation du jeton d’accès

Dans le deuxième cas (l’application cliente n’est pas connue du RS) notre solution consiste à inscrire le champ Access-Control-Allow-Origin dans la réponse du serveur de ressource (RS) si, et seulement si, le jeton a été validé auprès du serveur d’autorisation. Cela semble la bonne méthode, puisque le RS s’en remet au serveur d’autorisation pour réaliser toutes les vérifications nécessaires, et n’a donc pas besoin de connaître au préalable le domaine d’origine de la requête [2] [3].

Voici un exemple de fonction d’autorisation en PHP pour SPIP 3.1, dans laquelle le RS valide le jeton par introspection (voir : Validation du jeton d’accès par interrogation du serveur d’autorisation (Introspection)) :

  1. /*
  2. Autorisation avec OAuth Server by DnC
  3. Auteur : Bertrand degoy
  4. Copyright (c) 2016 DnC
  5. */
  6.  
  7. function oauth_authorize($accesstoken) {
  8.  
  9.     if ( !empty( $idtoken ) ) {
  10.  
  11.         // Interroger OAuth Server by DnC
  12.         include_spip('inc/distant');
  13.         // Vérifier le jeton d'identité (ID token)
  14.         $url = "https://oa.dnc.global/introspect.php?token=" .  
  15.                  $idtoken&requester_ip=$_SERVER['REMOTE_ADDR'];
  16.         $res = recuperer_url($url);  
  17.         if ( (int)$res['status'] === 200 ) {
  18.             if ( isset($_SERVER["HTTP_ORIGIN"]) ) {
  19.                 // Accès HTTP (CORS) : autoriser l'origine
  20.                 $issuer = trim(strtr($_SERVER["REMOTE_ADDR"], '<>"\'', '[]##'));    
  21.                 header('Access-Control-Allow-Origin', $issuer);
  22.             }
  23.             return true ;
  24.         }
  25.  
  26.     } else return false;
  27.  
  28. }    

Télécharger

Sécurité :
Notez que l’IP $_SERVER[’REMOTE_ADDR’] est passée à l’introspection par le paramètre ’requester_ip’ et sera comparée à lIP de l’application cliente enregistrée sur le serveur. Il n’y a donc pas de risque de répondre à une application étrangère. Voyez : Vérification de l’origine de la requête reçue par un serveur de ressource.

Tester :
http://chemindeleau.com/?page=test-rpc-3 (utilise la fonction ProtectedApi_GET_auto() décrite ici : Une fonction pour tout simplifier !).

Cet exemple s’appuie sur l’API HTTP REST de Radar. Le site Chemin de l’eau est un site opérationnel qui met en œuvre des fonctionnalités issues des prototypes OAuthSD et Radar.

Message d’erreur : Blocage d’une requête multi-origines

"Blocage d’une requête multi-origines (Cross-Origin Request) : la politique « Same Origin » ne permet pas de consulter la ressource distante située sur http://r.dnc.global/http.api/collectionjson/gis?lat=47.8655638&lon=5.3314162&dmax=10&token=. Raison : échec du canal de pré-vérification des requêtes CORS."

La requête suivante, bien que le type indiqué soit ’GET’, est négociée avec la méthode ’OPTIONS’. Il en résulte que la requête cross-site est de type requête prévérifiée ("preflighted requests") :

  1. <div id="result"></div>
  2.  
  3. <script>
  4.  
  5.     $.ajax({
  6.        
  7.         url: 'http://r.dnc.global/http.api/collectionjson/gis',
  8.         dataType: 'json',
  9.         type: 'GET',
  10.         contentType: 'application/json',
  11.         data: 'lat=47.8655638&lon=5.3314162&dmax=10&token=#SESSION{auth_token}',
  12.         timeout: 15000,
  13.         success: function(data) {
  14.             var text = '<b>Requete : ' + data.collection.href + '</b><br /><br />';
  15.             items = data.collection.items;
  16.             for (var i=0; i < items.length; i++ ) {
  17.                 for (var j=0; j < items[i].data.length; j++ ) {
  18.                     text += items[i].data[j].name + '=' + items[i].data[j].value + "<br />";
  19.                 }
  20.                 text += "<br />";    
  21.             }
  22.             $("#result").html(text);
  23.  
  24.         },
  25.         error: function(oops) {
  26.             // oops.statusText returns error
  27.             $("#result").html(oops.statusText);
  28.              
  29.         }
  30.     });
  31.  
  32. </script>

Télécharger

Cliquez sur la vignette pour voir la requête et la réponse :

(Article à compléter. Mais est-ce pertinent ? L’utilisation de script expose le jeton, ce n’est pas la méthode recommandée).

Notes

[1La traduction française "multi-origines" est inexacte : la requête n’a évidemment qu’une seule origine, mais celle-ci est indéfinie vue du RS, et en tous cas issue d’un domaine différent du sien.

[2Voici une démonstration claire de la différence entre autorisation et authentification : le serveur de ressource va répondre sans rien savoir a priori de l’identité de l’application cliente ni de l’utilisateur final. OpenID Connect permet de réaliser l’authentification, donc de répondre (ou non) en fonction de l’identité de l’application, de l’utilisateur final, voire des portées d’autorisation (scopes)

[3Ceci ne veut pas dire que le RS répond à n’importe quelle application qui présente le jeton, voir : Vérification de l’origine de la requête reçue par un serveur de ressource.

Q&R pour les auteurs et développeurs

  publié le par DnC

L’Auteur est un utilisateur privilégié de OAuth Server. Il est notamment propriétaire d’une ou plusieurs applications clientes qu’il a inscrites sur le serveur à l’aide de ce site. Il est un développeur ou en possède les connaissances.

OAuthSD est-il compatible OPenID Connect ?

OAuthSD a pour ambition de se conformer aux spécifications de l’IETF telles qu’elles sont publiées par l’OPenID Foundation.
OAuthSD est 100% conforme en ce qui concerne le flux Authentication Code, voyez : OAuthSD vers la Certification OpenID ®.
Notez que :
- OAuthSD ne met pas en œuvre certains flux, voyez : OpenID Connect : Synthèse des flux d’autorisation (Grant Type).
- OAuthSD évolue vite et de nouveaux tests de conformité seront effectués,
- OAuthSD impose certaines restrictions qui vont dans le sens de la sécurité ; ainsi, le paramètre state est obligatoire dans les requêtes à Authorize, alors que la spécification ne l’exige pas.
- OAuthSD offre des fonctionnalités étendues qui, cependant, ne contredisent en rien les spécifications. Il en est ainsi du mécanisme SLI : les spécifications d’OIDC définissent tout ce qu’il faut pour la ré-authentification silencieuse (notamment avec le paramètre prompt=’none’), mais cela resterait à traiter par allers-retours entre le serveur et l’application cliente. OAuthSD traite cela au niveau du contrôleur Authorise à l’aide du cookie SLI, évitant aux développeurs un fastidieux travail d’adaptation des applications.
- OAuthSD apporte quelques ajouts à la spécification, sans influence sur la conformité. Ainsi, le contrôleur Authorize peut être interrogé en Ajax et retourner le temps de session restant. Voyez : Monitoring de l’état de l’authentification et SLO, Avertir de la fin de session OIDC et la prolonger.

Qu’est-ce qu’apporte OAuthSD en matière de sécurité ?

Les spécifications d’OIDC ne prennent pas en compte certaines menaces. Par exemple l’attaque "Broken End-User Authentication" n’est pas prise en compte par les spécifications, la preuve en est le fait que le paramètre state n’est pas obligatoire.
L’accent est mis sur l’éviction d’application cliente ou de user-agent douteux. Par exemple, il y a aujourd’hui 12 tests dans le controleur Authorize permettant d’éliminer des requêtes douteuses.

Dans le domaine de la sécurité, OAuthSD n’est pas seulement un serveur d’authentification mais un superviseur.

Par exemple, dans les pages de la rubrique "Surveiller", l’historisation des incidents est datée à mieux que la microseconde afin de faciliter le croisement des informations. Il faut pour cela que les autres historiques soient aussi à la microseconde [1].

OAuthSD comporte également un dispositif d’alerte de l’administrateur par E-mail ou SMS, ainsi qu’un interface pour le suivi et l’alerte avec PRTG.

L’historique des événements est dans un format compatible syslog afin de permettre leur traitement par des applications de détection d’intrusion, de suivi et d’alerte.

On peut lire des critiques sur certains flux, pourquoi les mettre en œuvre ?

Il faut considérer qu’un serveur OIDC est aussi sécurisé que le flux le moins sécurisé qu’il implémente.
Une caractéristique étonnante d’OAuth 2.0 est d’offrir des flux très peu sécurisés par rapport au flux Autorisation avec Code. Encore celui-ci n’est-il vraiment complet que dans sa version OpenID Connect (pas OAuth 2.0) qui met en œuvre le jeton d’identité.
Ouvrir la possibilité que le serveur réponde à des requêtes très diverses (telles que les flux implicites et hybrides) sans contrôler l’application cliente peut être considéré comme une faille de sécurité [2] !
Nous souhaitons mettre à disposition un serveur 100% compatible OpenID Connect. En même temps, les développeurs et administrateurs doivent être conscients qu’exposer le serveur aux flux implicites et hybrides est une faille de sécurité. Pour cette raison, une constante de configuration permet d’autoriser l’usage ou non du flux implicite. On pourrait ainsi ne répondre qu’aux requêtes de types Authorization Code et Autorisation de Serveur à serveur.

Peut-on tout faire avec le flux Authorization Code ?

Nous pensons que oui, voyez par exemple : Applications natives : un exemple avec Windev.
Le flux Client Credentials est également intéressant pour l’accès d’une application aux ressources protégées. Mais il est possible de se servir du flux Authorization Code pour cela, voyez : OpenID Connect : Exemples complets du flux d’Autorisation via un code puis requête UserInfo.
Cela reste un point ouvert à la discussion.

Que signifie "Il existe des informations complémentaires, connectez vous pour les voir" ?

Lorsque ce message apparait en bas d’un article, cela signifie qu’il existe des informations réservées aux utilisateurs privilégiés de ce site. Si vous avez les droits d’écriture sur la rubrique, vous serez autorisés à voir ce texte après vous être connecté.

Notes

[1Le projet est que le noyau du serveur OIDC sera encapsulé dans un composant Docker dans lequel il y aura également la base de données et un firewall applicatif. Ce dernier sera couplé aux événements d’OIDC pour réagir immédiatement.

[2C’est peut-être la raison pour laquelle vous pouvez lire ici : "Cependant, l’accent étant mis sur l’interopérabilité, l’accent a été mis sur les problèmes de sécurité autour de la mise en œuvre".

Documents de référence

  publié le par DnC

Documents essentiels

Synthèse : OAuth 2.0
Code : Brent Shaffer’s OAuth Server PHP

Documents de l’IETF

-  RFC 6749 L’architecture d’authentification OAuth 2.0.
L’architecture d’autorisation OAuth 2.0 permet à une application tierce d’obtenir un accès limité à un service HTTP, soit au nom d’un propriétaire de ressource en orchestrant une interaction d’approbation entre le propriétaire de la ressource et le service HTTP, soit en autorisant l’application tierce à obtenir l’accès en son propre nom.

- RFC 7523 JSON Web Token (JWT) Profil pour les soumissions d’authentification et d’autorisation de client OAuth 2.0.
Cette spécification définit l’utilisation d’un JSON Web Token (JWT) Bearer Token comme moyen de demander un jeton d’accès OAuth 2.0 ainsi que pour l’authentification du client.

- RFC 7516 JSON Web Encryption (JWE)
JSON Web Encryption (JWE) représente du contenu chiffré basé sur JSON. Les algorithmes cryptographiques et les identificateurs à utiliser avec cette spécification sont décrits dans le document JSON Spécification des Algorithmes Web (JWA) et registres IANA définis par cette spécification. En relation, la signature numérique et les capacités du Code d’Authentification de Message (MAC) sont décrites dans le document séparé Spécification JSON Web Signature (JWS).

- RFC 7515 JSON Web Signature (JWS)
JSON Web Signature (JWS) représente du contenu sécurisé par signature numérique ou des codes d’authentification de message (MACs) utilisant des structures de données basées sur JSON. Les algorithmes cryptographiques et les identificateurs à utiliser avec cette spécification sont décrits dans le document JSON Web Algorithmes (JWA) et un registre IANA défini par la spécification. Les capacités de cryptage associées sont décrites par la spécification distincte JSON Web Encryption (JWE).

- RFC 7662 OAuth 2.0 Token Introspection
Cette spécification définit une méthode permettant à une ressource protégée d’interroger un serveur d’autorisation OAuth 2.0 pour déterminer l’état actif d’un jeton OAuth 2.0 et pour déterminer des méta-informations sur ce jeton.
Les déploiements OAuth 2.0 peuvent utiliser cette méthode pour transmettre à la ressource protégée des informations sur le contexte d’autorisation du jeton à partir du serveur d’autorisation.

-  RFC 7009 OAuth 2.0 Token Revocation.
Ce document propose un point d’extrémité supplémentaire pour le serveur d’autorisation OAuth, qui permet aux clients d’avertir le serveur d’autorisation qu’un jeton de rafraîchissement ou d’accès précédemment obtenu n’est plus nécessaire. Cela permet au serveur d’autorisation de nettoyer les données de sécurité. Une demande de révocation invalidera le jeton en question et, le cas échéant, d’autres jetons basés sur la même autorisation.

OpenID Connect

La référence : OpenID Connect.

OpenID Connect Core 1.0 incorporating errata set 1
OpenID Connect 1.0 est une couche d’identité simple fondée sur le protocole OAuth 2.0. Il permet aux clients de vérifier l’identité de l’utilisateur final en fonction de l’authentification effectuée par un serveur d’autorisation, ainsi que d’obtenir des informations de profil de base sur l’utilisateur final de manière interopérable et REST.
Cette spécification définit la fonctionnalité de base OpenID Connect : authentification construite sur OAuth 2.0 et l’utilisation de revendications pour communiquer des informations sur l’utilisateur final. Il décrit également les considérations de sécurité et de confidentialité pour l’utilisation d’OpenID Connect.

OpenID Connect Basic Client Implementer’s Guide 1.0 - draft 37
OpenID Connect 1.0 est une couche d’identité simple fondée sur le protocole OAuth 2.0. Il permet aux clients de vérifier l’identité de l’utilisateur final en fonction de l’authentification effectuée par un serveur d’autorisation, ainsi que d’obtenir des informations de profil de base sur l’utilisateur final de manière interopérable et REST.
Ce Guide d’implémentation OpenID Connect Basic Client 1.0 contient un sous-ensemble de la spécification OpenID Connect Core 1.0 conçue pour être facile à lire et à implémenter pour les échanges entre applications Web basés sur le flux d’autorisation OAuth. Ce document double intentionnellement le contenu de la spécification Core pour fournir un guide d’implémentation autonome pour des développements d’applications Web élémentaires utilisant le flux de code d’autorisation OAuth.
Les fournisseurs OpenID et les applications non Web doivent plutôt consulter la spécification Core.

Documents de Microsoft Exchange

Exchange 2013 utilise un jeton JWT (JSON Web Token) pour le jeton d’identité. La documentation technique d’Exchange appelle "Jeton d’identité" quelque chose de très similaire à l’ID Token défini par OpenID Connect. Il est donc intéressant de consulter les documents suivants :

- Présentation du jeton d’identité Exchange

- Utilisation de PHP pour valider un jeton d’identité