Skip to content

Commit b744992

Browse files
committed
Fix create_id_token with extra scope claims + add ruff as formatter.
1 parent 98b9810 commit b744992

File tree

8 files changed

+418
-357
lines changed

8 files changed

+418
-357
lines changed

.vscode/settings.json

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,10 @@
11
{
22
"[python]": {
3+
"editor.formatOnSave": true,
34
"editor.codeActionsOnSave": {
4-
"source.sortImports": "explicit"
5-
}
6-
},
7-
"python.formatting.provider": "black",
8-
"editor.formatOnSave": true,
9-
"black-formatter.args": [
10-
"--line-length=100",
11-
"--preview",
12-
],
13-
"isort.args": [
14-
"--profile",
15-
"black"
16-
],
5+
"source.fixAll": "explicit",
6+
"source.organizeImports": "explicit"
7+
},
8+
"editor.defaultFormatter": "charliermarsh.ruff"
9+
}
1710
}

docs/sections/contribute.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ Use `tox <https://pypi.python.org/pypi/tox>`_ for running tests in each of the e
2424
# Run with Python 3.11 and Django 4.2.
2525
$ tox -e py311-django42
2626

27-
# Run single test file on specific environment.
28-
$ tox -e py311-django42 -- tests/cases/test_authorize_endpoint.py
27+
# Run a single test method.
28+
$ tox -e py311-django42 -- tests/cases/test_authorize_endpoint.py::TestClass::test_some_method
2929

3030
We use `Github Actions <https://github.com/juanifioren/django-oidc-provider/actions>`_ to automatically test every commit to the project.
3131

oidc_provider/lib/endpoints/authorize.py

Lines changed: 139 additions & 124 deletions
Large diffs are not rendered by default.

oidc_provider/lib/endpoints/token.py

Lines changed: 113 additions & 110 deletions
Large diffs are not rendered by default.

oidc_provider/lib/utils/token.py

Lines changed: 31 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -19,52 +19,52 @@
1919
from oidc_provider import settings
2020

2121

22-
def create_id_token(token, user, aud, nonce='', at_hash='', request=None, scope=None):
22+
def create_id_token(token, user, aud, nonce="", at_hash="", request=None, scope=None):
2323
"""
2424
Creates the id_token dictionary.
2525
See: http://openid.net/specs/openid-connect-core-1_0.html#IDToken
2626
Return a dic.
2727
"""
2828
if scope is None:
2929
scope = []
30-
sub = settings.get('OIDC_IDTOKEN_SUB_GENERATOR', import_str=True)(user=user)
30+
sub = settings.get("OIDC_IDTOKEN_SUB_GENERATOR", import_str=True)(user=user)
3131

32-
expires_in = settings.get('OIDC_IDTOKEN_EXPIRE')
32+
expires_in = settings.get("OIDC_IDTOKEN_EXPIRE")
3333

3434
# Convert datetimes into timestamps.
3535
now = int(time.time())
3636
iat_time = now
3737
exp_time = int(now + expires_in)
3838
user_auth_time = user.last_login or user.date_joined
39-
auth_time = int(dateformat.format(user_auth_time, 'U'))
39+
auth_time = int(dateformat.format(user_auth_time, "U"))
4040

4141
dic = {
42-
'iss': get_issuer(request=request),
43-
'sub': sub,
44-
'aud': str(aud),
45-
'exp': exp_time,
46-
'iat': iat_time,
47-
'auth_time': auth_time,
42+
"iss": get_issuer(request=request),
43+
"sub": sub,
44+
"aud": str(aud),
45+
"exp": exp_time,
46+
"iat": iat_time,
47+
"auth_time": auth_time,
4848
}
4949

5050
if nonce:
51-
dic['nonce'] = str(nonce)
51+
dic["nonce"] = str(nonce)
5252

5353
if at_hash:
54-
dic['at_hash'] = at_hash
54+
dic["at_hash"] = at_hash
5555

