Projet

Général

Profil

0004-idp_oidc-implement-correct-error-reporting-in-user_i.patch

Benjamin Dauvergne, 03 décembre 2020 09:53

Télécharger (5,92 ko)

Voir les différences:

Subject: [PATCH 4/5] idp_oidc: implement correct error reporting in user_info
 (#47900)

* error and error_description are reported in a status 401 HTTP response,
  inside the WWW-Authenticate and inside the JSON body of the response.
 src/authentic2_idp_oidc/views.py | 34 +++++++++------
 tests/test_idp_oidc.py           | 73 ++++++++++++++++++++++++++++++++
 2 files changed, 95 insertions(+), 12 deletions(-)
src/authentic2_idp_oidc/views.py
116 116
    error_code = 'invalid_request'
117 117

  
118 118

  
119
class InvalidToken(OIDCException):
120
    error_code = 'invalid_token'
121

  
122

  
119 123
class MissingParameter(InvalidRequest):
120 124
    def __init__(self, parameter):
121 125
        super().__init__(error_description=_('Missing parameter "%s"') % parameter)
......
705 709

  
706 710
def authenticate_access_token(request):
707 711
    if 'HTTP_AUTHORIZATION' not in request.META:
708
        return None
712
        raise InvalidRequest(_('Bearer authentication is mandatory'), status=401)
709 713
    authorization = request.META['HTTP_AUTHORIZATION'].split()
710 714
    if authorization[0] != 'Bearer' or len(authorization) != 2:
711
        return None
715
        raise InvalidRequest(_('Invalid Bearer authentication'), status=401)
712 716
    try:
713 717
        access_token = models.OIDCAccessToken.objects.select_related().get(uuid=authorization[1])
714 718
    except models.OIDCAccessToken.DoesNotExist:
715
        return None
719
        raise InvalidToken(_('Token is unknown'), status=401)
716 720
    if not access_token.is_valid():
717
        return None
721
        raise InvalidToken(_('Token is expired or user is disconnected'), status=401)
718 722
    return access_token
719 723

  
720 724

  
721 725
@setting_enabled('ENABLE', settings=app_settings)
722 726
@csrf_exempt
723 727
def user_info(request, *args, **kwargs):
724
    access_token = authenticate_access_token(request)
725
    if access_token is None:
726
        return HttpResponse('unauthenticated', status=401)
727
    user_info = utils.create_user_info(request,
728
                                       access_token.client,
729
                                       access_token.user,
730
                                       access_token.scope_set())
731
    return JsonResponse(user_info)
728
    try:
729
        access_token = authenticate_access_token(request)
730
        user_info = utils.create_user_info(request,
731
                                           access_token.client,
732
                                           access_token.user,
733
                                           access_token.scope_set())
734
        return JsonResponse(user_info)
735
    except OIDCException as e:
736
        error_response = JsonResponse(
737
            {'error': e.error_code, 'error_description': e.error_description}, status=e.status)
738
        if e.status == 401:
739
            error_response['WWW-Authenticate'] = 'Bearer error="%s", error_description="%s"' % (
740
                e.error_code, e.error_description)
741
        return error_response
732 742

  
733 743

  
734 744
@setting_enabled('ENABLE', settings=app_settings)
tests/test_idp_oidc.py
1754 1754
    rf = RequestFactory()
1755 1755
    request = rf.get('/')
1756 1756
    assert good_next_url(request, 'https://example.com/')
1757

  
1758

  
1759
@pytest.fixture
1760
def access_token(client, simple_user):
1761
    return OIDCAccessToken.objects.create(
1762
        client=client,
1763
        user=simple_user,
1764
        scopes='openid profile email',
1765
        expired=now() + datetime.timedelta(seconds=3600))
1766

  
1767

  
1768
def test_user_info(app, client, access_token, freezer):
1769
    def get_user_info(**kwargs):
1770
        return app.get(
1771
            '/idp/oidc/user_info/',
1772
            headers=bearer_authentication_headers(access_token.uuid),
1773
            **kwargs)
1774

  
1775
    response = app.get('/idp/oidc/user_info/', status=401)
1776
    assert (
1777
        response['WWW-Authenticate']
1778
        == 'Bearer error="invalid_request", error_description="Bearer authentication is mandatory"'
1779
    )
1780

  
1781
    response = app.get('/idp/oidc/user_info/',
1782
                       headers={'Authorization': 'Bearer'}, status=401)
1783
    assert (
1784
        response['WWW-Authenticate']
1785
        == 'Bearer error="invalid_request", error_description="Invalid Bearer authentication"'
1786
    )
1787

  
1788
    response = get_user_info(status=200)
1789
    assert dict(response.json, sub='') == {
1790
        'email': 'user@example.net',
1791
        'email_verified': False,
1792
        'family_name': 'Dôe',
1793
        'family_name_verified': True,
1794
        'given_name': 'Jôhn',
1795
        'given_name_verified': True,
1796
        'preferred_username': 'user',
1797
        'sub': '',
1798
    }
1799

  
1800
    # token is expired
1801
    access_token.expired = now() - datetime.timedelta(seconds=1)
1802
    access_token.save()
1803
    response = get_user_info(status=401)
1804
    assert (
1805
        response['WWW-Authenticate']
1806
        == 'Bearer error="invalid_token", error_description="Token is expired or user is disconnected"'
1807
    )
1808

  
1809
    # token is unknown
1810
    access_token.delete()
1811
    response = get_user_info(status=401)
1812
    assert (
1813
        response['WWW-Authenticate']
1814
        == 'Bearer error="invalid_token", error_description="Token is unknown"'
1815
    )
1816

  
1817
    utils.login(app, access_token.user)
1818
    access_token.expired = now() + datetime.timedelta(seconds=1)
1819
    access_token.session_key = app.session.session_key
1820
    access_token.save()
1821

  
1822
    get_user_info(status=200)
1823

  
1824
    app.session.flush()
1825
    response = get_user_info(status=401)
1826
    assert (
1827
        response['WWW-Authenticate']
1828
        == 'Bearer error="invalid_token", error_description="Token is expired or user is disconnected"'
1829
    )
1757
-