Skip to content

Commit ba01884

Browse files
authored
Merge pull request #1 from davesque/master
Update upstream
2 parents 89cdeaa + 1aa5d68 commit ba01884

File tree

5 files changed

+77
-9
lines changed

5 files changed

+77
-9
lines changed

README.rst

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@ for Simple JWT's ``TokenObtainPairView`` and ``TokenRefreshView`` views:
6868
6969
urlpatterns = [
7070
...
71-
url(r'^api/token/$', TokenObtainPairView.as_view(), name='token_obtain_pair'),
72-
url(r'^api/token/refresh/$', TokenRefreshView.as_view(), name='token_refresh'),
71+
path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
72+
path('api/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
7373
...
7474
]
7575
@@ -81,7 +81,7 @@ signing key:
8181
8282
urlpatterns = [
8383
...
84-
url(r'^api/token/verify/$', TokenVerifyView.as_view(), name='token_verify'),
84+
path('api/token/verify/', TokenVerifyView.as_view(), name='token_verify'),
8585
...
8686
]
8787
@@ -151,6 +151,8 @@ Some of Simple JWT's behavior can be customized through settings variables in
151151
'ALGORITHM': 'HS256',
152152
'SIGNING_KEY': settings.SECRET_KEY,
153153
'VERIFYING_KEY': None,
154+
'AUDIENCE': None,
155+
'ISSUER': None,
154156
155157
'AUTH_HEADER_TYPES': ('Bearer',),
156158
'USER_ID_FIELD': 'id',
@@ -229,6 +231,16 @@ VERIFYING_KEY
229231
by the ``ALGORITHM`` setting, the ``VERIFYING_KEY`` setting must be set to a
230232
string which contains an RSA public key.
231233

234+
AUDIENCE
235+
The audience claim to be included in generated tokens and/or validated in
236+
decoded tokens. When set to ``None``, this field is excluded from tokens and
237+
is not validated.
238+
239+
ISSUER
240+
The issuer claim to be included in generated tokens and/or validated in
241+
decoded tokens. When set to ``None``, this field is excluded from tokens and
242+
is not validated.
243+
232244
AUTH_HEADER_TYPES
233245
The authorization header type(s) that will be accepted for views that require
234246
authentication. For example, a value of ``'Bearer'`` means that views
@@ -396,8 +408,8 @@ access token specific ``TokenObtainPairView`` and ``TokenRefreshView`` views:
396408
397409
urlpatterns = [
398410
...
399-
url(r'^api/token/$', TokenObtainSlidingView.as_view(), name='token_obtain'),
400-
url(r'^api/token/refresh/$', TokenRefreshSlidingView.as_view(), name='token_refresh'),
411+
path('api/token/', TokenObtainSlidingView.as_view(), name='token_obtain'),
412+
path('api/token/refresh/', TokenRefreshSlidingView.as_view(), name='token_refresh'),
401413
...
402414
]
403415

rest_framework_simplejwt/backends.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,14 @@
1616

1717

1818
class TokenBackend:
19-
def __init__(self, algorithm, signing_key=None, verifying_key=None):
19+
def __init__(self, algorithm, signing_key=None, verifying_key=None, audience=None, issuer=None):
2020
if algorithm not in ALLOWED_ALGORITHMS:
2121
raise TokenBackendError(format_lazy(_("Unrecognized algorithm type '{}'"), algorithm))
2222

2323
self.algorithm = algorithm
2424
self.signing_key = signing_key
25+
self.audience = audience
26+
self.issuer = issuer
2527
if algorithm.startswith('HS'):
2628
self.verifying_key = signing_key
2729
else:
@@ -31,7 +33,13 @@ def encode(self, payload):
3133
"""
3234
Returns an encoded token for the given payload dictionary.
3335
"""
34-
token = jwt.encode(payload, self.signing_key, algorithm=self.algorithm)
36+
jwt_payload = payload.copy()
37+
if self.audience is not None:
38+
jwt_payload['aud'] = self.audience
39+
if self.issuer is not None:
40+
jwt_payload['iss'] = self.issuer
41+
42+
token = jwt.encode(jwt_payload, self.signing_key, algorithm=self.algorithm)
3543
return token.decode('utf-8')
3644

3745
def decode(self, token, verify=True):
@@ -43,6 +51,8 @@ def decode(self, token, verify=True):
4351
signature check fails, or if its 'exp' claim indicates it has expired.
4452
"""
4553
try:
46-
return jwt.decode(token, self.verifying_key, algorithms=[self.algorithm], verify=verify)
54+
return jwt.decode(token, self.verifying_key, algorithms=[self.algorithm], verify=verify,
55+
audience=self.audience, issuer=self.issuer,
56+
options={'verify_aud': self.audience is not None})
4757
except InvalidTokenError:
4858
raise TokenBackendError(_('Token is invalid or expired'))

rest_framework_simplejwt/settings.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
'ALGORITHM': 'HS256',
1919
'SIGNING_KEY': settings.SECRET_KEY,
2020
'VERIFYING_KEY': None,
21+
'AUDIENCE': None,
22+
'ISSUER': None,
2123

2224
'AUTH_HEADER_TYPES': ('Bearer',),
2325
'USER_ID_FIELD': 'id',

rest_framework_simplejwt/state.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@
55

66
User = get_user_model()
77
token_backend = TokenBackend(api_settings.ALGORITHM, api_settings.SIGNING_KEY,
8-
api_settings.VERIFYING_KEY)
8+
api_settings.VERIFYING_KEY, api_settings.AUDIENCE, api_settings.ISSUER)

tests/test_backends.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,16 @@
5151
-----END PUBLIC KEY-----
5252
'''
5353

54+
AUDIENCE = 'openid-client-id'
55+
56+
ISSUER = 'https://www.myoidcprovider.com'
57+
5458

5559
class TestTokenBackend(TestCase):
5660
def setUp(self):
5761
self.hmac_token_backend = TokenBackend('HS256', SECRET)
5862
self.rsa_token_backend = TokenBackend('RS256', PRIVATE_KEY, PUBLIC_KEY)
63+
self.aud_iss_token_backend = TokenBackend('RS256', PRIVATE_KEY, PUBLIC_KEY, AUDIENCE, ISSUER)
5964
self.payload = {'foo': 'bar'}
6065

6166
def test_init(self):
@@ -95,6 +100,35 @@ def test_encode_rsa(self):
95100
),
96101
)
97102

103+
def test_encode_aud_iss(self):
104+
# Should return a JSON web token for the given payload
105+
original_payload = {'exp': make_utc(datetime(year=2000, month=1, day=1))}
106+
payload = original_payload.copy()
107+
108+
rsa_token = self.aud_iss_token_backend.encode(payload)
109+
110+
# Assert that payload has not been mutated by the encode() function
111+
self.assertEqual(payload, original_payload)
112+
113+
# Token could be one of 12 depending on header dict ordering
114+
self.assertIn(
115+
rsa_token,
116+
(
117+
'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJleHAiOjk0NjY4NDgwMCwiYXVkIjoib3BlbmlkLWNsaWVudC1pZCIsImlzcyI6Imh0dHBzOi8vd3d3Lm15b2lkY3Byb3ZpZGVyLmNvbSJ9.kSz7KyUZgpKaeQHYSQlhsE-UFLG2zhBiJ2MFCIvhstA4lSIKj3U1fdP1OhEDg7X66EquRRIZrby6M7RncqCdsjRwKrEIaL74KgC4s5PDXa_HC6dtpi2GhXqaLz8YxfCPaNGZ_9q9rs4Z4O6WpwBLNmMQrTxNno9p0uT93Z2yKj5hGih8a9C_CSf_rKtsHW9AJShWGoKpR6qQFKVNP1GAwQOQ6IeEvZenq_LSEywnrfiWp4Y5UF7xi42wWx7_YPQtM9_Bp5sB-DbrKg_8t0zSc-OHeVDgH0TKqygGEea09W0QkmJcROkaEbxt2LxJg9OuSdXgudVytV8ewpgNtWNE4g',
118+
'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJleHAiOjk0NjY4NDgwMCwiaXNzIjoiaHR0cHM6Ly93d3cubXlvaWRjcHJvdmlkZXIuY29tIiwiYXVkIjoib3BlbmlkLWNsaWVudC1pZCJ9.l-sJR5VKGKNrEn5W8sfO4tEpPq4oQ-Fm5ttQyqUkF6FRJHmCfS1TZIUSXieDLHmarnb4hdIGLr5y-EAbykiqYaTn8d25oT2_zIPlCYHt0DxxeuOliGad5l3AXbWee0qPoZL7YCV8FaSdv2EjtMDOEiJBG5yTkaqZlRmSkbfqu1_y2DRErv3X5LpfEHuKoum4jv5YpoCS6wAWDaWJ9cXMPQaGc4gXg4cuSQxb_EjiQ3QYyztHhG37gOu1J-r_rudaiiIk_VZQdYNfCcirp8isS0z2dcNij_0bELp_oOaipsF7uwzc6WfNGR7xP50X1a_K6EBZzVs0eXFxvl9b3C_d8A',
119+
'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiJvcGVuaWQtY2xpZW50LWlkIiwiZXhwIjo5NDY2ODQ4MDAsImlzcyI6Imh0dHBzOi8vd3d3Lm15b2lkY3Byb3ZpZGVyLmNvbSJ9.aTwQEIxSzhI5fN4ffQMzZ6h61Ur-Gzh_gPkgOyyWvMX890Z18tC2RisEjXeL5xDGKe2XiEAVtuJa9CjXB9eJoCxNN1k05C-ph82cco-0m_TbMbs0d1MFnfC9ESr4JKynP_Klxi8bi0iZMazduT15pH4UhRkEGsnp8rOKtlt_8_8UOGBJAzG34161lM4JbZqrZDit1DvQdGxaC0lmMgosKg3NDMECfkPe3pGLJ5F_su5yhQk0xyKNCjYyE2FNlilWoDV2KkGiCWdsFOgRMAJwHZ-cdgPg8Vyh2WthBNblsbRVfDrGtfPX0VuW5B0RhBhvI5Iut34P9kkxKRFo3NaiEg',
120+
'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiJvcGVuaWQtY2xpZW50LWlkIiwiaXNzIjoiaHR0cHM6Ly93d3cubXlvaWRjcHJvdmlkZXIuY29tIiwiZXhwIjo5NDY2ODQ4MDB9.w46s7u28LgsnmK6K_5O15q1SFkKeRgkkplFLi5idq1z7qJjXUi45qpXIyQw3W8a0k1fwa22WB_0XC1MTo22OK3Z0aGNCI2ZCBxvZGOAc1WcCUae44a9LckPHp80q0Hs03NvjsuRVLGXRwDVHrYxuGnFxQSEMbZ650-MQkfVFIXVzMOOAn5Yl4ntjigLcw8iPEqJPnDLdFUSnObZjRzK1M6mJf0-125pqcFsCJaa49rjdbTtnN-VuGnKmv9wV1GwevRQPWjx2vinETURVO9IyZCDtdaLJkvL7Z5IpToK5jrZPc1UWAR0VR8WeWfussFoHzJF86LxVxnqIeXnqOhq5SQ',
121+
'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3d3dy5teW9pZGNwcm92aWRlci5jb20iLCJhdWQiOiJvcGVuaWQtY2xpZW50LWlkIiwiZXhwIjo5NDY2ODQ4MDB9.Np_SJYye14vz0cvALvfYNqZXvXMD_gY6kIaxA458kbeu6veC_Ds45uWgjJFhTSYFAFWd3TB6M7qZbWgiO0ycION2-B9Yfgaf82WzNtPfgDhu51w1cbLnvuOSRvgX69Q6Z2i1SByewKaSDw25BaMv9Ty4DBdoCbG62qELnNKjDSQvuHlz8cRJv2I6xJBeOYgZV-YN8Zmxsles44a57Vvcj-DjVouHj5m4LperIxb9islNUQBPRTbvw1d_tR8O8ny0mQqbsWL7e2J-wfwdduVf1kVCPePkhHMM6GLhPIrSoTgMzZuRYLBJ61yphuDK98zTknNBM-Jtn5cMyBwP9JBJvA',
122+
'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3d3dy5teW9pZGNwcm92aWRlci5jb20iLCJleHAiOjk0NjY4NDgwMCwiYXVkIjoib3BlbmlkLWNsaWVudC1pZCJ9.KJcWZtEluPrkRbYj2i_QmdWpZqGZt63Q8nO4QAJ4B4418ZfmgB6A54_vUmWd3Xc18DYgReLoNPlaOuRXtR7rzlMk0-ADjV0bsca5NwTNAV2F-gR9Xsr9iFlcPMNAYf4CAs85gg7deMIxlbGTAaoft__58ah2_vdd8o_nem1PdzsPC198AYtcwnIV206qpeCNR8S_ZTU46OaHwCoySVRx9E7tNG13YSCmGvBaEqgQHKH82qLXo0KivFrjGmGP0xePFG1B8HCZl-LH1euXGGCp6S48q-tmepX5GJwvzaZNBam4pfxQ0GIHa7z11e7sEN-196iDPCK8NzDcXFwHOAnyaA',
123+
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjk0NjY4NDgwMCwiYXVkIjoib3BlbmlkLWNsaWVudC1pZCIsImlzcyI6Imh0dHBzOi8vd3d3Lm15b2lkY3Byb3ZpZGVyLmNvbSJ9.MfhVcFN-9Rd0j11CLtxopzREKdyJH1loSpD4ibjP6Aco4-iM5C99B6gliPgOldtuevhneXV2I7NGhmZFULaYhulcLrAgKe3Gj_TK-sHvwb62e14ArugmK_FAhN7UqbX8hU9wP42LaWXqA7ps4kkJSx-sfgHqMzCKewlAZWwyZBoFgWEgoheKZ7ILkSGf0jzBZlS_1R0jFRSrlYD9rI1S4Px-HllS0t32wRCSbzkp6aVMRs46S0ePrN1mK3spsuQXtYhE2913ZC7p2KwyTGfC2FOOeJdRJknh6kI3Z7pTcsjN2jnQN3o8vPEkN3wl7MbAgAsHcUV45pvyxn4SNBmTMQ',
124+
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjk0NjY4NDgwMCwiaXNzIjoiaHR0cHM6Ly93d3cubXlvaWRjcHJvdmlkZXIuY29tIiwiYXVkIjoib3BlbmlkLWNsaWVudC1pZCJ9.3NjgS14SGFyJ6cix2XJZFPlcyeSu4LSduEMUIH0grJuCljbhalyoib5s4JnBaK4slKrQv1WHlhnKB737hX1FF7_EwQM3toCf--DBjrIuq5cYK3rzcn71JDe_op3CvClwpVyxd2vQZtQfP_tWqN6cNTuWF8ZQ0rJGug6Zb-NeE5h68YK_9tXLZC_i5anyjjAVONOc3Nd-TeIUBaLQKQXOddw0gcTcA7vg3uS0gXTEDq-_ZkF-v9bn1ua4_lgRPbuaYvrBFbXSCEdvNORPfPz4zfL3XU09q0gmnmXC9nxjUFVX4BjkP_YiCCO42sqUKY4y7STTB_IkK_04e2wntonVZA',
125+
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3d3dy5teW9pZGNwcm92aWRlci5jb20iLCJleHAiOjk0NjY4NDgwMCwiYXVkIjoib3BlbmlkLWNsaWVudC1pZCJ9.b4pdohov81oqzLyCIp4y7e4VYz7LSez7bH0t1o0Zwzau1uXPYXcasT9lxxNMEEiZwHIovPLyWQ6XvF0bMWTk9vc4PyIpkLqsLBJPsuQ-wYUOD04fECmqUX_JaINqm2pPbohTcOQwl0xsE1UMIKTFBZDL1hEXGEMdW9lrPcXilhbC1ikyMpzsmVh55Q_wL2GqydssnOOcDQTqEkWoKvELJJhBcE-YuQkUp8jEVhF3VZ4jEZkzCErTlyXcfe1qXZHkWtw2QNo9s_SfLuRy_fACOo8XE9pHBoE7rqiSm-FmISgiLO1Jj3Pqq-abjN4SnAbU7PZWcV3fUoO1eYLGORmAcw',
126+
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3d3dy5teW9pZGNwcm92aWRlci5jb20iLCJhdWQiOiJvcGVuaWQtY2xpZW50LWlkIiwiZXhwIjo5NDY2ODQ4MDB9.yDGMBeee4hj8yDtEvVtIsS4tnkPjDMQADTkNh74wtb3oYPgQNqMRWKngXiwjvW2FmnsCUue2cFzLgTbpqlDq0QKcBP0i_UwBiXk9m2wLo0WRFtgw2zNHYSsowu26sFoEjKLgpPZzKrPlU4pnxqa8u3yqg8vIcSTlpX8t3uDqNqhUKP6x-w6wb25h67XDmnORiMwhaOZE_Gs9-H6uWnKdguTIlU1Tj4CjUEnZgZN7dJORiDnO_vHiAyL5yvRjhp5YK0Pq-TtCj5kWoJsjQiKc4laIcgofAKoq_b62Psns8MhxzAxwM7i0rbQZXXYB0VKMUho88uHlpbSWCZxu415lWw',
127+
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJvcGVuaWQtY2xpZW50LWlkIiwiaXNzIjoiaHR0cHM6Ly93d3cubXlvaWRjcHJvdmlkZXIuY29tIiwiZXhwIjo5NDY2ODQ4MDB9.BHSCjFeXS6B7KFJi1LpQMEd3ib4Bp9FY3WcB-v7dtP3Ay0SxQZz_gxIbi-tYiNCBQIlfKcfq6vELOjE1WJ5zxPDQM8uV0Pjl41hqYBu3qFv4649a-o2Cd-MaSPUSUogPxzTh2Bk4IdM3sG1Zbd_At4DR_DQwWJDuChA8duA5yG2XPkZr0YF1ZJ326O_jEowvCJiZpzOpH9QsLVPbiX49jtWTwqQGhvpKEj3ztTLFo8VHO-p8bhOGEph2F73F6-GB0XqiWk2Dm1yKAunJCMsM4qXooWfaX6gj-WFhPI9kEXNFfGmPal5i1gb17YoeinbdV2kjN42oxast2Iaa3CMldw',
128+
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJvcGVuaWQtY2xpZW50LWlkIiwiZXhwIjo5NDY2ODQ4MDAsImlzcyI6Imh0dHBzOi8vd3d3Lm15b2lkY3Byb3ZpZGVyLmNvbSJ9.s6sElpfKL8WHWfbD_Kbwiy_ip4O082V8ElZqwugvDpS-7yQ3FTvQ3WXqtVAJc-fBZe4ZsBnrXUWwZV0Nhoe6iWKjEjTPjonQWbWL_WUJmIC2HVz18AOISnqReV2rcuLSHQ2ckhsyktlE9K1Rfj-Hi6f3HzzzePEgTsL2ZdBH6GrcmJVDFKqLLrkvOHShoPp7rcwuFBr0J_S1oqYac5O0B-u0OVnxBXTwij0ThrTGMgVCp2rn6Hk0NvtF6CE49Eu4XP8Ue-APT8l5_SjqX9GcrjkJp8Gif_oyBheL-zRg_v-cU60X6qY9wVolO8WodVPSnlE02XyYhLVxvfK-w5129A',
129+
),
130+
)
131+
98132
def test_decode_hmac_with_no_expiry(self):
99133
no_exp_token = jwt.encode(self.payload, SECRET, algorithm='HS256')
100134

@@ -208,3 +242,13 @@ def test_decode_rsa_success(self):
208242
token = jwt.encode(self.payload, PRIVATE_KEY, algorithm='RS256').decode('utf-8')
209243

210244
self.assertEqual(self.rsa_token_backend.decode(token), self.payload)
245+
246+
def test_decode_aud_iss_success(self):
247+
self.payload['exp'] = aware_utcnow() + timedelta(days=1)
248+
self.payload['foo'] = 'baz'
249+
self.payload['aud'] = AUDIENCE
250+
self.payload['iss'] = ISSUER
251+
252+
token = jwt.encode(self.payload, PRIVATE_KEY, algorithm='RS256').decode('utf-8')
253+
254+
self.assertEqual(self.aud_iss_token_backend.decode(token), self.payload)

0 commit comments

Comments
 (0)