5656
# Inlude (or not) user standard claims in the id_token.
57-
if settings.get('OIDC_IDTOKEN_INCLUDE_CLAIMS'):
58-
if settings.get('OIDC_EXTRA_SCOPE_CLAIMS'):
59-
custom_claims = settings.get('OIDC_EXTRA_SCOPE_CLAIMS', import_str=True)(token)
60-
claims = custom_claims.create_response_dic()
61-
else:
62-
claims = StandardScopeClaims(token).create_response_dic()
63-
dic.update(claims)
57+
if settings.get("OIDC_IDTOKEN_INCLUDE_CLAIMS"):
58+
standard_claims = StandardScopeClaims(token)
59+
dic.update(standard_claims.create_response_dic())
60+
61+
if settings.get("OIDC_EXTRA_SCOPE_CLAIMS"):
62+
extra_claims = settings.get("OIDC_EXTRA_SCOPE_CLAIMS", import_str=True)(token)
63+
dic.update(extra_claims.create_response_dic())
6464

6565
dic = run_processing_hook(
66-
dic, 'OIDC_IDTOKEN_PROCESSING_HOOK',
67-
user=user, token=token, request=request)
66+
dic, "OIDC_IDTOKEN_PROCESSING_HOOK", user=user, token=token, request=request
67+
)
6868

6969
return dic
7070

