You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Hi, i need to parse the token from JSON that is in request.event var (SocketIO) and for that i had to implement custom _decode_jwt_from_event and wire it it like this (yes that is bunch of code cannibalized from flask-jwt-extended):
"""This is flask-jwt-extended version 4 implementation of custom resolver"""fromfunctoolsimportwrapsfromflaskimportrequest, current_appfromwerkzeug.exceptionsimportBadRequestfromflask_jwt_extended.configimportconfigfromflask_jwt_extended.view_decoratorsimport_load_user, _decode_jwt_from_cookies, _decode_jwt_from_query_string, _decode_jwt_from_headers, _verify_token_is_fresh, _decode_jwt_from_jsonfromflask_jwt_extended.exceptionsimportNoAuthorizationErrorfromflask_jwt_extended.utilsimportdecode_token, get_unverified_jwt_headersfromflask_jwt_extended.internal_utilsimportverify_token_type, verify_token_not_blocklisted, custom_verification_for_tokenfromflaskimport_request_ctx_stackdef_decode_jwt_from_event(refresh):
ifnotrequest.event:
raiseNoAuthorizationError('Invalid content-type. Must have event.')
ifrefresh:
token_key=config.refresh_json_keyelse:
token_key=config.json_keytry:
encoded_token=request.event.get('args')[0].get(token_key, None)
ifnotencoded_token:
raiseBadRequest()
exceptBadRequest:
raiseNoAuthorizationError('Missing "{}" key in json data.'.format(token_key))
returnencoded_token, Nonedef_decode_jwt_from_request(locations, fresh, refresh=False):
# Figure out what locations to look for the JWT in this requestifisinstance(locations, str):
locations= [locations]
ifnotlocations:
locations=config.token_location# Get the decode functions in the order specified by locations.# Each entry in this list is a tuple (<location>, <encoded-token-function>)get_encoded_token_functions= [("json", lambda: _decode_jwt_from_event(refresh))]
forlocationinlocations:
iflocation=="cookies":
get_encoded_token_functions.append(
(location, lambda: _decode_jwt_from_cookies(refresh))
)
eliflocation=="query_string":
get_encoded_token_functions.append(
(location, _decode_jwt_from_query_string)
)
eliflocation=="headers":
get_encoded_token_functions.append((location, _decode_jwt_from_headers))
eliflocation=="json":
get_encoded_token_functions.append(
(location, lambda: _decode_jwt_from_json(refresh))
)
else:
raiseRuntimeError(f"'{location}' is not a valid location")
# Try to find the token from one of these locations. It only needs to exist# in one place to be valid (not every location).errors= []
decoded_token=Nonejwt_header=Nonejwt_location=Noneforlocation, get_encoded_token_functioninget_encoded_token_functions:
try:
encoded_token, csrf_token=get_encoded_token_function()
decoded_token=decode_token(encoded_token, csrf_token)
jwt_location=locationjwt_header=get_unverified_jwt_headers(encoded_token)
breakexceptNoAuthorizationErrorase:
errors.append(str(e))
# Do some work to make a helpful and human readable error message if no# token was found in any of the expected locations.ifnotdecoded_token:
iflen(locations) >1:
err_msg="Missing JWT in {start_locs} or {end_locs} ({details})".format(
start_locs=", ".join(locations[:-1]),
end_locs=locations[-1],
details="; ".join(errors),
)
raiseNoAuthorizationError(err_msg)
else:
raiseNoAuthorizationError(errors[0])
# Additional verifications provided by this extensionverify_token_type(decoded_token, refresh)
iffresh:
_verify_token_is_fresh(jwt_header, decoded_token)
verify_token_not_blocklisted(jwt_header, decoded_token)
custom_verification_for_token(jwt_header, decoded_token)
returndecoded_token, jwt_header, jwt_locationdefverify_jwt_in_request(optional=False, fresh=False, refresh=False, locations=None):
""" Verify that a valid JWT is present in the request, unless ``optional=True`` in which case no JWT is also considered valid. :param optional: If ``True``, do not raise an error if no JWT is present in the request. Defaults to ``False``. :param fresh: If ``True``, require a JWT marked as ``fresh`` in order to be verified. Defaults to ``False``. :param refresh: If ``True``, require a refresh JWT to be verified. :param locations: A location or list of locations to look for the JWT in this request, for example ``'headers'`` or ``['headers', 'cookies']``. Defaults to ``None`` which indicates that JWTs will be looked for in the locations defined by the ``JWT_TOKEN_LOCATION`` configuration option. """ifrequest.methodinconfig.exempt_methods:
returntry:
ifrefresh:
jwt_data, jwt_header, jwt_location=_decode_jwt_from_request(
locations, fresh, refresh=True
)
else:
jwt_data, jwt_header, jwt_location=_decode_jwt_from_request(
locations, fresh
)
exceptNoAuthorizationError:
ifnotoptional:
raise_request_ctx_stack.top.jwt= {}
_request_ctx_stack.top.jwt_header= {}
_request_ctx_stack.top.jwt_user= {"loaded_user": None}
_request_ctx_stack.top.jwt_location=Nonereturn# Save these at the very end so that they are only saved in the requet# context if the token is valid and all callbacks succeed_request_ctx_stack.top.jwt_user=_load_user(jwt_header, jwt_data)
_request_ctx_stack.top.jwt_header=jwt_header_request_ctx_stack.top.jwt=jwt_data_request_ctx_stack.top.jwt_location=jwt_locationreturnjwt_header, jwt_datadefjwt_required(optional=False, fresh=False, refresh=False, locations=None):
""" A decorator to protect a Flask endpoint with JSON Web Tokens. Any route decorated with this will require a valid JWT to be present in the request (unless optional=True, in which case no JWT is also valid) before the endpoint can be called. :param optional: If ``True``, allow the decorated endpoint to be accessed if no JWT is present in the request. Defaults to ``False``. :param fresh: If ``True``, require a JWT marked with ``fresh`` to be able to access this endpoint. Defaults to ``False``. :param refresh: If ``True``, requires a refresh JWT to access this endpoint. If ``False``, requires an access JWT to access this endpoint. Defaults to ``False``. :param locations: A location or list of locations to look for the JWT in this request, for example ``'headers'`` or ``['headers', 'cookies']``. Defaults to ``None`` which indicates that JWTs will be looked for in the locations defined by the ``JWT_TOKEN_LOCATION`` configuration option. """defwrapper(fn):
@wraps(fn)defdecorator(*args, **kwargs):
verify_jwt_in_request(optional, fresh, refresh, locations)
# Compatibility with flask < 2.0ifhasattr(current_app, "ensure_sync") andcallable(
getattr(current_app, "ensure_sync", None)
):
returncurrent_app.ensure_sync(fn)(*args, **kwargs)
returnfn(*args, **kwargs) # pragma: no coverreturndecoratorreturnwrapper
So my Q is, is there a better way how to do this^ (did i miss some thing?). And if there is none, will you be willing to merge a PR implementing lets say decorator custom_jwt_decoder to handle custom decoding?
The text was updated successfully, but these errors were encountered:
If I recall correctly, I tried something like that a long time ago, but ran into some issues I think around how to handle CSRF as required when dealing with custom decoding. There should be an issue around somewhere that detailed that, but I can't find it off hand :(
That said, this was a while ago, and the extension probably looked very different at that time. If there is a clean way to handle this in a more generalized way I would 100% welcome a PR, that would be amazing! If there are problems with a generalized way, a PR that adds websocket/sockio as a first class citizen to the extension (like we have with headers, cookies, etc) is also completely welcome!
Hi, i need to parse the token from JSON that is in
request.event
var (SocketIO) and for that i had to implement custom _decode_jwt_from_event and wire it it like this (yes that is bunch of code cannibalized fromflask-jwt-extended
):So my Q is, is there a better way how to do this^ (did i miss some thing?). And if there is none, will you be willing to merge a PR implementing lets say decorator
custom_jwt_decoder
to handle custom decoding?The text was updated successfully, but these errors were encountered: