0001-auth_oidc-add-support-for-claims-parameter-fixes-265.patch
src/authentic2_auth_oidc/migrations/0005_oidcprovider_claims_parameter_supported.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
from __future__ import unicode_literals |
|
3 | ||
4 |
from django.db import migrations, models |
|
5 | ||
6 | ||
7 |
class Migration(migrations.Migration): |
|
8 | ||
9 |
dependencies = [ |
|
10 |
('authentic2_auth_oidc', '0004_auto_20171017_1522'), |
|
11 |
] |
|
12 | ||
13 |
operations = [ |
|
14 |
migrations.AddField( |
|
15 |
model_name='oidcprovider', |
|
16 |
name='claims_parameter_supported', |
|
17 |
field=models.BooleanField(default=False, verbose_name='Claims parameter supported'), |
|
18 |
), |
|
19 |
] |
src/authentic2_auth_oidc/models.py | ||
---|---|---|
92 | 92 |
default=ALGO_RSA, |
93 | 93 |
choices=ALGO_CHOICES, |
94 | 94 |
verbose_name=_('IDToken signature algorithm')) |
95 |
claims_parameter_supported = models.BooleanField( |
|
96 |
verbose_name=_('Claims parameter supported'), |
|
97 |
default=False) |
|
95 | 98 | |
96 | 99 |
# ou where new users should be created |
97 | 100 |
strategy = models.CharField( |
... | ... | |
138 | 141 |
def __unicode__(self): |
139 | 142 |
return self.name |
140 | 143 | |
144 |
def authorization_claims_parameter(self): |
|
145 |
idtoken_claims = {} |
|
146 |
userinfo_claims = {} |
|
147 |
for claim_mapping in self.claim_mappings.all(): |
|
148 |
d = idtoken_claims if claim_mapping.idtoken_claim else userinfo_claims |
|
149 |
value = {} |
|
150 |
if claim_mapping.required: |
|
151 |
value['essential'] = True |
|
152 |
value = value or None |
|
153 |
d[claim_mapping.claim] = value |
|
154 |
return { |
|
155 |
'id_token': idtoken_claims, |
|
156 |
'userinfo': userinfo_claims, |
|
157 |
} |
|
158 | ||
141 | 159 |
def __repr__(self): |
142 | 160 |
return '<OIDCProvider %r>' % self.issuer |
143 | 161 |
src/authentic2_auth_oidc/utils.py | ||
---|---|---|
258 | 258 |
else: |
259 | 259 |
raise ValueError(_('no common algorithm found for signing idtokens: %s') % |
260 | 260 |
openid_configuration['id_token_signing_alg_values_supported']) |
261 |
claims_parameter_supported = openid_configuration.get('claims_parameter_supported') is True |
|
261 | 262 |
kwargs = dict( |
262 | 263 |
ou=ou or get_default_ou(), |
263 | 264 |
name=name, |
... | ... | |
267 | 268 |
userinfo_endpoint=openid_configuration['userinfo_endpoint'], |
268 | 269 |
jwkset_json=jwkset_json, |
269 | 270 |
idtoken_algo=idtoken_algo, |
270 |
strategy=models.OIDCProvider.STRATEGY_CREATE) |
|
271 |
strategy=models.OIDCProvider.STRATEGY_CREATE, |
|
272 |
claims_parameter_supported=claims_parameter_supported) |
|
271 | 273 |
if old_pk: |
272 | 274 |
models.OIDCProvider.objects.filter(pk=old_pk).update(**kwargs) |
273 | 275 |
return models.OIDCProvider.objects.get(pk=old_pk) |
src/authentic2_auth_oidc/views.py | ||
---|---|---|
1 | 1 |
import uuid |
2 | 2 |
import logging |
3 |
import json |
|
3 | 4 | |
4 | 5 |
import requests |
5 | 6 | |
... | ... | |
35 | 36 |
'state': state, |
36 | 37 |
'nonce': nonce, |
37 | 38 |
} |
39 |
if provider.claims_parameter_supported: |
|
40 |
params['claims'] = json.dumps(provider.authorization_claims_parameter()) |
|
38 | 41 |
if 'login_hint' in request.GET: |
39 | 42 |
params['login_hint'] = request.GET['login_hint'] |
40 | 43 |
if get_language(): |
tests/test_auth_oidc.py | ||
---|---|---|
88 | 88 |
jwkset.add(key) |
89 | 89 |
return jwkset |
90 | 90 | |
91 | ||
92 |
@pytest.fixture(params=[OIDCProvider.ALGO_RSA, OIDCProvider.ALGO_HMAC]) |
|
91 |
OIDC_PROVIDER_PARAMS = [ |
|
92 |
{}, |
|
93 |
{ |
|
94 |
'idtoken_algo': OIDCProvider.ALGO_HMAC |
|
95 |
}, |
|
96 |
{ |
|
97 |
'claims_parameter_supported': True, |
|
98 |
} |
|
99 |
] |
|
100 | ||
101 | ||
102 |
@pytest.fixture(params=OIDC_PROVIDER_PARAMS) |
|
93 | 103 |
def oidc_provider(request, db, oidc_provider_jwkset): |
94 |
idtoken_algo = request.param |
|
104 |
idtoken_algo = request.param.get('idtoken_algo', OIDCProvider.ALGO_RSA) |
|
105 |
claims_parameter_supported = request.param.get('claims_parameter_supported', False) |
|
95 | 106 |
from authentic2_auth_oidc.utils import get_provider, get_provider_by_issuer |
96 | 107 |
get_provider.cache.clear() |
97 | 108 |
get_provider_by_issuer.cache.clear() |
... | ... | |
113 | 124 |
strategy=OIDCProvider.STRATEGY_CREATE, |
114 | 125 |
jwkset_json=jwkset, |
115 | 126 |
idtoken_algo=idtoken_algo, |
127 |
claims_parameter_supported=claims_parameter_supported, |
|
116 | 128 |
) |
117 | 129 |
provider.full_clean() |
118 | 130 |
OIDCClaimMapping.objects.create( |
... | ... | |
266 | 278 |
assert query['scope'] == 'openid' |
267 | 279 |
assert query['redirect_uri'] == 'http://testserver' + reverse('oidc-login-callback') |
268 | 280 | |
281 |
if oidc_provider.claims_parameter_supported: |
|
282 |
claims = json.loads(query['claims']) |
|
283 |
assert claims['id_token']['sub'] is None |
|
284 |
assert claims['userinfo']['email']['essential'] |
|
285 |
assert claims['userinfo']['given_name']['essential'] |
|
286 |
assert claims['userinfo']['family_name']['essential'] |
|
287 |
assert claims['userinfo']['ou'] is None |
|
288 | ||
269 | 289 |
User = get_user_model() |
270 | 290 |
assert User.objects.count() == 0 |
271 | 291 | |
272 |
- |