Skip to content

Commit ed486dc

Browse files
authored
Merge pull request #279 from snok/fix_azure_ad
fix: do not hardcode scopes for azure AD v2
2 parents 8b7cf83 + 1f2dc70 commit ed486dc

File tree

5 files changed

+52
-7
lines changed

5 files changed

+52
-7
lines changed

django_auth_adfs/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44
Adding imports here will break setup.py
55
"""
66

7-
__version__ = '1.11.4'
7+
__version__ = '1.11.5'

django_auth_adfs/config.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ def __init__(self):
7878
self.PROXIES = None
7979

8080
self.VERSION = 'v1.0'
81+
self.SCOPES = []
8182

8283
required_settings = [
8384
"AUDIENCE",
@@ -138,6 +139,10 @@ def __init__(self):
138139
elif "VERSION" in _settings:
139140
raise ImproperlyConfigured("The VERSION cannot be set when TENANT_ID is not set.")
140141

142+
if self.VERSION == "v2.0" and not self.SCOPES and self.RELYING_PARTY_ID:
143+
warnings.warn('Use `SCOPES` for AzureAD instead of RELYING_PARTY_ID', DeprecationWarning)
144+
if not isinstance(self.SCOPES, list):
145+
raise ImproperlyConfigured("Scopes must be a list")
141146
# Overwrite defaults with user settings
142147
for setting, value in _settings.items():
143148
if hasattr(self, setting):
@@ -346,7 +351,10 @@ def build_authorization_endpoint(self, request, disable_sso=None, force_mfa=Fals
346351
})
347352
if self._mode == "openid_connect":
348353
if settings.VERSION == 'v2.0':
349-
query["scope"] = f"openid api://{settings.RELYING_PARTY_ID}/.default"
354+
if settings.SCOPES:
355+
query['scope'] = " ".join(settings.SCOPES)
356+
else:
357+
query["scope"] = f"openid api://{settings.RELYING_PARTY_ID}/.default"
350358
query.pop("resource")
351359
else:
352360
query["scope"] = "openid"

docs/settings_ref.rst

+14-4
Original file line numberDiff line numberDiff line change
@@ -117,17 +117,17 @@ The dictionary can also map extra details to the Django user account using an
117117
`Extension of the User model <https://docs.djangoproject.com/en/stable/topics/auth/customizing/#extending-the-existing-user-model>`_
118118
Set a dictionary as value in the CLAIM_MAPPING setting with as key the name User model.
119119
You will need to make sure the related field exists before the user authenticates.
120-
This can be done by creating a receiver on the
120+
This can be done by creating a receiver on the
121121
`post_save <https://docs.djangoproject.com/en/4.0/ref/signals/#post-save>`_ signal that
122122
creates the related instance when the ``User`` instance is created.
123123

124124
example
125125

126126
.. code-block:: python
127127
128-
'CLAIM_MAPPING': {'first_name': 'given_name',
129-
'last_name': 'family_name',
130-
'email': 'upn',
128+
'CLAIM_MAPPING': {'first_name': 'given_name',
129+
'last_name': 'family_name',
130+
'email': 'upn',
131131
'userprofile': {
132132
'employee_id': 'employeeid'
133133
}}
@@ -369,6 +369,16 @@ RETRIES
369369
The number of time a request to the ADFS server is retried. It allows, in combination with :ref:`timeout_setting`
370370
to fine tune the behaviour of the connection to ADFS.
371371

372+
373+
SCOPES
374+
------
375+
* **Default**: ``[]``
376+
* **Type**: ``list``
377+
378+
**Only used when you have v2 AzureAD config**
379+
380+
381+
372382
SERVER
373383
------
374384
* **Default**:

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = 'django-auth-adfs'
3-
version = "1.11.4" # Remember to also change __init__.py version
3+
version = "1.11.5" # Remember to also change __init__.py version
44
description = 'A Django authentication backend for Microsoft ADFS and AzureAD'
55
authors = ['Joris Beckers <[email protected]>']
66
maintainers = ['Jonas Krüger Svensson <[email protected]>', 'Sondre Lillebø Gundersen <[email protected]>']

tests/test_authentication.py

+27
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,33 @@ def test_oauth_redir_azure_version_two(self):
476476
self.assertEqual(redir.path.rstrip("/"), '/01234567-89ab-cdef-0123-456789abcdef/oauth2/authorize')
477477
self.assertEqual(qs, sq_expected)
478478

479+
@mock_adfs("azure")
480+
def test_scopes_generated_correctly(self):
481+
from django_auth_adfs.config import django_settings
482+
settings = deepcopy(django_settings)
483+
del settings.AUTH_ADFS["SERVER"]
484+
settings.AUTH_ADFS["TENANT_ID"] = "dummy_tenant_id"
485+
settings.AUTH_ADFS["VERSION"] = 'v2.0'
486+
settings.AUTH_ADFS["SCOPES"] = ['openid', 'api://your-configured-client-id/user_impersonation']
487+
with patch("django_auth_adfs.config.django_settings", settings), \
488+
patch("django_auth_adfs.config.settings", Settings()), \
489+
patch("django_auth_adfs.views.provider_config", ProviderConfig()):
490+
response = self.client.get("/oauth2/login?next=/test/")
491+
self.assertEqual(response.status_code, 302)
492+
redir = urlparse(response["Location"])
493+
qs = parse_qs(redir.query)
494+
sq_expected = {
495+
'scope': ['openid api://your-configured-client-id/user_impersonation'],
496+
'client_id': ['your-configured-client-id'],
497+
'state': ['L3Rlc3Qv'],
498+
'response_type': ['code'],
499+
'redirect_uri': ['http://testserver/oauth2/callback']
500+
}
501+
self.assertEqual(redir.scheme, 'https')
502+
self.assertEqual(redir.hostname, 'login.microsoftonline.com')
503+
self.assertEqual(redir.path.rstrip("/"), '/01234567-89ab-cdef-0123-456789abcdef/oauth2/authorize')
504+
self.assertEqual(qs, sq_expected)
505+
479506
@mock_adfs("2016")
480507
def test_inactive_user(self):
481508
user = User.objects.create(**{

0 commit comments

Comments
 (0)