Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow list of valid audiences (#205 continued) #306

Merged
merged 11 commits into from
Nov 27, 2017
2 changes: 2 additions & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,5 @@ Patches and Suggestions
- Wouter Bolsterlee <[email protected]>

- Michael Davis <[email protected]> <[email protected]>

- Vinod Gupta <[email protected]>
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ This project adheres to [Semantic Versioning](http://semver.org/).

- Dropped support for python 2.6 and 3.3 [#297][297]

- Audience parameter now supports Iterables [#205][205]

### Fixed

### Added

[v1.5.3][1.5.3]
Expand Down
1 change: 0 additions & 1 deletion jwt/api_jws.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import binascii
import json
import warnings

from collections import Mapping

from .algorithms import (
Expand Down
15 changes: 10 additions & 5 deletions jwt/api_jwt.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import json
import warnings

from calendar import timegm
from collections import Mapping
from collections import Iterable, Mapping
from datetime import datetime, timedelta

from .api_jws import PyJWS
Expand Down Expand Up @@ -103,8 +102,8 @@ def _validate_claims(self, payload, options, audience=None, issuer=None,
if isinstance(leeway, timedelta):
leeway = leeway.total_seconds()

if not isinstance(audience, (string_types, type(None))):
raise TypeError('audience must be a string or None')
if not isinstance(audience, (string_types, type(None), Iterable)):
raise TypeError('audience must be a string, Iterable, or None')

self._validate_required_claims(payload, options)

Expand Down Expand Up @@ -177,9 +176,15 @@ def _validate_aud(self, payload, audience):
raise InvalidAudienceError('Invalid claim format in token')
if any(not isinstance(c, string_types) for c in audience_claims):
raise InvalidAudienceError('Invalid claim format in token')
if audience not in audience_claims:

if isinstance(audience, string_types):
audience = [audience]

if not any(aud in audience_claims for aud in audience):
raise InvalidAudienceError('Invalid audience')

return
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This return is extraneous since we're at the end of the function, right?


def _validate_iss(self, payload, issuer):
if issuer is None:
return
Expand Down
1 change: 0 additions & 1 deletion jwt/contrib/algorithms/pycrypto.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import Crypto.Hash.SHA256
import Crypto.Hash.SHA384
import Crypto.Hash.SHA512

from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5

Expand Down
1 change: 0 additions & 1 deletion tests/test_api_jws.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@

import json

from decimal import Decimal

from jwt.algorithms import Algorithm
Expand Down
20 changes: 18 additions & 2 deletions tests/test_api_jwt.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@

import json
import time

from calendar import timegm
from datetime import datetime, timedelta
from decimal import Decimal
Expand Down Expand Up @@ -92,7 +91,7 @@ def test_decode_with_invalid_audience_param_throws_exception(self, jwt):
jwt.decode(example_jwt, secret, audience=1)

exception = context.value
assert str(exception) == 'audience must be a string or None'
assert str(exception) == 'audience must be a string, Iterable, or None'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably ok to just say iterable here instead of Iterable. I think most people have an understanding of what an iterable is with regards to Python.


def test_decode_with_nonlist_aud_claim_throws_exception(self, jwt):
secret = 'secret'
Expand Down Expand Up @@ -281,6 +280,23 @@ def test_check_audience_when_valid(self, jwt):
token = jwt.encode(payload, 'secret')
jwt.decode(token, 'secret', audience='urn:me')

def test_check_audience_list_when_valid(self, jwt):
payload = {
'some': 'payload',
'aud': 'urn:me'
}
token = jwt.encode(payload, 'secret')
jwt.decode(token, 'secret', audience=['urn:you', 'urn:me'])

def test_raise_exception_invalid_audience_list(self, jwt):
payload = {
'some': 'payload',
'aud': 'urn:me'
}
token = jwt.encode(payload, 'secret')
with pytest.raises(InvalidAudienceError):
jwt.decode(token, 'secret', audience=['urn:you', 'urn:him'])

def test_check_audience_in_array_when_valid(self, jwt):
payload = {
'some': 'payload',
Expand Down
1 change: 0 additions & 1 deletion tests/utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import os
import struct

from calendar import timegm
from datetime import datetime

Expand Down