Files
edx-platform/openedx/core/lib/jwt.py
2025-01-23 15:55:58 -03:00

92 lines
2.7 KiB
Python

"""
JWT Token handling and signing functions.
"""
import json
from time import time
from django.conf import settings
from jwkest import Expired, Invalid, MissingKey, jwk
from jwkest.jws import JWS
def create_jwt(lms_user_id, expires_in_seconds, additional_token_claims, now=None):
"""
Produce an encoded JWT (string) indicating some temporary permission for the indicated user.
What permission that is must be encoded in additional_claims.
Arguments:
lms_user_id (int): LMS user ID this token is being generated for
expires_in_seconds (int): Time to token expiry, specified in seconds.
additional_token_claims (dict): Additional claims to include in the token.
now(int): optional now value for testing
"""
now = now or int(time())
payload = {
'lms_user_id': lms_user_id,
'exp': now + expires_in_seconds,
'iat': now,
'iss': settings.TOKEN_SIGNING['JWT_ISSUER'],
'version': settings.TOKEN_SIGNING['JWT_SUPPORTED_VERSION'],
}
payload.update(additional_token_claims)
return _encode_and_sign(payload)
def _encode_and_sign(payload):
"""
Encode and sign the provided payload.
The signing key and algorithm are pulled from settings.
"""
keys = jwk.KEYS()
serialized_keypair = json.loads(settings.TOKEN_SIGNING['JWT_PRIVATE_SIGNING_JWK'])
keys.add(serialized_keypair)
algorithm = settings.TOKEN_SIGNING['JWT_SIGNING_ALGORITHM']
data = json.dumps(payload)
jws = JWS(data, alg=algorithm)
return jws.sign_compact(keys=keys)
def unpack_jwt(token, lms_user_id, now=None):
"""
Unpack and verify an encoded JWT.
Validate the user and expiration.
Arguments:
token (string): The token to be unpacked and verified.
lms_user_id (int): LMS user ID this token should match with.
now (int): Optional now value for testing.
Returns a valid, decoded json payload (string).
"""
now = now or int(time())
payload = _unpack_and_verify(token)
if "lms_user_id" not in payload:
raise MissingKey("LMS user id is missing")
if "exp" not in payload:
raise MissingKey("Expiration is missing")
if payload["lms_user_id"] != lms_user_id:
raise Invalid("User does not match")
if payload["exp"] < now:
raise Expired("Token is expired")
return payload
def _unpack_and_verify(token):
"""
Unpack and verify the provided token.
The signing key and algorithm are pulled from settings.
"""
keys = jwk.KEYS()
keys.load_jwks(settings.TOKEN_SIGNING['JWT_PUBLIC_SIGNING_JWK_SET'])
decoded = JWS().verify_compact(token.encode('utf-8'), keys)
return decoded