Skip to content

Commit d126975

Browse files
committed
Adding tests
1 parent 0711a00 commit d126975

File tree

7 files changed

+250
-17
lines changed

7 files changed

+250
-17
lines changed

example/flask_rp/views.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ def repost_fragment():
186186
return finalize(op_identifier, args)
187187

188188

189-
@oidc_rp_views.route('/authz_im_cb')
189+
@oidc_rp_views.route('/authz_tok_cb')
190190
def authz_im_cb(op_identifier='', **kwargs):
191191
logger.debug('implicit_hybrid_flow kwargs: {}'.format(kwargs))
192192
return render_template('repost_fragment.html', op_identifier=op_identifier)

src/idpyoidc/client/oauth2/authorization.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,17 @@ def post_parse_response(self, response, **kwargs):
100100
pass
101101
return response
102102

103-
def _do_flow(self, flow_type, response_types):
103+
def _do_flow(self, flow_type, response_types, context):
104104
if flow_type == "code":
105105
if "code" in response_types:
106106
return True
107107
elif flow_type in ["implicit", "hybrid"]:
108108
if implicit_response_types(response_types):
109109
return True
110+
elif flow_type == 'form_post':
111+
rm = context.get_preference('response_modes_supported')
112+
if rm and 'form_post' in rm:
113+
return context.config.conf.get("separate_form_post_cb", True)
110114
return False
111115

