mirror of
				https://gitlab.crans.org/bde/nk20
				synced 2025-11-04 01:12:08 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			287 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
			
		
		
	
	
			287 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
OAuth2
 | 
						|
======
 | 
						|
 | 
						|
L'authentification `OAuth2 <https://fr.wikipedia.org/wiki/OAuth>`_ est supportée par la
 | 
						|
Note Kfet. Elle offre l'avantage non seulement d'identifier les utilisateur⋅rices, mais
 | 
						|
aussi de transmettre des informations à un service tiers tels que des informations
 | 
						|
personnelles, le solde de la note ou encore les adhésions de l'utilisateur⋅rice, en
 | 
						|
l'avertissant sur quelles données sont effectivement collectées. Ainsi, il est possible
 | 
						|
de développer des appplications tierces qui peuvent se baser sur les données de la Note
 | 
						|
Kfet ou encore faire des transactions.
 | 
						|
 | 
						|
 | 
						|
Configuration du serveur
 | 
						|
------------------------
 | 
						|
 | 
						|
On utilise ``django-oauth-toolkit``, qui peut être installé grâce à PIP ou bien via APT,
 | 
						|
via le paquet ``python3-django-oauth-toolkit``.
 | 
						|
 | 
						|
On commence par ajouter ``oauth2_provider`` aux applications Django installées. On
 | 
						|
n'oublie pas ni d'appliquer les migrations (``./manage.py migrate``) ni de collecter
 | 
						|
les fichiers statiques (``./manage.py collectstatic``).
 | 
						|
 | 
						|
On souhaite que l'API gérée par ``django-rest-framework`` puisse être accessible via
 | 
						|
l'authentification OAuth2. On adapte alors la configuration pour permettre cela :
 | 
						|
 | 
						|
.. code:: python
 | 
						|
 | 
						|
   REST_FRAMEWORK = {
 | 
						|
       'DEFAULT_AUTHENTICATION_CLASSES': [
 | 
						|
           'rest_framework.authentication.SessionAuthentication',
 | 
						|
           'rest_framework.authentication.TokenAuthentication',
 | 
						|
           'oauth2_provider.contrib.rest_framework.OAuth2Authentication',
 | 
						|
           ...
 | 
						|
       ],
 | 
						|
       ...
 | 
						|
   }
 | 
						|
 | 
						|
On a ensuite besoin de définir nos propres scopes afin d'avoir des permissions fines :
 | 
						|
 | 
						|
.. code:: python
 | 
						|
 | 
						|
   OAUTH2_PROVIDER = {
 | 
						|
       'SCOPES_BACKEND_CLASS': 'permission.scopes.PermissionScopes',
 | 
						|
       'OAUTH2_VALIDATOR_CLASS': "permission.scopes.PermissionOAuth2Validator",
 | 
						|
       'REFRESH_TOKEN_EXPIRE_SECONDS': timedelta(days=14),
 | 
						|
       'PKCE_REQUIRED': False,
 | 
						|
       'OIDC_ENABLED': True,
 | 
						|
       'OIDC_RSA_PRIVATE_KEY':
 | 
						|
           os.getenv('OIDC_RSA_PRIVATE_KEY', '/var/secrets/oidc.key'),
 | 
						|
       'SCOPES': { 'openid': "OpenID Connect scope" },
 | 
						|
   }
 | 
						|
 | 
						|
Cela a pour effet d'avoir des scopes sous la forme ``PERMISSION_CLUB``,
 | 
						|
et de demander des scopes facultatives (voir plus bas).
 | 
						|
Un jeton de rafraîchissement expire de plus au bout de 14 jours, si non-renouvelé.
 | 
						|
 | 
						|
On ajoute enfin les routes dans ``urls.py`` :
 | 
						|
 | 
						|
.. code:: python
 | 
						|
 | 
						|
   urlpatterns.append(
 | 
						|
        path('o/', include('oauth2_provider.urls', namespace='oauth2_provider'))
 | 
						|
    )
 | 
						|
 | 
						|
Enfin pour utiliser OIDC, il faut générer une clé privé que l'on va, par défaut,
 | 
						|
mettre dans `/var/secrets/oidc.key` :
 | 
						|
 | 
						|
.. code:: bash
 | 
						|
 | 
						|
   cd /var/secrets/
 | 
						|
   openssl genrsa -out oidc.key 4096
 | 
						|
 | 
						|
