Skip to content
This repository has been archived by the owner on Oct 22, 2024. It is now read-only.

Commit

Permalink
WIP: Flow OIDC correctement implementé coté back
Browse files Browse the repository at this point in the history
Tests à prévoir
  • Loading branch information
ikarius committed Sep 26, 2024
1 parent 3549f51 commit 90c14c1
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 1 deletion.
4 changes: 4 additions & 0 deletions config/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,10 +336,14 @@
OIDC_OP_TOKEN_ENDPOINT = f"https://{PC_ISSUER}/token"
# https://fca.integ01.dev-agentconnect.fr/api/v2/userinfo
OIDC_OP_USER_ENDPOINT = f"https://{PC_ISSUER}/userinfo"
OIDC_OP_LOGOUT_ENDPOINT = f"https://{PC_ISSUER}/session/end"

# Intervalle de rafraichissement du token (4h)
OIDC_RENEW_ID_TOKEN_EXPIRY_SECONDS = 4 * 60 * 60

# Redirection vers le front DORA en cas de succès de l'identification
LOGIN_REDIRECT_URL = "/oidc/logged_in"

# Temporaire : modifié pour l'intégration, à supprimer pour la production
OIDC_AUTHENTICATION_CALLBACK_URL = "oidc_authorize_callback"
# Temporaire : force la représentation interne des URL avec un scheme HTTPS (build_absolute_uri)
Expand Down
36 changes: 35 additions & 1 deletion dora/oidc/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
from logging import getLogger

import requests
from django.core.exceptions import SuspiciousOperation
from mozilla_django_oidc.auth import (
OIDCAuthenticationBackend as MozillaOIDCAuthenticationBackend,
)
from rest_framework.authtoken.models import Token

from dora.users.models import User

logger = getLogger(__name__)


class OIDCError(Exception):
Expand Down Expand Up @@ -56,11 +63,38 @@ def create_user(self, claims):

# L'utilisateur est créé sans mot de passe (aucune connexion à l'admin),
# et comme venant de ProConnect, on considère l'e-mail vérifié.
return self.UserModel.objects.create_user(
new_user = self.UserModel.objects.create_user(
email,
sub_pc=sub,
first_name=claims.get("given_name", "N/D"),
last_name=claims.get("usual_name", "N/D"),
is_valid=True,
)

# compatibilité :
# durant la phase de migration vers ProConnect on ne replace *que* le fournisseur d'identité,
# et on ne touche pas aux mécanismes d'identification entre back et front.
self.get_or_create_drf_token(new_user)

return new_user

def get_user(self, user_id):
if user := super().get_user(user_id):
self.get_or_create_drf_token(user)
return user
return None

def get_or_create_drf_token(self, user_email):
# Pour être temporairement compatible, on crée un token d'identification DRF lié au nouvel utilisateur.
print("get token for:", user_email)
if not user_email:
logger.exception("Utilisateur non renseigné pour la création du token DRF")

user = User.objects.get(email=user_email)

token, created = Token.objects.get_or_create(user=user)

if created:
logger.info("Initialisation du token DRF pour l'utilisateur %s", user_email)

return token
2 changes: 2 additions & 0 deletions dora/oidc/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,6 @@
"oidc/authorize", views.oidc_authorize_callback, name="oidc_authorize_callback"
),
path("oidc/login", views.oidc_login, name="oidc_login"),
path("oidc/logged_in", views.oidc_logged_in, name="oidc_logged_in"),
path("oidc/pre_logout", views.oidc_pre_logout, name="oidc_pre_logout"),
]
45 changes: 45 additions & 0 deletions dora/oidc/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from django.conf import settings
from django.core.cache import cache
from django.db import transaction
from django.http import HttpResponseForbidden
from django.http.response import HttpResponseRedirect
from django.urls import reverse
from django.utils.crypto import get_random_string
Expand Down Expand Up @@ -188,6 +189,7 @@ def oidc_authorize_callback(request):
+ f"?{request.META.get("QUERY_STRING")}"
)


@api_view(["GET"])
@permission_classes([permissions.AllowAny])
def oidc_login(request):
Expand All @@ -196,3 +198,46 @@ def oidc_login(request):
redirect_to=reverse("oidc_authentication_init")
+ f"?{request.META.get("QUERY_STRING")}"
)


@api_view(["GET"])
@permission_classes([permissions.AllowAny])
def oidc_logged_in(request):
# redirection vers la page d'accueil de DORA
print("request.user:", request.user)
# attention : l'utilisateur est toujours anonyme (a ce point il n'existe qu'un token DRF)
token = Token.objects.get(user_id=request.session["_auth_user_id"])
print("fetching token...")
return HttpResponseRedirect(
redirect_to=f"{settings.FRONTEND_URL}/auth/pc-callback/{token}"
)


@api_view(["GET"])
@permission_classes([permissions.AllowAny])
def oidc_pre_logout(request):
# attention : le nom oidc_logout est pris par mozilla-django-oidc
# récuperation du token stocké en session:
print("OIDC logout")
if oidc_token := request.session.get("oidc_id_token"):
print("session oidc_token:", oidc_token)
print(
"URL de logout de mozilla-oidc:",
request.build_absolute_uri(reverse("oidc_logout")),
)
# construction de l'URL de logout
# attention au trailing slashes !!!!
params = {
"id_token_hint": oidc_token,
"state": "todo_xxx",
"post_logout_redirect_uri": request.build_absolute_uri(
reverse("oidc_logout").rstrip("/")
),
}
print("logout params:", params)
logout_url = furl(settings.OIDC_OP_LOGOUT_ENDPOINT, args=params)
print("logout URL:", logout_url)
# attention : pas de trailing slash dans la version de test
return HttpResponseRedirect(redirect_to=logout_url.url)
# FIXME: URL de fallback ?
return HttpResponseForbidden("Déconnexion incorrecte")
2 changes: 2 additions & 0 deletions dora/users/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ class User(AbstractBaseUser):
ic_id = models.UUIDField(
verbose_name="Identifiant Inclusion Connect", null=True, blank=True
)

# null possible en base ... pour l'instant
sub_pc = models.UUIDField(verbose_name="Identifiant ProConnect", null=True)

email = models.EmailField(
Expand Down

0 comments on commit 90c14c1

Please sign in to comment.