Skip to content

Commit

Permalink
Getting public key from discovery URL to verify incoming JWT
Browse files Browse the repository at this point in the history
  • Loading branch information
alexis-judi committed Dec 24, 2019
1 parent 718fb0a commit 515e6e6
Showing 1 changed file with 31 additions and 3 deletions.
34 changes: 31 additions & 3 deletions falcon_auth/backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@
except ImportError:
pass

try:
# Optional dependencies for key discovery JWT backend
import urllib.request
import json
from jwt.algorithms import RSAAlgorithm
except ImportError:
pass

from falcon_auth.serializer import ExtendedJSONEncoder


Expand Down Expand Up @@ -153,13 +161,20 @@ class JWTAuthBackend(AuthBackend):
as value of ``iss`` field in the jwt payload. It will also be checked
against the ``iss`` field while decoding.
key_discovery_url(string, optional): Specifies the URL that will be used
to determine which public key to verify incoming JWT with. A JSON
array from this URL will be parsed and the ``kid`` (key id) field of
each object in the array will be compared against the ``kid`` field
of the incoming JWT.
"""

def __init__(self, user_loader, secret_key,
algorithm='HS256', auth_header_prefix='jwt',
leeway=0, expiration_delta=24 * 60 * 60,
audience=None, issuer=None,
verify_claims=None, required_claims=None):
verify_claims=None, required_claims=None,
key_discovery_url=None):

try:
jwt
Expand All @@ -176,6 +191,7 @@ def __init__(self, user_loader, secret_key,
self.issuer = issuer
self.verify_claims = verify_claims or ['signature', 'exp', 'nbf', 'iat']
self.required_claims = required_claims or ['exp', 'iat', 'nbf']
self.key_discovery_url = key_discovery_url

if 'aud' in self.verify_claims and not audience:
raise ValueError('Audience parameter must be provided if '
Expand All @@ -185,13 +201,25 @@ def __init__(self, user_loader, secret_key,
raise ValueError('Issuer parameter must be provided if '
'`iss` claim needs to be verified')

def _discover_key(self, key_id):
with urllib.request.urlopen(self.key_discovery_url) as url:
data = json.loads(url.read().decode())
for key in data["keys"]:
if key_id == key["kid"]:
self.secret_key = RSAAlgorithm.from_jwk(json.dumps(key))

def _decode_jwt_token(self, req):

# Decodes the jwt token into a payload
auth_header = req.get_header('Authorization')
token = self.parse_auth_token_from_request(auth_header=auth_header)

options = dict(('verify_' + claim, True) for claim in self.verify_claims)
if(self.key_discovery_url):
headers = jwt.get_unverified_header(token)
self._discover_key(headers["kid"])

options = dict(('verify_' + claim, True)
for claim in self.verify_claims)

options.update(
dict(('require_' + claim, True) for claim in self.required_claims)
Expand Down Expand Up @@ -511,7 +539,7 @@ def __init__(self, *backends):
for backend in backends:
if not isinstance(backend, AuthBackend):
raise ValueError(('Invalid authentication backend {0}.'
'Must inherit `falcon.auth.backends.AuthBackend`')
'Must inherit `falcon.auth.backends.AuthBackend`')
.format(backend))

self.backends = backends
Expand Down

0 comments on commit 515e6e6

Please sign in to comment.