L'OAuth2 est désormais prêt à être utilisé.
 | 
						|
 | 
						|
 | 
						|
Configuration client
 | 
						|
--------------------
 | 
						|
 | 
						|
Contrairement au `CAS <cas>`_, n'importe qui peut créer une application OAuth2.
 | 
						|
 | 
						|
Pour créer une application, il faut se rendre à la page
 | 
						|
`/o/applications/ <https://note.crans.org/o/applications/>`_. Dans ``client type``,
 | 
						|
rentrez ``public`` (ou ``confidential`` selon vos choix), et vous rentrerez
 | 
						|
généralement ``authorization-code`` dans ``Authorization Grant Type``.
 | 
						|
Le champ ``Redirect Uris`` contient une liste d'adresses URL autorisées pour des
 | 
						|
redirections post-connexion.
 | 
						|
 | 
						|
Il vous suffit de donner à votre application :
 | 
						|
 | 
						|
* L'identifiant client (client-ID)
 | 
						|
* La clé secrète
 | 
						|
* Les scopes, qui peuvent être récupérées sur cette page : `<https://note.crans.org/permission/scopes/>`_
 | 
						|
* L'URL d'autorisation : `<https://note.crans.org/o/authorize/>`_
 | 
						|
* L'URL d'obtention de jeton : `<https://note.crans.org/o/token/>`_
 | 
						|
* Si besoin, l'URL de récupération des informations de l'utilisateur⋅rice : `<https://note.crans.org/api/me/>`_
 | 
						|
 | 
						|
N'hésitez pas à consulter la page `<https://note.crans.org/api/me/>`_ pour s'imprégner
 | 
						|
du format renvoyé.
 | 
						|
 | 
						|
.. warning::
 | 
						|
 | 
						|
   Un petit mot sur les scopes : tel qu'implémenté, une scope est une permission unitaire
 | 
						|
   (telle que décrite dans le modèle ``Permission``) associée à un club. Ainsi, un jeton
 | 
						|
   a accès à une scope si et seulement si læ propriétaire du jeton dispose d'une adhésion
 | 
						|
   courante dans le club lié à la scope qui lui octroie cette permission.
 | 
						|
 | 
						|
   Par exemple, un jeton pourra avoir accès à la permission de créer des transactions en lien
 | 
						|
   avec un club si et seulement si læ propriétaire du jeton est trésorièr⋅e du club.
 | 
						|
 | 
						|
   La vérification des droits de læ propriétaire est faite systématiquement, afin de ne pas
 | 
						|
   faire confiance au jeton en cas de droits révoqués à saon propriétaire.
 | 
						|
 | 
						|
   Vous pouvez donc contrôler le plus finement possible les permissions octroyées à vos
 | 
						|
   jetons.
 | 
						|
 | 
						|
