OAuth2

L’authentification OAuth2 est supportée par la Note Kfet. Elle offre l’avantage non seulement d’identifier les utilisateurs, 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, 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 :

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 :

OAUTH2_PROVIDER = {
    'SCOPES_BACKEND_CLASS': 'permission.scopes.PermissionScopes',
    'OAUTH2_VALIDATOR_CLASS': "permission.scopes.PermissionOAuth2Validator",
    'REFRESH_TOKEN_EXPIRE_SECONDS': timedelta(days=14),
}

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 :

urlpatterns.append(
     path('o/', include('oauth2_provider.urls', namespace='oauth2_provider'))
 )

L’OAuth2 est désormais prêt à être utilisé.

Configuration client

Contrairement au CAS, n’importe qui peut créer une application OAuth2.

Pour créer une application, il faut se rendre à la page /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 :

N’hésitez pas à consulter la page https://note.crans.org/api/me/ pour s’imprégner du format renvoyé.

Avertissement

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 le/la 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 le propriétaire du jeton est trésorier du club.

La vérification des droits du propriétaire est faite systématiquement, afin de ne pas faire confiance au jeton en cas de droits révoqués à son 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 connecté 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 :

$ 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 :

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 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 :

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 utilisateurs. 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 aura le choix d’accepter ou non. Dans les deux cas, l’utilisateur 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 :

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 :

{
    "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 :

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.