0001-idp_oidc-use-invalid_grant-error-in-token-endpoint-6.patch
src/authentic2_idp_oidc/views.py | ||
---|---|---|
74 | 74 |
content['error_description'] = self.error_description |
75 | 75 | |
76 | 76 |
if self.client: |
77 |
content['client_id'] = self.client.client_id |
|
77 | 78 |
msg = 'idp_oidc: error "%s" in %s endpoint "%s" for client %s' |
78 | 79 |
if self.extra_info: |
79 | 80 |
msg += ' (%s)' % self.extra_info |
... | ... | |
181 | 182 |
error_code = 'invalid_client' |
182 | 183 | |
183 | 184 | |
185 |
class InvalidGrant(OIDCException): |
|
186 |
error_code = 'invalid_grant' |
|
187 | ||
188 | ||
184 | 189 |
class WrongClientSecret(InvalidClient): |
185 | 190 |
error_description = _('Wrong client secret') |
186 | 191 | |
... | ... | |
730 | 735 |
try: |
731 | 736 |
oidc_code = models.OIDCCode.objects.select_related().get(uuid=code) |
732 | 737 |
except models.OIDCCode.DoesNotExist: |
733 |
raise InvalidRequest(_('Parameter "code" is invalid'), client=client) |
|
738 |
raise InvalidGrant(_('Code is unknown.'), client=client) |
|
739 |
if oidc_code.client != client: |
|
740 |
raise InvalidGrant(_('Code was issued to a different client.'), client=client) |
|
734 | 741 |
if not oidc_code.is_valid(): |
735 |
raise InvalidRequest(_('Parameter "code" has expired or user is disconnected'), client=client)
|
|
742 |
raise InvalidGrant(_('Code has expired, user is disconnected or session was lost.'), client=client)
|
|
736 | 743 |
redirect_uri = request.POST.get('redirect_uri') |
737 | 744 |
if oidc_code.redirect_uri != redirect_uri: |
738 |
raise InvalidRequest(_('Parameter "redirect_uri" does not match the code.'), client=client)
|
|
745 |
raise InvalidGrant(_('Redirect_uri does not match the code.'), client=client)
|
|
739 | 746 |
if client.access_token_duration is None: |
740 | 747 |
expires_in = datetime.timedelta(seconds=oidc_code.session.get_expiry_age()) |
741 | 748 |
expired = None |
tests/idp_oidc/conftest.py | ||
---|---|---|
82 | 82 |
return settings |
83 | 83 | |
84 | 84 | |
85 |
def make_client(app, superuser, params=None):
|
|
86 |
Attribute.objects.create( |
|
85 |
def make_client(app, params=None): |
|
86 |
Attribute.objects.get_or_create(
|
|
87 | 87 |
name='cityscape_image', |
88 |
label='cityscape', |
|
89 |
kind='profile_image', |
|
90 |
asked_on_registration=True, |
|
91 |
required=False, |
|
92 |
user_visible=True, |
|
93 |
user_editable=True, |
|
88 |
defaults=dict( |
|
89 |
label='cityscape', |
|
90 |
kind='profile_image', |
|
91 |
asked_on_registration=True, |
|
92 |
required=False, |
|
93 |
user_visible=True, |
|
94 |
user_editable=True, |
|
95 |
), |
|
94 | 96 |
) |
95 | 97 | |
96 | 98 |
client = OIDCClient( |
97 | 99 |
name='oidcclient', |
98 | 100 |
slug='oidcclient', |
101 |
client_id='1234', |
|
99 | 102 |
ou=get_default_ou(), |
100 | 103 |
unauthorized_url='https://example.com/southpark/', |
101 | 104 |
redirect_uris='https://example.com/callbac%C3%A9', |
... | ... | |
111 | 114 |
return client |
112 | 115 | |
113 | 116 | |
117 |
@pytest.fixture(name='make_client') |
|
118 |
def make_client_fixture(): |
|
119 |
return make_client |
|
120 | ||
121 | ||
114 | 122 |
@pytest.fixture |
115 | 123 |
def client(app, superuser): |
116 |
return make_client(app, superuser, {})
|
|
124 |
return make_client(app) |
|
117 | 125 | |
118 | 126 | |
119 | 127 |
@pytest.fixture |
... | ... | |
125 | 133 | |
126 | 134 |
@pytest.fixture |
127 | 135 |
def oidc_client(request, superuser, app, simple_user, oidc_settings): |
128 |
return make_client(app, superuser, getattr(request, 'param', None) or {})
|
|
136 |
return make_client(app, getattr(request, 'param', None) or {}) |
|
129 | 137 | |
130 | 138 | |
131 | 139 |
@pytest.fixture |
tests/idp_oidc/test_misc.py | ||
---|---|---|
470 | 470 | |
471 | 471 | |
472 | 472 |
@pytest.mark.parametrize('oidc_client', OIDC_CLIENT_PARAMS, indirect=True) |
473 |
def test_invalid_request(oidc_client, caplog, oidc_settings, simple_user, app): |
|
473 |
def test_invalid_request(oidc_client, caplog, oidc_settings, simple_user, app, make_client):
|
|
474 | 474 |
redirect_uri = oidc_client.redirect_uris.split()[0] |
475 | 475 |
if oidc_client.authorization_flow == oidc_client.FLOW_AUTHORIZATION_CODE: |
476 | 476 |
fragment = False |
... | ... | |
865 | 865 |
# check expired codes |
866 | 866 |
if oidc_client.authorization_flow == oidc_client.FLOW_AUTHORIZATION_CODE: |
867 | 867 |
assert OIDCCode.objects.count() == 1 |
868 |
code = OIDCCode.objects.get() |
|
869 |
assert code.is_valid() |
|
870 |
# make code expire |
|
871 |
code.expired = now() - datetime.timedelta(seconds=120) |
|
872 |
assert not code.is_valid() |
|
873 |
code.save() |
|
868 |
oidc_code = OIDCCode.objects.get() |
|
869 |
assert oidc_code.is_valid() |
|
870 | ||
874 | 871 |
location = urllib.parse.urlparse(response['Location']) |
875 | 872 |
query = urllib.parse.parse_qs(location.query) |
876 | 873 |
assert set(query.keys()) == {'code'} |
877 |
assert query['code'] == [code.uuid] |
|
874 |
assert query['code'] == [oidc_code.uuid]
|
|
878 | 875 |
code = query['code'][0] |
879 | 876 |
token_url = make_url('oidc-token') |
877 | ||
878 |
# missing code parameter |
|
880 | 879 |
params = { |
881 | 880 |
'grant_type': 'authorization_code', |
882 | 881 |
'redirect_uri': oidc_client.redirect_uris.split()[0], |
... | ... | |
888 | 887 |
assert response.json['error_description'] == 'Missing parameter "code"' |
889 | 888 | |
890 | 889 |
params['code'] = code |
890 | ||
891 |
# wrong redirect_uri |
|
892 |
response = app.post( |
|
893 |
token_url, |
|
894 |
params=dict(params, redirect_uri='https://example.com/'), |
|
895 |
headers=client_authentication_headers(oidc_client), |
|
896 |
status=400, |
|
897 |
) |
|
898 |
assert 'error' in response.json |
|
899 |
assert response.json['error'] == 'invalid_grant', response.json |
|
900 |
assert response.json['error_description'] == 'Redirect_uri does not match the code.' |
|
901 |
assert response.json['client_id'] == '1234' |
|
902 | ||
903 |
# unknown code |
|
904 |
response = app.post( |
|
905 |
token_url, |
|
906 |
params=dict(params, code='xyz'), |
|
907 |
headers=client_authentication_headers(oidc_client), |
|
908 |
status=400, |
|
909 |
) |
|
910 |
assert 'error' in response.json |
|
911 |
assert response.json['error'] == 'invalid_grant' |
|
912 |
assert response.json['error_description'] == 'Code is unknown.' |
|
913 |
assert response.json['client_id'] == '1234' |
|
914 | ||
915 |
# code from another client |
|
916 |
other_client = make_client(app, params={'slug': 'other', 'name': 'other', 'client_id': 'abcd'}) |
|
917 |
other_oidc_code = OIDCCode.objects.create( |
|
918 |
client=other_client, |
|
919 |
user=oidc_code.user, |
|
920 |
profile=None, |
|
921 |
scopes='', |
|
922 |
state='1234', |
|
923 |
nonce='1234', |
|
924 |
expired=now() + datetime.timedelta(hours=1), |
|
925 |
redirect_uri=oidc_code.redirect_uri, |
|
926 |
auth_time=now(), |
|
927 |
session_key=oidc_code.session_key, |
|
928 |
) |
|
929 |
response = app.post( |
|
930 |
token_url, |
|
931 |
params=dict(params, code=other_oidc_code.uuid), |
|
932 |
headers=client_authentication_headers(oidc_client), |
|
933 |
status=400, |
|
934 |
) |
|
935 |
assert 'error' in response.json |
|
936 |
assert response.json['error'] == 'invalid_grant' |
|
937 |
assert response.json['error_description'] == 'Code was issued to a different client.', response.json |
|
938 |
assert response.json['client_id'] == '1234' |
|
939 |
other_oidc_code.delete() |
|
940 |
other_client.delete() |
|
941 | ||
942 |
# make code expire |
|
943 |
oidc_code.expired = now() - datetime.timedelta(seconds=120) |
|
944 |
assert not oidc_code.is_valid() |
|
945 |
oidc_code.save() |
|
946 | ||
947 |
# expired code |
|
891 | 948 |
response = app.post( |
892 | 949 |
token_url, params=params, headers=client_authentication_headers(oidc_client), status=400 |
893 | 950 |
) |
894 | 951 |
assert 'error' in response.json |
895 |
assert response.json['error'] == 'invalid_request' |
|
896 |
assert response.json['error_description'] == 'Parameter "code" has expired or user is disconnected' |
|
952 |
assert response.json['error'] == 'invalid_grant' |
|
953 |
assert ( |
|
954 |
response.json['error_description'] |
|
955 |
== 'Code has expired, user is disconnected or session was lost.' |
|
956 |
) |
|
957 |
assert response.json['client_id'] == '1234' |
|
897 | 958 | |
898 | 959 |
# invalid logout |
899 | 960 |
logout_url = make_url( |
... | ... | |
926 | 987 |
status=400, |
927 | 988 |
) |
928 | 989 |
assert 'error' in response.json |
929 |
assert response.json['error'] == 'invalid_request' |
|
930 |
assert response.json['error_description'] == 'Parameter "code" has expired or user is disconnected' |
|
990 |
assert response.json['error'] == 'invalid_grant' |
|
931 | 991 | |
932 | 992 | |
933 | 993 |
def test_client_secret_post_authentication(oidc_settings, app, simple_oidc_client, simple_user): |
934 |
- |