This release includes a lot of breaking changes that have been a long time coming, and will require some manual intervention to upgrade your application. Breaking changes are never fun, but I really believe they are for the best. As a result of all these changes, this extension should be simpler to use, provide more flexibility, and allow for easier additions to the API without introducing further breaking changes. Here is everything you will need to be aware of when upgrading to 4.0.0.
The
JWT_USER_CLAIMSconfiguration option has been removed. Now when creating JWTs with additional claims, those claims are put on the top level of the token, instead of inside the the nesteduser_claimsdictionary. This has the very important benefit of allowing you to override reserved claims (such asnbf) which was not previously possible in this extension.IMPORTANT NOTE:
This has the unfortunate side effect that any existing JWTs your application is using will not work correctly if they utilize additional claims. We strongly suggest changing your secret key to force all users to get the new format of JWTs. If that is not feasible for your application you could build a shim to handle both the old JWTs which store additional claims in the
user_claimskey, and the new format where additional claims are now stored at the top level, until all the JWTs have had a chance to cycle to the new format.The default
JWT_IDENTITY_CLAIMoption is nowsubinstead ofidentity.
- Dropped support for everything before Python 3.6 (including Python 2).
- Requires PyJWT >= 2.0.0.
- Depreciation warnings in
3.25.2have been removed and are now errors:- The
JWT_CSRF_HEADER_NAMEoption has removed. - The
jwt.expired_token_loaderwill error if the callback does not take an argument for the expired token header and expired token payload. - The
jwt.decode_key_loaderwill error if the callback does not take an argument for the unverified_headers and the unverified_payload.
- The
- Calling
get_jwt(),get_jwt_header(), orget_jwt_identity()will raise aRuntimeErrorwhen called outside of a protected context (ie if you forgot@jwt_required()orverify_jwt_in_request()). Previously these calls would returnNone. - Calling
get_jwt()orget_jwt_header()will return an empty dictionary if called from an optionally protected endpoint. Previously this would returnNone. - Calling
get_current_user()orcurrent_userwill raise aRuntimeErrorif no@jwt.user_lookup_loadercallback is defined.
- All occurrences of
blacklisthave been renamed toblocklist - The
JWT_BLACKLIST_ENABLEDoption has been removed. If you do not want to check a JWT against your blocklist, do not register a callback function with@jwt.token_in_blocklist_loader. - The
JWT_BLACKLIST_TOKEN_CHECKSoption has been removed. If you don't want to check a given token type against the blocklist, specifically ignore it in your callback function by checking thejwt_payload["type"]and short circuiting accordingly.jwt_payload["type"]will be either"access"or"refresh".
Renamed
@jwt.claims_verification_loaderto@jwt.token_verification_loaderRenamed
@jwt.claims_verification_failed_loaderto@jwt.token_verification_failed_loaderRenamed
@jwt.user_claims_loaderto@jwt.additional_claims_loaderRenamed
@jwt.user_in_blacklist_loaderto@jwt.user_in_blocklist_loaderRenamed
@jwt.user_loader_callback_loaderto@jwt.user_lookup_loaderRenamed
@jwt.user_loader_error_loaderto@jwt.user_lookup_error_loaderThe following callback functions have all been changed to take two arguments. Those arguments are the
jwt_headersandjwt_payload.@jwt.needs_fresh_token_loader@jwt.revoked_token_loader@jwt.user_lookup_loader@jwt.user_lookup_error_loader@jwt.expired_token_loader@jwt.token_in_blocklist_loader@jwt.token_verification_loader@jwt.token_verification_failed_loader
@jwt.revoked_token_loader def revoked_token_response(jwt_header, jwt_payload): return jsonify(msg=f"I'm sorry {jwt_payload['sub']} I can't let you do that")
The arguments for
@jwt.decode_key_loaderhave been reversed to be consistent with the rest of the application. Previously the arguments were(jwt_payload, jwt_headers). Now they are(jwt_headers, jwt_payload).
- All view decorators have been moved to a single decorator:
@jwt_requiredis now@jwt_required()@jwt_optionalis now@jwt_required(optional=True)@fresh_jwt_requiredis now@jwt_required(fresh=True)@jwt_refresh_token_requiredis now@jwt_required(refresh=True)
- All additional
verify_jwt_in_requestfunctions have been moved to a single method: verify_jwt_in_request_optional()is nowverify_jwt_in_request(optional=True)verify_jwt_refresh_token_in_request()is nowverify_jwt_in_request(refresh=True)verify_fresh_jwt_in_request()is nowverify_jwt_in_request(fresh=True)
- All additional
- Renamed
get_raw_jwt()toget_jwt() - Renamed
get_raw_jwt_headers()toget_jwt_headers() - Removed
get_jwt_claims(). Useget_jwt()instead. - The
headersargument increate_access_token()andcreate_refresh_token()has been renamed toadditional_headers.- If you pass in the
additional_headers, it will now be merged with the headers returned by the@jwt.additional_headers_loadercallback, with ties going to theadditional_headersargument.
- If you pass in the
- The
user_claimsargument increate_access_token()andcreate_refresh_token()has been renamed toadditional_claims.- If you pass in the
additional_claimsoption, it will now be merged with the claims returned by the@jwt.additional_claims_loadercallback, with ties going to theadditional_claimsargument.
- If you pass in the
- The
JWT_VERIFY_AUDIENCEoption has been removed. If you do not want to verify the JWT audience (aud) claim, simply do not set theJWT_DECODE_AUDIENCEoption. - The
JWT_CLAIMS_IN_REFRESH_TOKENoption has been removed. Additional claims will now always be put in the JWT regardless of if it is an access or refresh tokens. If you don't want additional claims in your refresh tokens, do not include any additional claims when creating the refresh token. - Removed
UserLoadErrorfromflask_jwt_extended.exceptions. UseUserLookupErrorinstead.
- Add
locationsargument to@jwt_required()andverify_jwt_in_request. This will allow you to override theJWT_LOCATIONSoption on a per route basis. - Revamped and cleaned up documentation. It should be clearer how to work with this extension both on the backend and frontend now.
- Lots of code cleanup behind the scenes.