112116
def _do_redirect_uris(self, base_url, hex, context, callback_uris, response_types):
@@ -116,22 +120,22 @@ def _do_redirect_uris(self, base_url, hex, context, callback_uris, response_type
116120
# the same redirect_uris for all flow types
117121
callback_uris["redirect_uris"] = {}
118122
for flow_type in self._callback_path["redirect_uris"].keys():
119-
if self._do_flow(flow_type, response_types):
123+
if self._do_flow(flow_type, response_types, context):
120124
callback_uris["redirect_uris"][flow_type] = _redirect_uris
121125
elif callback_uris:
122126
if "redirect_uris" in callback_uris:
123127
pass
124128
else:
125129
callback_uris["redirect_uris"] = {}
126130
for flow_type, path in self._callback_path["redirect_uris"].items():
127-
if self._do_flow(flow_type, response_types):
131+
if self._do_flow(flow_type, response_types, context):
128132
callback_uris["redirect_uris"][flow_type] = [
129133
self.get_uri(base_url, path, hex)
130134
]
131135
else:
132136
callback_uris["redirect_uris"] = {}
133137
for flow_type, path in self._callback_path["redirect_uris"].items():
134-
if self._do_flow(flow_type, response_types):
138+
if self._do_flow(flow_type, response_types, context):
135139
callback_uris["redirect_uris"][flow_type] = [self.get_uri(base_url, path, hex)]
136140
return callback_uris
137141

src/idpyoidc/client/oauth2/stand_alone_client.py

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -167,14 +167,17 @@ def init_authorization(
167167

168168
_context = self.get_context()
169169
_response_type = self._get_response_type(_context, req_args)
170-
_response_mode = self._get_response_mode(_context, _response_type)
171170
request_args = {
172171
"redirect_uri": pick_redirect_uri(
173172
_context, request_args=req_args, response_type=_response_type
174173
),
175174
"response_type": _response_type,
176175
}
177176

177+
_response_mode = self._get_response_mode(_context, _response_type)
178+
if _response_mode:
179+
request_args['response_mode'] = _response_mode
180+
178181
_nonce = ''
179182
if self.client_type == 'oidc':
180183
_nonce = rndstr(24)
@@ -214,15 +217,6 @@ def init_authorization(
214217
logger.debug("Authorization info: {}".format(_info))
215218
return _info["url"]
216219

217-
@staticmethod
218-
def get_response_type(self):
219-
"""
220-
Return the response_type a client wants to use.
221-
222-
:return: The response_type
223-
"""
224-
return self.service_context.claims.get_usage("response_types")[0]
225-
226220
@staticmethod
227221
def get_client_authn_method(self, endpoint):
228222
"""

src/idpyoidc/client/oauth2/utils.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,10 @@ def pick_redirect_uri(
4949

5050
if _response_mode:
5151
if _response_mode == "form_post":
52-
redirect_uri = _callback_uris["form_post"][0]
52+
try:
53+
redirect_uri = _callback_uris["form_post"][0]
54+
except KeyError:
55+
redirect_uri = _callback_uris["code"][0]
5356
elif response_type == "code" or response_type == ["code"]:
5457
redirect_uri = _callback_uris["code"][0]
5558
else:

src/idpyoidc/client/oidc/authorization.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ class Authorization(authorization.Authorization):
5050
"redirect_uris": { # based on response_types
5151
"code": "authz_cb",
5252
"implicit": "authz_tok_cb",
53-
"form_post": "form",
53+
"form_post": "authz_cb_form",
5454
},
5555
}
5656

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
from urllib.parse import parse_qs
2+
from urllib.parse import urlsplit
3+
4+
import pytest
5+
import responses
6+
from cryptojwt.key_jar import build_keyjar
7+
8+
from idpyoidc.client.defaults import DEFAULT_KEY_DEFS
9+
from idpyoidc.client.defaults import OIDCONF_PATTERN
10+
from idpyoidc.client.oauth2.stand_alone_client import StandAloneClient
11+
from idpyoidc.message.oidc import ProviderConfigurationResponse
12+
from idpyoidc.message.oidc import RegistrationResponse
13+
14+
ISSUER = "https://op.example.com"
15+
16+
STATIC_CONFIG = {
17+
"base_url": "https://example.com/cli/",
18+
"client_id": "Number5",
19+
"client_type": "oidc",
20+
"client_secret": "asdflkjh0987654321",
21+
"provider_info": {
22+
"issuer": ISSUER,
23+
"authorization_endpoint": "https://op.example.com/authn",
24+
"token_endpoint": "https://op.example.com/token",
25+
"userinfo_endpoint": "https://op.example.com/user",
26+
}
27+
}
28+
29+
30+
def get_state_from_url(url):
31+
p = urlsplit(url)
32+
qs = parse_qs(p.query)
33+
return qs['state'][0]
34+
35+
36+
class TestStandAloneClientOIDCStatic(object):
37+
38+
@pytest.fixture(autouse=True)
39+
def client_setup(self):
40+
self.client = StandAloneClient(config=STATIC_CONFIG)
41+
42+
def test_get_services(self):
43+
assert set(self.client.get_services().keys()) == {'provider_info', 'registration',
44+
'authorization', 'accesstoken',
45+
'refresh_token', 'userinfo'}
46+
47+
def test_do_provider_info(self):
48+
issuer = self.client.do_provider_info()
49+
assert issuer == STATIC_CONFIG['provider_info']['issuer']
50+
assert self.client.context.get('issuer') == issuer
51+
52+
def test_client_registration(self):
53+
self.client.do_provider_info()
54+
self.client.do_client_registration()
55+
assert self.client.context.get_usage('client_id') == STATIC_CONFIG['client_id']
56+
57+
def test_init_authorization(self):
58+
self.client.do_provider_info()
59+
self.client.do_client_registration()
60+
url = self.client.init_authorization()
61+
assert url
62+
p = urlsplit(url)
63+
qs = parse_qs(p.query)
64+
assert qs['client_id'][0] == STATIC_CONFIG['client_id']
65+
assert qs['response_type'][0] == 'code'
66+
67+
def test_response_type_id_token(self):
68+
self.client.do_provider_info()
69+
self.client.do_client_registration()
70+
71+
# Explicitly set
72+
url = self.client.init_authorization(req_args={'response_type': 'id_token'})
73+
74+
assert url
75+
p = urlsplit(url)
76+
qs = parse_qs(p.query)
77+
assert qs['client_id'][0] == STATIC_CONFIG['client_id']
78+
assert qs['response_type'][0] == 'id_token'
79+
80+
81+
def test_response_mode():
82+
conf = STATIC_CONFIG.copy()
83+
conf.update({
84+
"response_modes_supported": ['code','form_post'],
85+
'separate_form_post_cb': True
86+
})
87+
client = StandAloneClient(config=conf)
88+
client.do_provider_info()
89+
client.do_client_registration()
90+
91+
# Explicitly set
92+
url = client.init_authorization(req_args={'response_mode': 'form_post'})
93+
94+
assert url
95+
p = urlsplit(url)
96+
qs = parse_qs(p.query)
97+
assert 'authz_cb_form' in qs['redirect_uri'][0]
98+
assert qs['client_id'][0] == STATIC_CONFIG['client_id']
99+
assert qs['response_type'][0] == 'code'
100+
assert qs['response_mode'][0] == 'form_post'
101+
102+
def test_response_mode_not_separate():
103+
conf = STATIC_CONFIG.copy()
104+
conf.update({
105+
"response_modes_supported": ['code','form_post'],
106+
'separate_form_post_cb': False
107+
})
108+
client = StandAloneClient(config=conf)
109+
client.do_provider_info()
110+
client.do_client_registration()
111+
112+
# Explicitly set
113+
url = client.init_authorization(req_args={'response_mode': 'form_post'})
114+
115+
assert url
116+
p = urlsplit(url)
117+
qs = parse_qs(p.query)
118+
assert 'authz_cb_form' not in qs['redirect_uri'][0]
119+
assert qs['client_id'][0] == STATIC_CONFIG['client_id']
120+
assert qs['response_type'][0] == 'code'
121+
assert qs['response_mode'][0] == 'form_post'
122+
123+
124+
SEMI_DYN_CONFIG = {
125+
"base_url": "https://example.com/cli/",
126+
"client_id": "Number5",
127+
"client_secret": "asdflkjh0987654321",
128+
"client_type": "oidc",
129+
"provider_info": {
130+
"issuer": "https://op.example.com"
131+
}
132+
}
133+
134+
PROVIDER_INFO = ProviderConfigurationResponse(
135+
issuer=ISSUER,
136+
authorization_endpoint="https://op.example.com/authn",
137+
token_endpoint="https://op.example.com/token",
138+
userinfo_endpoint="https://op.example.com/user",
139+
registration_endpoint="https://op.example.com/register",
140+
jwks_uri="https://op.example.com/keys/jwks.json",
141+
response_types_supported=["code"],
142+
subject_types_supported=['public'],
143+
id_token_signing_alg_values_supported=['RS256']
144+
)
145+
146+
OP_KEYS = build_keyjar(DEFAULT_KEY_DEFS)
147+
148+
149+
class TestStandAloneClientOIDCDynProviderInfo(object):
150+
151+
@pytest.fixture(autouse=True)
152+
def client_setup(self):
153+
self.client = StandAloneClient(config=SEMI_DYN_CONFIG)
154+
155+
def test_do_provider_info(self):
156+
with responses.RequestsMock() as rsps:
157+
rsps.add(
158+
"GET",
159+
OIDCONF_PATTERN.format(SEMI_DYN_CONFIG['provider_info']['issuer']),
160+
body=PROVIDER_INFO.to_json(),
161+
adding_headers={"Content-Type": "application/json"},
162+
status=200,
163+
)
164+
rsps.add(
165+
"GET",
166+
PROVIDER_INFO['jwks_uri'],
167+
body=OP_KEYS.export_jwks_as_json(),
168+
adding_headers={"Content-Type": "application/json"},
169+
status=200,
170+
)
171+
issuer = self.client.do_provider_info()
172+
173+
assert issuer == SEMI_DYN_CONFIG['provider_info']['issuer']
174+
assert self.client.context.get('issuer') == issuer
175+
176+
177+
DYN_CONFIG = {
178+
"base_url": "https://rp.example.com",
179+
"redirect_uris": ["https://rp.example.com/cb"],
180+
"key_conf": {"key_defs": DEFAULT_KEY_DEFS},
181+
"client_type": "oidc",
182+
"provider_info": {
183+
"issuer": "https://op.example.com"
184+
}
185+
}
186+
187+
188+
class TestStandAloneClientOIDCDyn(object):
189+
190+
@pytest.fixture(autouse=True)
191+
def client_setup(self):
192+
self.client = StandAloneClient(config=DYN_CONFIG)
193+
194+
def test_do_provider_info(self):
195+
with responses.RequestsMock() as rsps:
196+
rsps.add(
197+
"GET",
198+
OIDCONF_PATTERN.format(SEMI_DYN_CONFIG['provider_info']['issuer']),
199+
body=PROVIDER_INFO.to_json(),
200+
adding_headers={"Content-Type": "application/json"},
201+
status=200,
202+
)
203+
rsps.add(
204+
"GET",
205+
PROVIDER_INFO['jwks_uri'],
206+
body=OP_KEYS.export_jwks_as_json(),
207+
adding_headers={"Content-Type": "application/json"},
208+
status=200,
209+
)
210+
issuer = self.client.do_provider_info()
211+
212+
assert issuer == DYN_CONFIG['provider_info']['issuer']
213+
assert self.client.context.get('issuer') == issuer
214+
215+
registration_response = RegistrationResponse(
216+
client_id="client_1",
217+
client_secret="a0b1c2d3e4f5g6h7i8j9",
218+
redirect_uris=["https://rp.example.com/cb"]
219+
)
220+
with responses.RequestsMock() as rsps:
221+
# registration response
222+
rsps.add(
223+
"POST",
224+
PROVIDER_INFO['registration_endpoint'],
225+
body=registration_response.to_json(),
226+
adding_headers={"Content-Type": "application/json"},
227+
status=200,
228+
)
229+
230+
self.client.do_client_registration()
231+
232+
assert self.client.context.get_usage('client_id') == 'client_1'
File renamed without changes.

0 commit comments

Comments
 (0)