code: def decode_jwt(encoded_token, secret, algorithm, identity_claim_key,
                 user_claims_key, csrf_value=None, audience=None,
                 leeway=0, allow_expired=False):
      
      options = {}
      if allow_expired:
          options['verify_exp'] = False
  
      # This call verifies the ext, iat, nbf, and aud claims
      data = jwt.decode(encoded_token, secret, algorithms=[algorithm], audience=audience,
                        leeway=leeway, options=options)
  
      # Make sure that any custom claims we expect in the token are present
      if 'jti' not in data:
          data['jti'] = None
      if identity_claim_key not in data:
          raise JWTDecodeError("Missing claim: {}".format(identity_claim_key))
      if 'type' not in data:
          data['type'] = 'access'
      if data['type'] not in ('refresh', 'access'):
          raise JWTDecodeError("Missing or invalid claim: type")
      if data['type'] == 'access':
          if 'fresh' not in data:
              data['fresh'] = False
      if user_claims_key not in data:
          data[user_claims_key] = {}
      if csrf_value:
          if 'csrf' not in data:
              raise JWTDecodeError("Missing claim: csrf")
          if not safe_str_cmp(data['csrf'], csrf_value):
              raise CSRFError("CSRF double submit tokens do not match")
      return data
docs: Decodes an encoded JWT
  
      :param encoded_token: The encoded JWT string to decode
      :param secret: Secret key used to encode the JWT
      :param algorithm: Algorithm used to encode the JWT
      :param identity_claim_key: expected key that contains the identity
      :param user_claims_key: expected key that contains the user claims
      :param csrf_value: Expected double submit csrf value
      :param audience: expected audience in the JWT
      :param leeway: optional leeway to add some margin around expiration times
      :param allow_expired: Options to ignore exp claim validation in token
      :return: Dictionary containing contents of the JWT