0004-idp_oidc-implement-correct-error-reporting-in-user_i.patch
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 |
- |