@@ -94,7 +94,7 @@ def client_id_from_id_token(id_token):
9494
Returns a string or None.
9595
"""
9696
payload = JWT().unpack(id_token).payload()
97-
aud = payload.get('aud', None)
97+
aud = payload.get("aud", None)
9898
if aud is None:
9999
return None
100100
if isinstance(aud, list):
@@ -116,15 +116,15 @@ def create_token(user, client, scope, id_token_dic=None):
116116
token.id_token = id_token_dic
117117

118118
token.refresh_token = uuid.uuid4().hex
119-
token.expires_at = timezone.now() + timedelta(
120-
seconds=settings.get('OIDC_TOKEN_EXPIRE'))
119+
token.expires_at = timezone.now() + timedelta(seconds=settings.get("OIDC_TOKEN_EXPIRE"))
121120
token.scope = scope
122121

123122
return token
124123

125124

126-
def create_code(user, client, scope, nonce, is_authentication,
127-
code_challenge=None, code_challenge_method=None):
125+
def create_code(
126+
user, client, scope, nonce, is_authentication, code_challenge=None, code_challenge_method=None
127+
):
128128
"""
129129
Create and populate a Code object.
130130
Return a Code object.
@@ -139,8 +139,7 @@ def create_code(user, client, scope, nonce, is_authentication,
139139
code.code_challenge = code_challenge
140140
code.code_challenge_method = code_challenge_method
141141

142-
code.expires_at = timezone.now() + timedelta(
143-
seconds=settings.get('OIDC_CODE_EXPIRE'))
142+
code.expires_at = timezone.now() + timedelta(seconds=settings.get("OIDC_CODE_EXPIRE"))
144143
code.scope = scope
145144
code.nonce = nonce
146145
code.is_authentication = is_authentication
@@ -153,15 +152,15 @@ def get_client_alg_keys(client):
153152
Takes a client and returns the set of keys associated with it.
154153
Returns a list of keys.
155154
"""
156-
if client.jwt_alg == 'RS256':
155+
if client.jwt_alg == "RS256":
157156
keys = []
158157
for rsakey in RSAKey.objects.all():
159158
keys.append(jwk_RSAKey(key=importKey(rsakey.key), kid=rsakey.kid))
160159
if not keys:
161-
raise Exception('You must add at least one RSA Key.')
162-
elif client.jwt_alg == 'HS256':
160+
raise Exception("You must add at least one RSA Key.")
161+
elif client.jwt_alg == "HS256":
163162
keys = [SYMKey(key=client.client_secret, alg=client.jwt_alg)]
164163
else:
165-
raise Exception('Unsupported key algorithm.')
164+
raise Exception("Unsupported key algorithm.")
166165

167166
return keys

oidc_provider/tests/app/utils.py

Lines changed: 55 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,27 @@
55
from django.contrib.auth.backends import ModelBackend
66

77
try:
8-
from urlparse import parse_qs, urlsplit
8+
from urlparse import parse_qs
9+
from urlparse import urlsplit
910
except ImportError:
10-
from urllib.parse import parse_qs, urlsplit
11+
from urllib.parse import parse_qs
12+
from urllib.parse import urlsplit
1113

12-
from django.utils import timezone
1314
from django.contrib.auth.models import User
15+
from django.utils import timezone
1416

15-
from oidc_provider.models import (
16-
Client,
17-
Code,
18-
Token,
19-
ResponseType)
20-
17+
from oidc_provider.lib.claims import ScopeClaims
18+
from oidc_provider.models import Client
19+
from oidc_provider.models import Code
20+
from oidc_provider.models import ResponseType
21+
from oidc_provider.models import Token
2122

22-
FAKE_NONCE = 'cb584e44c43ed6bd0bc2d9c7e242837d'
23-
FAKE_RANDOM_STRING = ''.join(
24-
random.choice(string.ascii_uppercase + string.digits) for _ in range(32))
25-
FAKE_CODE_CHALLENGE = 'YlYXEqXuRm-Xgi2BOUiK50JW1KsGTX6F1TDnZSC8VTg'
26-
FAKE_CODE_VERIFIER = 'SmxGa0XueyNh5bDgTcSrqzAh2_FmXEqU8kDT6CuXicw'
23+
FAKE_NONCE = "cb584e44c43ed6bd0bc2d9c7e242837d"
24+
FAKE_RANDOM_STRING = "".join(
25+
random.choice(string.ascii_uppercase + string.digits) for _ in range(32)
26+
)
27+
FAKE_CODE_CHALLENGE = "YlYXEqXuRm-Xgi2BOUiK50JW1KsGTX6F1TDnZSC8VTg"
28+
FAKE_CODE_VERIFIER = "SmxGa0XueyNh5bDgTcSrqzAh2_FmXEqU8kDT6CuXicw"
2729

2830

2931
def create_fake_user():
@@ -33,11 +35,11 @@ def create_fake_user():
3335
Return a User object.
3436
"""
3537
user = User()
36-
user.username = 'johndoe'
37-
user.email = 'johndoe@example.com'
38-
user.first_name = 'John'
39-
user.last_name = 'Doe'
40-
user.set_password('1234')
38+
user.username = "johndoe"
39+
user.email = "johndoe@example.com"
40+
user.first_name = "John"
41+
user.last_name = "Doe"
42+
user.set_password("1234")
4143

4244
user.save()
4345

@@ -52,20 +54,20 @@ def create_fake_client(response_type, is_public=False, require_consent=True):
5254
Return a Client object.
5355
"""
5456
client = Client()
55-
client.name = 'Some Client'
57+
client.name = "Some Client"
5658
client.client_id = str(random.randint(1, 999999)).zfill(6)
5759
if is_public:
58-
client.client_type = 'public'
59-
client.client_secret = ''
60+
client.client_type = "public"
61+
client.client_secret = ""
6062
else:
6163
client.client_secret = str(random.randint(1, 999999)).zfill(6)
62-
client.redirect_uris = ['http://example.com/']
64+
client.redirect_uris = ["http://example.com/"]
6365
client.require_consent = require_consent
64-
client.scope = ['openid', 'email']
66+
client.scope = ["openid", "email"]
6567
client.save()
6668

6769
# check if response_type is a string in a python 2 and 3 compatible way
68-
if isinstance(response_type, ("".__class__, u"".__class__)):
70+
if isinstance(response_type, ("".__class__, "".__class__)):
6971
response_type = (response_type,)
7072
for value in response_type:
7173
client.response_types.add(ResponseType.objects.get(value=value))
@@ -90,7 +92,7 @@ def is_code_valid(url, user, client):
9092
try:
9193
parsed = urlsplit(url)
9294
params = parse_qs(parsed.query or parsed.fragment)
93-
code = params['code'][0]
95+
code = params["code"][0]
9496
code = Code.objects.get(code=code)
9597
is_code_ok = (code.client == client) and (code.user == user)
9698
except Exception:
@@ -103,15 +105,28 @@ def userinfo(claims, user):
103105
"""
104106
Fake function for setting OIDC_USERINFO.
105107
"""
106-
claims['given_name'] = 'John'
107-
claims['family_name'] = 'Doe'
108-
claims['name'] = '{0} {1}'.format(claims['given_name'], claims['family_name'])
109-
claims['email'] = user.email
110-
claims['email_verified'] = True
111-
claims['address']['country'] = 'Argentina'
108+
claims["given_name"] = "John"
109+
claims["family_name"] = "Doe"
110+
claims["name"] = "{0} {1}".format(claims["given_name"], claims["family_name"])
111+
claims["email"] = user.email
112+
claims["email_verified"] = True
113+
claims["address"]["country"] = "Argentina"
112114
return claims
113115

114116

117+
class FakeScopeClaims(ScopeClaims):
118+
info_pizza = (
119+
"Pizza",
120+
"Some description for the scope.",
121+
)
122+
123+
def scope_pizza(self):
124+
dic = {
125+
"pizza": "Margherita",
126+
}
127+
return dic
128+
129+
115130
def fake_sub_generator(user):
116131
"""
117132
Fake function for setting OIDC_IDTOKEN_SUB_GENERATOR.
@@ -123,8 +138,8 @@ def fake_idtoken_processing_hook(id_token, user, **kwargs):
123138
"""
124139
Fake function for inserting some keys into token. Testing OIDC_IDTOKEN_PROCESSING_HOOK.
125140
"""
126-
id_token['test_idtoken_processing_hook'] = FAKE_RANDOM_STRING
127-
id_token['test_idtoken_processing_hook_user_email'] = user.email
141+
id_token["test_idtoken_processing_hook"] = FAKE_RANDOM_STRING
142+
id_token["test_idtoken_processing_hook_user_email"] = user.email
128143
return id_token
129144

130145

@@ -133,32 +148,31 @@ def fake_idtoken_processing_hook2(id_token, user, **kwargs):
133148
Fake function for inserting some keys into token.
134149
Testing OIDC_IDTOKEN_PROCESSING_HOOK - tuple or list as param
135150
"""
136-
id_token['test_idtoken_processing_hook2'] = FAKE_RANDOM_STRING
137-
id_token['test_idtoken_processing_hook_user_email2'] = user.email
151+
id_token["test_idtoken_processing_hook2"] = FAKE_RANDOM_STRING
152+
id_token["test_idtoken_processing_hook_user_email2"] = user.email
138153
return id_token
139154

140155

141156
def fake_idtoken_processing_hook3(id_token, user, token, **kwargs):
142157
"""
143158
Fake function for checking scope is passed to processing hook.
144159
"""
145-
id_token['scope_of_token_passed_to_processing_hook'] = token.scope
160+
id_token["scope_of_token_passed_to_processing_hook"] = token.scope
146161
return id_token
147162

148163

149164
def fake_idtoken_processing_hook4(id_token, user, **kwargs):
150165
"""
151166
Fake function for checking kwargs passed to processing hook.
152167
"""
153-
id_token['kwargs_passed_to_processing_hook'] = {
154-
key: repr(value)
155-
for (key, value) in kwargs.items()
168+
id_token["kwargs_passed_to_processing_hook"] = {
169+
key: repr(value) for (key, value) in kwargs.items()
156170
}
157171
return id_token
158172

159173

160174
def fake_introspection_processing_hook(response_dict, client, id_token):
161-
response_dict['test_introspection_processing_hook'] = FAKE_RANDOM_STRING
175+
response_dict["test_introspection_processing_hook"] = FAKE_RANDOM_STRING
162176
return response_dict
163177

164178

0 commit comments

Comments
 (0)