auth_oidc: add support for "claims" parameter (fixes #26565)
It allows A2 to signal to OIDC OP that some claims are required, see : https://openid.net/specs/openid-connect-core-1_0.html#ClaimsParameter
This commit is contained in:
parent
cc8ec18ea4
commit
68f87021c2
|
@ -0,0 +1,19 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('authentic2_auth_oidc', '0004_auto_20171017_1522'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='oidcprovider',
|
||||
name='claims_parameter_supported',
|
||||
field=models.BooleanField(default=False, verbose_name='Claims parameter supported'),
|
||||
),
|
||||
]
|
|
@ -98,6 +98,9 @@ class OIDCProvider(models.Model):
|
|||
default=ALGO_RSA,
|
||||
choices=ALGO_CHOICES,
|
||||
verbose_name=_('IDToken signature algorithm'))
|
||||
claims_parameter_supported = models.BooleanField(
|
||||
verbose_name=_('Claims parameter supported'),
|
||||
default=False)
|
||||
|
||||
# ou where new users should be created
|
||||
strategy = models.CharField(
|
||||
|
@ -144,6 +147,21 @@ class OIDCProvider(models.Model):
|
|||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
def authorization_claims_parameter(self):
|
||||
idtoken_claims = {}
|
||||
userinfo_claims = {}
|
||||
for claim_mapping in self.claim_mappings.all():
|
||||
d = idtoken_claims if claim_mapping.idtoken_claim else userinfo_claims
|
||||
value = {}
|
||||
if claim_mapping.required:
|
||||
value['essential'] = True
|
||||
value = value or None
|
||||
d[claim_mapping.claim] = value
|
||||
return {
|
||||
'id_token': idtoken_claims,
|
||||
'userinfo': userinfo_claims,
|
||||
}
|
||||
|
||||
def __repr__(self):
|
||||
return '<OIDCProvider %r>' % self.issuer
|
||||
|
||||
|
|
|
@ -258,6 +258,7 @@ def register_issuer(name, issuer=None, openid_configuration=None, verify=True, t
|
|||
else:
|
||||
raise ValueError(_('no common algorithm found for signing idtokens: %s') %
|
||||
openid_configuration['id_token_signing_alg_values_supported'])
|
||||
claims_parameter_supported = openid_configuration.get('claims_parameter_supported') is True
|
||||
kwargs = dict(
|
||||
ou=ou or get_default_ou(),
|
||||
name=name,
|
||||
|
@ -267,7 +268,8 @@ def register_issuer(name, issuer=None, openid_configuration=None, verify=True, t
|
|||
userinfo_endpoint=openid_configuration['userinfo_endpoint'],
|
||||
jwkset_json=jwkset_json,
|
||||
idtoken_algo=idtoken_algo,
|
||||
strategy=models.OIDCProvider.STRATEGY_CREATE)
|
||||
strategy=models.OIDCProvider.STRATEGY_CREATE,
|
||||
claims_parameter_supported=claims_parameter_supported)
|
||||
if old_pk:
|
||||
models.OIDCProvider.objects.filter(pk=old_pk).update(**kwargs)
|
||||
return models.OIDCProvider.objects.get(pk=old_pk)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import uuid
|
||||
import logging
|
||||
import json
|
||||
|
||||
import requests
|
||||
|
||||
|
@ -35,6 +36,8 @@ def oidc_login(request, pk, next_url=None, *args, **kwargs):
|
|||
'state': state,
|
||||
'nonce': nonce,
|
||||
}
|
||||
if provider.claims_parameter_supported:
|
||||
params['claims'] = json.dumps(provider.authorization_claims_parameter())
|
||||
if 'login_hint' in request.GET:
|
||||
params['login_hint'] = request.GET['login_hint']
|
||||
if get_language():
|
||||
|
|
|
@ -88,10 +88,21 @@ def oidc_provider_jwkset():
|
|||
jwkset.add(key)
|
||||
return jwkset
|
||||
|
||||
OIDC_PROVIDER_PARAMS = [
|
||||
{},
|
||||
{
|
||||
'idtoken_algo': OIDCProvider.ALGO_HMAC
|
||||
},
|
||||
{
|
||||
'claims_parameter_supported': True,
|
||||
}
|
||||
]
|
||||
|
||||
@pytest.fixture(params=[OIDCProvider.ALGO_RSA, OIDCProvider.ALGO_HMAC])
|
||||
|
||||
@pytest.fixture(params=OIDC_PROVIDER_PARAMS)
|
||||
def oidc_provider(request, db, oidc_provider_jwkset):
|
||||
idtoken_algo = request.param
|
||||
idtoken_algo = request.param.get('idtoken_algo', OIDCProvider.ALGO_RSA)
|
||||
claims_parameter_supported = request.param.get('claims_parameter_supported', False)
|
||||
from authentic2_auth_oidc.utils import get_provider, get_provider_by_issuer
|
||||
get_provider.cache.clear()
|
||||
get_provider_by_issuer.cache.clear()
|
||||
|
@ -113,6 +124,7 @@ def oidc_provider(request, db, oidc_provider_jwkset):
|
|||
strategy=OIDCProvider.STRATEGY_CREATE,
|
||||
jwkset_json=jwkset,
|
||||
idtoken_algo=idtoken_algo,
|
||||
claims_parameter_supported=claims_parameter_supported,
|
||||
)
|
||||
provider.full_clean()
|
||||
OIDCClaimMapping.objects.create(
|
||||
|
@ -266,6 +278,14 @@ def test_sso(app, caplog, code, oidc_provider, oidc_provider_jwkset, login_url,
|
|||
assert query['scope'] == 'openid'
|
||||
assert query['redirect_uri'] == 'http://testserver' + reverse('oidc-login-callback')
|
||||
|
||||
if oidc_provider.claims_parameter_supported:
|
||||
claims = json.loads(query['claims'])
|
||||
assert claims['id_token']['sub'] is None
|
||||
assert claims['userinfo']['email']['essential']
|
||||
assert claims['userinfo']['given_name']['essential']
|
||||
assert claims['userinfo']['family_name']['essential']
|
||||
assert claims['userinfo']['ou'] is None
|
||||
|
||||
User = get_user_model()
|
||||
assert User.objects.count() == 0
|
||||
|
||||
|
|
Loading…
Reference in New Issue