def test_a_franceconnect_usager_journal_entry_can_be_created(self):
        entry = Journal.log_franceconnection_usager(
            aidant=self.aidant_thierry,
            usager=self.usager_ned,
        )

        self.assertEqual(len(Journal.objects.all()), 3)
        self.assertEqual(entry.action, "franceconnect_usager")
Beispiel #2
0
def fc_callback(request):
    def fc_error(log_msg):
        log.error(log_msg)
        django_messages.error(
            request,
            "Nous avons rencontré une erreur en tentant d'interagir avec "
            "France Connect. C'est probabablement temporaire. Pouvez-vous réessayer "
            "votre requête ?",
        )
        return redirect("new_mandat")

    fc_base = settings.FC_AS_FS_BASE_URL
    fc_callback_uri = f"{settings.FC_AS_FS_CALLBACK_URL}/callback"
    fc_callback_uri_logout = f"{settings.FC_AS_FS_CALLBACK_URL}/logout-callback"
    fc_id = settings.FC_AS_FS_ID
    fc_secret = settings.FC_AS_FS_SECRET
    state = request.GET.get("state")

    try:
        connection = Connection.objects.get(state=state)
    except Connection.DoesNotExist:
        return fc_error(
            f"FC as FS - This state does not seem to exist: {state}")

    if request.GET.get("error"):
        return fc_error(f"FranceConnect returned an error: "
                        f"{request.GET.get('error_description')}")

    if connection.is_expired:
        return fc_error("408: FC connection has expired.")

    code = request.GET.get("code")
    if not code:
        return fc_error("FC AS FS: no code has been provided")

    token_url = f"{fc_base}/token"
    payload = {
        "grant_type": "authorization_code",
        "redirect_uri": fc_callback_uri,
        "client_id": fc_id,
        "client_secret": fc_secret,
        "code": code,
    }
    headers = {"Accept": "application/json"}

    request_for_token = python_request.post(token_url,
                                            data=payload,
                                            headers=headers)

    try:
        content = request_for_token.json()
    except ValueError:  # not a valid JSON
        return fc_error(
            f"Request to {token_url} failed. Status code: "
            f"{request_for_token.status_code}, body: {request_for_token.text}")

    connection.access_token = content.get("access_token")
    if connection.access_token is None:
        return fc_error(
            f"No access_token return when requesting {token_url}. JSON response: "
            f"{repr(content)}")

    connection.save()
    fc_id_token = content.get("id_token")

    try:
        decoded_token = jwt.decode(
            fc_id_token,
            settings.FC_AS_FS_SECRET,
            audience=settings.FC_AS_FS_ID,
            algorithms=["HS256"],
        )
    except ExpiredSignatureError:
        return fc_error("403: token signature has expired.")

    if connection.nonce != decoded_token.get("nonce"):
        return fc_error(
            "FC as FS: The nonce is different than the one expected")

    if connection.is_expired:
        log.info("408: FC connection has expired.")
        return render(request, "408.html", status=408)

    usager, error = get_user_info(connection)
    if error:
        return fc_error(error)

    connection.usager = usager
    connection.save()

    Journal.log_franceconnection_usager(
        aidant=connection.aidant,
        usager=connection.usager,
    )

    logout_base = f"{fc_base}/logout"
    logout_id_token = f"id_token_hint={fc_id_token}"
    logout_state = f"state={state}"
    logout_redirect = f"post_logout_redirect_uri={fc_callback_uri_logout}"
    logout_url = f"{logout_base}?{logout_id_token}&{logout_state}&{logout_redirect}"
    return redirect(logout_url)
Beispiel #3
0
def fc_callback(request):
    fc_base = settings.FC_AS_FS_BASE_URL
    fc_callback_uri = f"{settings.FC_AS_FS_CALLBACK_URL}/callback"
    fc_callback_uri_logout = f"{settings.FC_AS_FS_CALLBACK_URL}/logout-callback"
    fc_id = settings.FC_AS_FS_ID
    fc_secret = settings.FC_AS_FS_SECRET
    state = request.GET.get("state")

    try:
        connection = Connection.objects.get(state=state)
    except Connection.DoesNotExist:
        log.info("FC as FS - This state does not seem to exist")
        log.info(state)
        return HttpResponseForbidden()

    if connection.is_expired:
        log.info("408: FC connection has expired.")
        return render(request, "408.html", status=408)

    code = request.GET.get("code")
    if not code:
        log.info("403: No code has been provided.")
        return HttpResponseForbidden()

    token_url = f"{fc_base}/token"
    payload = {
        "grant_type": "authorization_code",
        "redirect_uri": fc_callback_uri,
        "client_id": fc_id,
        "client_secret": fc_secret,
        "code": code,
    }
    headers = {"Accept": "application/json"}

    request_for_token = python_request.post(token_url,
                                            data=payload,
                                            headers=headers)
    content = request_for_token.json()
    connection.access_token = content.get("access_token")
    connection.save()
    fc_id_token = content.get("id_token")

    try:
        decoded_token = jwt.decode(
            fc_id_token,
            settings.FC_AS_FS_SECRET,
            audience=settings.FC_AS_FS_ID,
            algorithm="HS256",
        )
    except ExpiredSignatureError:
        log.info("403: token signature has expired.")
        return HttpResponseForbidden()

    if connection.nonce != decoded_token.get("nonce"):
        log.info("403: The nonce is different than the one expected.")
        return HttpResponseForbidden()

    if connection.is_expired:
        log.info("408: FC connection has expired.")
        return render(request, "408.html", status=408)

    usager, error = get_user_info(connection)
    if error:
        django_messages.error(request, error)
        return redirect("espace_aidant_home")

    connection.usager = usager
    connection.save()

    Journal.log_franceconnection_usager(
        aidant=connection.aidant,
        usager=connection.usager,
    )

    logout_base = f"{fc_base}/logout"
    logout_id_token = f"id_token_hint={fc_id_token}"
    logout_state = f"state={state}"
    logout_redirect = f"post_logout_redirect_uri={fc_callback_uri_logout}"
    logout_url = f"{logout_base}?{logout_id_token}&{logout_state}&{logout_redirect}"
    return redirect(logout_url)