.. danger::
 | 
						|
 | 
						|
   Demander des scopes n'implique pas de les avoir.
 | 
						|
 | 
						|
   Lorsque des scopes sont demandées par un client, la Note
 | 
						|
   va considérer l'ensemble des permissions accessibles parmi
 | 
						|
   ce qui est demandé. Dans vos programmes, vous devrez donc
 | 
						|
   vérifier les permissions acquises (communiquées lors de la
 | 
						|
   récupération du jeton d'accès à partir du grant code),
 | 
						|
   et prévoir un comportement dans le cas où des permissions
 | 
						|
   sont manquantes.
 | 
						|
 | 
						|
   Cela offre un intérêt supérieur par rapport au protocole
 | 
						|
   OAuth2 classique, consistant à demander trop de permissions
 | 
						|
   et agir en conséquence.
 | 
						|
 | 
						|
   Par exemple, vous pourriez demander la permission d'accéder
 | 
						|
   aux membres d'un club ou de faire des transactions, et agir
 | 
						|
   uniquement dans le cas où l'utilisateur⋅rice connecté⋅e
 | 
						|
   possède la permission problématique.
 | 
						|
 | 
						|
Avec Django-allauth
 | 
						|
###################
 | 
						|
 | 
						|
Si vous utilisez Django-allauth pour votre propre application, vous pouvez utiliser
 | 
						|
le module pré-configuré disponible ici :
 | 
						|
`<https://gitlab.crans.org/bde/allauth-note-kfet>`_. Pour l'installer, vous
 | 
						|
pouvez simplement faire :
 | 
						|
 | 
						|
.. code:: bash
 | 
						|
 | 
						|
   $ pip3 install git+https://gitlab.crans.org/bde/allauth-note-kfet.git
 | 
						|
 | 
						|
L'installation du module se fera automatiquement.
 | 
						|
 | 
						|
Il vous suffit ensuite d'inclure l'application ``allauth_note_kfet`` à vos applications
 | 
						|
installées (sur votre propre client), puis de bien ajouter l'application sociale :
 | 
						|
 | 
						|
.. code:: python
 | 
						|
 | 
						|
   SOCIALACCOUNT_PROVIDERS = {
 | 
						|
       'notekfet': {
 | 
						|
           # 'DOMAIN': 'note.crans.org',
 | 
						|
           'SCOPE': ['1_1', '2_1'],
 | 
						|
       },
 | 
						|
       ...
 | 
						|
   }
 | 
						|
 | 
						|
Le paramètre ``DOMAIN`` permet de changer d'instance de Note Kfet. Par défaut, il
 | 
						|
se connectera à ``note.crans.org`` si vous ne renseignez rien.
 | 
						|
 | 
						|
Le paramètre ``SCOPE`` permet de définir les scopes à demander.
 | 
						|
Dans l'exemple ci-dessous, les permissions d'accéder à l'utilisateur⋅rice
 | 
						|
et au profil sont demandées.
 | 
						|
 | 
						|
En créant l'application sur la note, vous pouvez renseigner
 | 
						|
``https://monsite.example.com/accounts/notekfet/login/callback/`` en URL de redirection,
 | 
						|
à adapter selon votre configuration.
 | 
						|
 | 
						|
Vous devrez ensuite enregistrer l'application sociale dans la base de données.
 | 
						|
Vous pouvez passer par Django-admin, mais cela peut nécessiter d'avoir déjà un compte,
 | 
						|
alors autant le faire via un shell python :
 | 
						|
 | 
						|
.. code:: python
 | 
						|
 | 
						|
   from allauth.socialaccount.models import SocialApp
 | 
						|
   SocialApp.objects.create(
 | 
						|
           name="Note Kfet",
 | 
						|
           provider="notekfet",
 | 
						|
           client_id="VOTRECLIENTID",
 | 
						|
           secret="VOTRESECRET",
 | 
						|
           key="",
 | 
						|
   )
 | 
						|
 | 
						|
Si vous avez bien configuré ``django-allauth``, vous êtes désormais prêts par à vous
 | 
						|
connecter via la note :) Par défaut, nom, prénom, pseudo et adresse e-mail sont
 | 
						|
récupérés. Les autres données sont stockées mais inutilisées.
 | 
						|
 | 
						|
 | 
						|
Application personnalisée
 | 
						|
#########################
 | 
						|
 | 
						|
Ce modèle vous permet de créer vos propres applications à interfacer avec la Note Kfet.
 | 
						|
 | 
						|
Commencez par créer une application : `<https://note.crans.org/o/applications/register>`_.
 | 
						|
Dans ``Client type``, choisissez ``Confidential`` si des informations confidentielles sont
 | 
						|
amenées à transiter, sinon ``public``. Choisissez ``Authorization code`` dans
 | 
						|
``Authorization grant type``.
 | 
						|
 | 
						|
Dans ``Redirect uris``, vous devez insérer l'ensemble des URL autorisées à être redirigées
 | 
						|
à la suite d'une autorisation OAuth2. La première URL entrée sera l'URL par défaut dans le
 | 
						|
cas où elle n'est pas explicitement indiquée lors de l'autorisation.
 | 
						|
 | 
						|
.. note::
 | 
						|
 | 
						|
   À des fins de tests, il est possible de laisser `<http://localhost/>`_ pour faire des
 | 
						|
   appels à la main en récupérant le jeton d'autorisation.
 | 
						|
 | 
						|
Lorsqu'un client veut s'authentifier via la Note Kfet, il va devoir accéder à une page
 | 
						|
d'authentification. La page d'autorisation est `<https://note.crans.org/o/authorize/>`_,
 | 
						|
