0001-pass-id_token-on-logout-from-FranceConnect-fixes-289.patch
tests/test_fc_auth.py | ||
---|---|---|
168 | 168 |
assert session.extra_user_variables['fc_sub'] == 'ymca' |
169 | 169 | |
170 | 170 |
resp = app.get('/logout') |
171 |
assert resp.location.endswith('/ident/fc/logout') |
|
172 |
resp = resp.follow() |
|
173 |
assert resp.location == 'https://fcp.integ01.dev-franceconnect.fr/api/v1/logout?post_logout_redirect_uri=http%3A%2F%2Fexample.net' |
|
171 |
splitted = urlparse.urlsplit(resp.location) |
|
172 |
assert urlparse.urlunsplit((splitted.scheme, splitted.netloc, splitted.path, '', '')) \ |
|
173 |
== 'https://fcp.integ01.dev-franceconnect.fr/api/v1/logout' |
|
174 |
assert urlparse.parse_qs(splitted.query)['post_logout_redirect_uri'] == ['http://example.net'] |
|
175 |
assert urlparse.parse_qs(splitted.query)['id_token_hint'] |
|
174 | 176 |
assert not get_session(app) |
175 | 177 | |
176 | 178 |
# Test error handling path |
wcs/qommon/ident/franceconnect.py | ||
---|---|---|
327 | 327 |
return None |
328 | 328 |
# check id_token nonce |
329 | 329 |
id_token = result['id_token'] |
330 |
access_token = result['access_token'] |
|
330 | 331 |
header, payload, signature = id_token.split('.') |
331 | 332 |
payload = json_loads(base64url_decode(payload)) |
332 | 333 |
nonce = hashlib.sha256(str(session.id)).hexdigest() |
333 | 334 |
if payload['nonce'] != nonce: |
334 | 335 |
logger.error('FranceConnect returned nonce did not match') |
335 | 336 |
return None |
336 |
return result['access_token']
|
|
337 |
return access_token, id_token
|
|
337 | 338 | |
338 | 339 |
def get_user_info(self, access_token): |
339 | 340 |
logger = get_logger() |
... | ... | |
436 | 437 |
logger.error(_('FranceConnect authentication failed: %s'), |
437 | 438 |
_(msg) if msg else error) |
438 | 439 |
return redirect(next_url) |
439 |
access_token = self.get_access_token(request.form['code']) |
|
440 |
access_token, id_token = self.get_access_token(request.form['code'])
|
|
440 | 441 |
if not access_token: |
441 | 442 |
return redirect(next_url) |
442 | 443 |
user_info = self.get_user_info(access_token) |
... | ... | |
448 | 449 |
session_var_fc_user = {} |
449 | 450 |
for key in flattened_user_info: |
450 | 451 |
session_var_fc_user['fc_' + key] = flattened_user_info[key] |
451 | ||
452 |
session_var_fc_user['fc_access_token'] = access_token |
|
453 |
session_var_fc_user['fc_id_token'] = id_token |
|
452 | 454 |
# Lookup or create user |
453 | 455 |
sub = user_info['sub'] |
454 | 456 |
user = None |
... | ... | |
471 | 473 |
return redirect(next_url) |
472 | 474 | |
473 | 475 |
def logout(self): |
476 |
session = get_session() |
|
477 |
id_token = session.extra_user_variables['fc_id_token'] |
|
478 |
get_session_manager().expire_session() |
|
474 | 479 |
logout_url = self.get_logout_url() |
475 | 480 |
post_logout_redirect_uri = get_publisher().get_frontoffice_url() |
476 | 481 |
logout_url += '?' + urllib.urlencode({ |
482 |
'id_token_hint': id_token, |
|
477 | 483 |
'post_logout_redirect_uri': post_logout_redirect_uri, |
478 | 484 |
}) |
479 | 485 |
return redirect(logout_url) |
wcs/root.py | ||
---|---|---|
265 | 265 |
return redirect(get_publisher().get_root_url()) |
266 | 266 |
ident_methods = get_cfg('identification', {}).get('methods', []) |
267 | 267 | |
268 |
if 'fc' in ident_methods and session.extra_user_variables and 'fc_sub' in session.extra_user_variables: |
|
269 |
get_session_manager().expire_session() |
|
270 |
return redirect(get_publisher().get_root_url() + 'ident/fc/logout') |
|
268 |
if 'fc' in ident_methods and session.extra_user_variables and 'fc_id_token' in session.extra_user_variables: |
|
269 |
return get_publisher().ident_methods['fc']().logout() |
|
271 | 270 | |
272 | 271 |
if not 'idp' in ident_methods: |
273 | 272 |
get_session_manager().expire_session() |
274 |
- |