c'est sur cette page qu'il faut rediriger les utilisateur⋅rices. Il faut mettre en paramètre GET :
 | 
						|
 | 
						|
* ``client_id`` : l'identifiant client de l'application (public) ;
 | 
						|
* ``response_type`` : mettre ``code`` ;
 | 
						|
* ``scope`` : l'ensemble des scopes demandés, séparés par des espaces. Ces scopes peuvent
 | 
						|
  être récupérés sur la page `<https://note.crans.org/permission/scopes/>`_.
 | 
						|
* ``redirect_uri`` : l'URL sur laquelle rediriger qui récupérera le code d'accès. Doit être
 | 
						|
  autorisée par l'application. À des fins de test, peut être `<http://localhost/>`_.
 | 
						|
* ``state`` : optionnel, peut être utilisé pour permettre au client de détecter des requêtes
 | 
						|
  provenant d'autres sites.
 | 
						|
 | 
						|
Sur cette page, les permissions demandées seront listées, et l'utilisateur⋅rice aura le
 | 
						|
choix d'accepter ou non. Dans les deux cas, l'utilisateur⋅rice sera redirigée vers
 | 
						|
``redirect_uri``, avec pour paramètre GET soit le message d'erreur, soit un paramètre
 | 
						|
``code`` correspondant au code d'autorisation.
 | 
						|
 | 
						|
Une fois ce code d'autorisation récupéré, il faut désormais récupérer le jeton d'accès.
 | 
						|
Il faut pour cela aller sur l'URL `<https://note.crans.org/o/token/>`_, effectuer une
 | 
						|
requête POST avec pour arguments :
 | 
						|
 | 
						|
* ``client_id`` ;
 | 
						|
* ``client_secret`` ;
 | 
						|
* ``grant_type`` : mettre ``authorization_code`` ;
 | 
						|
* ``code`` : le code généré.
 | 
						|
 | 
						|
À noter que le code fourni n'est disponible que pendant quelques secondes.
 | 
						|
 | 
						|
À des fins de tests, on peut envoyer la requête avec ``curl`` :
 | 
						|
 | 
						|
.. code:: bash
 | 
						|
 | 
						|
   curl -X POST https://note.crans.org/o/token/ -d "client_id=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&client_secret=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&grant_type=authorization_code&code=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
 | 
						|
 | 
						|
Le serveur renverra si tout se passe bien une réponse JSON :
 | 
						|
 | 
						|
.. code:: json
 | 
						|
 | 
						|
   {
 | 
						|
       "access_token": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
 | 
						|
       "expires_in": 36000,
 | 
						|
       "token_type": "Bearer",
 | 
						|
       "scope": "1_1 1_2",
 | 
						|
       "refresh_token": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
 | 
						|
   }
 | 
						|
 | 
						|
On note donc 2 jetons différents : un d'accès et un de rafraîchissement. Le jeton d'accès
 | 
						|
est celui qui sera donné à l'API pour s'authentifier, et qui expire au bout de quelques
 | 
						|
heures.
 | 
						|
 | 
						|
Il suffit désormais d'ajouter l'en-tête ``Authorization: Bearer ACCESS_TOKEN`` pour se
 | 
						|
connecter à la note grâce à ce jeton d'accès.
 | 
						|
 | 
						|
Pour tester :
 | 
						|
 | 
						|
.. code:: bash
 | 
						|
 | 
						|
   curl https://note.crans.org/api/me -H "Authorization: Bearer XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
 | 
						|
 | 
						|
En cas d'expiration de ce jeton d'accès, il est possible de le renouveler grâce au jeton
 | 
						|
de rafraichissement à usage unique. Il suffit pour cela de refaire une requête sur la page
 | 
						|
`<https://note.crans.org/o/token/>`_ avec pour paramètres :
 | 
						|
 | 
						|
* ``client_id`` ;
 | 
						|
* ``client_secret`` ;
 | 
						|
* ``grant_type`` : mettre ``refresh_token`` ;
 | 
						|
* ``refresh_token`` : le jeton de rafraîchissement.
 | 
						|
 | 
						|
Le serveur vous fournira alors une nouvelle paire de jetons, comme précédemment.
 | 
						|
À noter qu'un jeton de rafraîchissement est à usage unique.
 | 
						|
 | 
						|
N'hésitez pas à vous renseigner sur OAuth2 pour plus d'informations.
 |