vimalloc / flask-jwt-extended Goto Github PK
View Code? Open in Web Editor NEWAn open source Flask extension that provides JWT support (with batteries included)!
Home Page: http://flask-jwt-extended.readthedocs.io/en/stable/
License: MIT License
An open source Flask extension that provides JWT support (with batteries included)!
Home Page: http://flask-jwt-extended.readthedocs.io/en/stable/
License: MIT License
Is it possible to protect a route with different instance of JWT token?
Hi,
Thanks for this very helpful API.
I would like to use my own database to store the tokens and a service to access it. Simplekv breaks this architecture.
Is it possible to allow to plug a custom object rather than force us to use a simplekv.KeyValueStore ?
Hello.
flask_jwt has a really useful feature - identity_callback. It is calling every time jwt_required decorator is triggered. In this user-defined call-back you may e.g. initialize a g.auth_user variable and use it from the protected endpoint.
In flask_jwt_extended to achieve the same I have to manually call get_jwt_identity() from every protected endpoint and\or add an additional decorator to every function, which is not very handy having hundreds of such points in the existing code.
Is there a way to trigger some functionality from the every protected endpoint automatically?
Thanks!
Is there an easy way to integrate this package with flask-restless?
Hi, i've been using the awesome extension for my flask app since its released.
now i decided to move onto Sanic which is pretty new but is there some how this extension has supports for sanic?
Thanks.
Attempting to access a jwt_required endpoint with csrf enabled and allowing JWTs in headers or cookies results in the CSRF errors getting silently eaten. This happens because it is raising a NoAuthorizationError error for invalid CSRF, and this is the same error we are ignoring and later re-throwing if checking for the JWT in both cookies and headers.
Solution should be simple, add a new CSRF error. Only question is should this CSRF error be handled by the same unauthorized_loader
callback, or should we have a new callback specific to CSRF errors. For now, I'm thinking the former makes the most sense.
flask-jwt describes usage of the jwt-required-claims and jwt-certify-claims but neither are implemented.
It would be good if flask-jwt-extended implemented it.
These optionss are all available in the code and usable in app.config, but are not in the options page for the documentation.
COOKIE_SECURE = False
ACCESS_COOKIE_NAME = 'access_token_cookie'
REFRESH_COOKIE_NAME = 'refresh_token_cookie'
ACCESS_COOKIE_PATH = None
REFRESH_COOKIE_PATH = None
Hi there,
I am glad I encountered this open source, it has met all my basic needs in protecting
my backend endpoints. Thanks to all the examples in the documentation!
I have a question though, I don't know if this is because I don't have enough knowledge on JWTokens but I am curious on how the exp field works.
Lets say a user logged in and got issued a jwt that expires in 3 seconds.
If the user tries to access the endpoint after 3 seconds has past, it is considered an old token and the user is required to log in again. This part I understand.
What happens if the user makes consequent requests within the 3 seconds. Does the exp field get updated automatically? If not, does the client have to take care of the refreshing
of the token implicitly?
Thanks once again for the great open source!
Is there conflicts when using flask-wtf csrf vs flask-jwt-extended csrf? Especially when it comes to error handling both of these differently? Or are these both the same? Thanks!
According to the security benefit cited here : https://www.sjoerdlangkemper.nl/2016/04/14/preventing-csrf-with-samesite-cookie-attribute/
Is it possible to include this in global options and on cookies ?
Thanks for the hard work.
Looking though the docs it appears this extension is similar to flask-jwt but not identical. Would be nice to have a section in the docs on how to port to the newer library and any associated concerns there might be. So far I've noticed:
JWT --> JWTManager(app)
Need to create an auth view method
Config:
No leeway
Is that a good start?
Best practice question..
Should we create a new refresh token as well and return it back to the end user when we refresh a jwt token? This way the client will have a new refresh_token with a new_access token... if the session expires and access_token cannot be used, they can use the new refresh token to get another token instead of using the stale refresh token....
when trying to use create_access_token
i get an error
KeyError: 'csrf'
referring to:
def _get_csrf_token(encoded_token):
secret = _get_secret_key()
algorithm = get_algorithm()
token = _decode_jwt(encoded_token, secret, algorithm)
return token['csrf']
im running the app on my localhost.
i'm pretty much following the jwt_in_cookie example
any suggestions?
@vimalloc Should we have a log of changes to clarify if newer releases might break previous versions?
What is the point of using a blacklist? Shouldn't this be dealt with revoking access on the user management database side?
As the title suggests, I've set JWT_TOKEN_LOCATION
to 'cookies'
but when I try to access a protected endpoint I get: "msg": "Missing Authorization Header"
PS Migrating from a forked version of flask-jwt where I was using cookies as well. Thanks for this project @vimalloc!
Hi, I´m a user of Flask JWT and I´ve been trying to understand more about this extended version.
Right now, we need to send a key in the same JSON that contains the access_token key. I could not find a easy way to do this using the regular Flask-JWT.
Looking at your extended version, I could not find a methodlike the "identity" method used in flask-jwt.
We are using this method to check the payload against our database, so only after that, the resource will be avaliable for the user.
( authenticate method creates the token -- identity method gets the payload and verify it against our db)
How can we do that using your version?
We´re new to this, so maybe we´re missing some things here hehe
A lot of articles talk about to leave JWT because the architecture is vulnerable.
http://blog.websecurify.com/2017/02/hacking-json-web-tokens.html
This library (and PyJWT) are vulnerable to this hack ?
Currently this loops over every item in the store, decodes said item, and checks if the username matches the provided username. This is obviously very inefficient.
Ideally, it would be nice if we had an additional field in the store where the key was the identity (instead of the jti), and the value for that field was a list of all the jti's in this store which belonged to this identity. However, the underlying library we are using (simplekv) does not support any form of mutual exclusion, so this would be prone to race conditions. We cannot throw our own mutex around these calls, because we do not know if the flask app will be running on multiple processes or on multiple servers.
I think the ideal solution to this would be to try to patch simplekv with a 'MutexMixin` (much like the TTLMixin) which allows you to use a with
statement to create a mutex lock using whatever locking mechanisms are provided by the underlying store (if applicable).
We would need to reach out to the simplekv developer, and see if he would be interested in a pull requests (if not, we could always fork).
With how much work that would be needed to go into this, it will probably be awhile before any progress is made.
Want to allow the option to store the jwt in cookie instead of local storage on a web browser (see https://stormpath.com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage).
If we do it that way, we need to enable additional security to prevent against csrf (see https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet)
There are probably other ways and options to protect against this stuff as well. I should do some more research on the matter before starting on this.
Going off the Flask-JWT library. Should/would it be a good idea to have an auth url rule?
Hi,
I've been wondering whether there's a particular reason why you decided to have a configuration option that determines the location of the JWT when checking a request with jwt_required
. Instead, wouldn't it be better to pass a list of locations to the decorator?
@jwt_required(['cookie', 'header'])
def view():
...
This decorator would look for a cookie first and, if there's no cookie with the JWT, also check the header. Of course, only checking one of the two would be possible, too, and there could be a configurable default location.
What do you think?
I create both refresh token and access token when login. However, when logout, those tokens should be revoked at the same time, without affecting other tokens owned by the user.
I look at the doc. Like:
# Endpoint for revoking the current users access token
@app.route('/logout', methods=['POST'])
@jwt_required
def logout():
try:
_revoke_current_token()
except KeyError:
return jsonify({
'msg': 'Access token not found in the blacklist store'
}), 500
return jsonify({"msg": "Successfully logged out"}), 200
# Endpoint for revoking the current users refresh token
@app.route('/logout2', methods=['POST'])
@jwt_refresh_token_required
def logout2():
try:
_revoke_current_token()
except KeyError:
return jsonify({
'msg': 'Refresh token not found in the blacklist store'
}), 500
return jsonify({"msg": "Successfully logged out"}), 200
Is there a way to revoke both?
Currently only supports python3. Use six and tox to get compatibility and unittests for python2 and python3
Should get rid of the submodule stuff and just include the theme in the requirements.txt
Should have optional support for addition claims (sub, aud, etc). Have an option for using pyjwt leeway as well when decoding tokens
Hello
Big fan of the package!
I've been trying for few hours now to find an efficient way to store more information to the tokens when JWT_BLACKLIST_ENABLED is True
. As far as I can tell the only way to access the token is using get_stored_token(jti), but I don't have access to the jti.
I am trying to avoid using get_stored_tokens(identity) and going over the list.
Any suggestions?
Thanks
from jwt.algorithms import requires_cryptography
It would be handy if there was a document that highlighted the difference between this package and flask-jwt. Specifically, it would be useful to know what has to be done differently to use this in place of flask-jwt. It appears that this isn't a drop-in replacement. In particular, the identity callback and login route don't seem to be present here.
Hi, I found an issue with jwt_required
decorator, I don't understand why works when I used like:
@custom_api.route('/resellers/<token>/registrations', methods=['GET'])
@jwt_required
def get_resellers(token):
...
but NOT when:
I'm using https://flask-restless.readthedocs.io/en/stable/ where I can use methods as preprocessor
@classmethod
@jwt_required
def get_many_preprocessor(cls, search_params=None, **kw):
print "Here not work"
This worked me with flask-jwt
, what could be?
def _decode_jwt_from_cookies(type):
if type == 'access':
cookie_key = get_access_cookie_name()
else:
cookie_key = get_refresh_cookie_name()
.......
if get_cookie_csrf_protect() and request.method in get_csrf_request_methods():
csrf_header_key = get_csrf_header_name()
....
When you renew token (with a refresh token), you must (on frontend) replace in headers the CSRF content by the content of the REFRESH-CSRF cookie, because only csrf header name can be specified (code above)
It would no be better to create an option to define a refresh_csrf_header or specify this in documentation ?
By default refresh_csrf_header_name could be equal to csrf_header_name, it's an idea.
Hey, really like the library. Very useful!
I want to use the identity from the JWT in another decorator.
Currently I have (details left out, but you get the gist):
@app.Route('/api/,,,'. methods=['GET']
@jwt_required
@service_supported(str(get_jwt_identity()), "SERVICE")
def method1():
pass
In this case the get_jwt_identity() returns {}.
When placed inside method1(), I get the correct result.
Any ideas?
Please see these config options:
https://github.com/vimalloc/flask-jwt-extended/blob/master/flask_jwt_extended/config.py#L27
Not being respected in this file:
https://github.com/vimalloc/flask-jwt-extended/blob/master/flask_jwt_extended/utils.py#L396
This means the cookies will be deleted every time the browser is closed, requiring the user to re-login. Is there something I am missing about expiring these cookies at the end of the session? (updated to clean up the intention of the issue.)
Hello,
there is a bug in create_refresh_token. It ignores identity parameter - function for @user_identity_loader is not called, like in access token. Thus, you have to use here user nickname/id / whatever is JSON serializable. Or it is intentional?
Hey, would be nice to pass a dictionary value when I'm creating token, so I can use in add_claims_to_access_token
and I don't have to find again to database using identity.
LOGIN: Here I have to find on database.
@custom_api.route('/auth', methods=['POST'])
def login():
options = {
'body': None,
'status_code': 200
}
username = request.json.get('username', None)
password = request.json.get('password', None)
controller = Controller().query.filter_by(uuid=username).first()
if controller and not safe_str_cmp(controller.jwt.encode('utf-8'), password.encode('utf-8')):
raise NotAuthorizedException()
options['body'] = {'access_token': create_access_token(controller)}
return __build_response_msg(options=options)
If not I have to make find twice:
@jwt.user_claims_loader
def add_claims_to_access_token(identity):
controller = Controller().query.filter_by(uuid=identity).first()
return {
'id': controller.id,
'role': controller.role
}
Would be nice to do something like that so I can make a search less:
@jwt.user_claims_loader
def add_claims_to_access_token(identity):
# I can use every parameter from identity dictionary
return {
'id': identity.id,
'role': identity.role
}
or alternately, only enable CSRF protection on certain requests (POST, PUT, DELETE, etc). Related to this: http://stackoverflow.com/questions/42005319
There is a typo in file flask_jwt_extended/config.py
at line 48:
if location not in ('headers', 'cookies'):
raise RuntimeError('JWT_LOCATION_LOCATION can only contain '
'"headers" and/or "cookies"')
I beleive it should be JWT_TOKEN_LOCATION
and not a JWT_LOCATION_LOCATION
.
https://github.com/vimalloc/flask-jwt-extended/blob/master/flask_jwt_extended/config.py#L48
Why does not include CSRF Token in claims of JWT (cookie mode) instead of create another dedicated new cookie ?
On front end we can read the payload and forge the http request with CSRF header
Edit: forget my question, the cookie is httponly ...
Flask with blueprint pattern, using Flask Restful with a Token Resource class and method decorator for jwt_required.
'''In app.py'''
from flask_jwt_extended import JWTManager
from flask import Flask
jwtmanager = JWTManager()
def create_app():
app = Flask(__name__)
def register_extensions(app):
jwtmanager.init_app(app)
'''in views.py'''
from flask_restful import Resource, Api
from flask_jwt_extended import jwt_required
api = Api()
class TokenResource(Resource):
method_decorators = [jwt_required]
class HelloWorld(TokenResource):
def get(self):
return 'Hello, World!'
api.add_resource(HelloWorld, '/hello')
The issue I am having is with exceptions and it likely has little to do with this library. When I run into a common jwt_extended exception on the flask development server with debug enabled I get this which is awesome!
{
"msg": "Token has expired"
}
With Gunicorn I get this on the exact same request with the same expired token.
gunicorn --reload --worker-class "gevent" "app:create_app()"
{"message": "Internal Server Error"}
with a traceback that the development server never showed.
File "/usr/local/lib/python3.5/site-packages/flask/app.py", line 1639, in full_dispatch_request
rv = self.dispatch_request()
File "/usr/local/lib/python3.5/site-packages/flask/app.py", line 1625, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/usr/local/lib/python3.5/site-packages/flask_restful/__init__.py", line 477, in wrapper
resp = resource(*args, **kwargs)
File "/usr/local/lib/python3.5/site-packages/flask/views.py", line 84, in view
return self.dispatch_request(*args, **kwargs)
File "/usr/local/lib/python3.5/site-packages/flask_restful/__init__.py", line 587, in dispatch_request
resp = meth(*args, **kwargs)
File "/usr/local/lib/python3.5/site-packages/flask_jwt_extended/utils.py", line 222, in wrapper
jwt_data = _decode_jwt_from_request(type='access')
File "/usr/local/lib/python3.5/site-packages/flask_jwt_extended/utils.py", line 204, in _decode_jwt_from_request
return _decode_jwt_from_headers()
File "/usr/local/lib/python3.5/site-packages/flask_jwt_extended/utils.py", line 176, in _decode_jwt_from_headers
return _decode_jwt(token, secret, algorithm)
File "/usr/local/lib/python3.5/site-packages/flask_jwt_extended/utils.py", line 136, in _decode_jwt
data = jwt.decode(token, secret, algorithm=algorithm)
File "/usr/local/lib/python3.5/site-packages/jwt/api_jwt.py", line 75, in decode
self._validate_claims(payload, merged_options, **kwargs)
File "/usr/local/lib/python3.5/site-packages/jwt/api_jwt.py", line 104, in _validate_claims
self._validate_exp(payload, now, leeway)
File "/usr/local/lib/python3.5/site-packages/jwt/api_jwt.py", line 149, in _validate_exp
raise ExpiredSignatureError('Signature has expired')
jwt.exceptions.ExpiredSignatureError: Signature has expired
It is raising the exception and I imagine it is calling the Signature expired callback which sends the json back out but clearly something goes wrong and it bubbles up and Flask hands out a 500 in place of what would have been a signature expired json message as the flask dev server does.
This probably has nothing to do with this specific library at all and any hints at all your give me about which direction to go in on this would be much appreciated by me. Using this with the flask development server has been perfect but now getting something ready for production I am encountering this persistent issue. My regular views return json just fine with the production setup but these exceptions don't. I was hoping maybe somebody had been running this in production and would be able to give me a hint on where I went wrong here. Thanks!
Hello!
The library uses the claim identity
for identifying the subject. This makes the library a bit awkard to interop with external identity providers or client implementations.
The JWT RFC recommends using sub
for identifying the principal:
4.1.2. "sub" (Subject) Claim
The "sub" (subject) claim identifies the principal that is the
subject of the JWT. The claims in a JWT are normally statements
about the subject. The subject value MUST either be scoped to be
locally unique in the context of the issuer or be globally unique.
The processing of this claim is generally application specific. The
"sub" value is a case-sensitive string containing a StringOrURI
value. Use of this claim is OPTIONAL.
I was thinking about making the "identity" claim to be changeable with a config key. This would allow for several pro:
As for the cons, while decoding should be easy to change, encoding is another matter.
Thoughts?
I am implementing an API in flask using your lovely library for a jwt-based authentication. I would like to offer the authenticated client the option to revoke its own token ("logout"). How would I implement this? I am using redis for the blacklist backend. As far as I can tell, the only way to revoke a token is by its jti
. However, the library only stores the identity and the user claims on the app context.
Would it make sense to store the full jwt on the app context and have the get_jwt_identity
and get_jwt_claims
functions just return the appropriate fields? This would allow for another function that returns the jti
.
Hi,
I'm getting "Missing Authorization Header" when trying to access a jwt_refresh_token_required endpoint.
I'm using 'Authorization': 'Bearer ' with the refresh_token but it tells me "Missing Authorization Header"
public getNewJwt() { // Get a new JWT from the server let refresh_token = localStorage.getItem('access_token'); let headers = new Headers({ 'Authorization': 'Bearer ' + refresh_token }); this.result = this.http.post(this.refreshTokenApiUrl, refresh_token, headers) .map((response: Response) => response.json) .subscribe(result => this.result =result); console.log(this.result); return this.result; }
What am I doing wrong :) ?
Add the possibility of some default logging.
Will need to do some additional thinking on the subject. Any feedback or requests welcome. I'm thinking anything complex should be done by overridding the various callback methods with the loader functions and put the logging in there. I'm not sure yet what I want to do for basic/default logging.
When using application factory where should I be putting my jwt.callbacks/ How should I be organizing them and also their exception handlers? Thanks!
Because application.py is where the
from extensions import jwt_manager
create_app():
app = Flask(__name__)
jwt_manager.init_app(app)
return app
extenstions.py
jwt_manager = JWTManager()
Where should I be putting the callback handlers for jwt to find them out?
Ex.
@jwt.expired_token_loader
def my_expired_token_callback():
return jsonify({
'status': 401,
'sub_status': 101,
'msg': 'The token has expired'
}), 200
This is the situation:
I'm wondering how to go about supporting this. It's possible I have a stupid idea here though.
I'm get the following error when using the provided token back to the refresh link.
The refresh_token wasn't used in the process?
Header:
Authorization: Bearer
{
"msg": "Missing or invalid claim: jti"
}
http://www.redotheweb.com/2015/11/09/api-security.html
Personally, I found it more helpful than the stackoverflow link, and less opinionated/pushy-salesman-like than the stormpath article. If you want to add it, otherwise, feel free to just close this issue :)
I'm trying to understand how CSRF works when you follow the example in csrf_protection_with_cookies. I read the documentation you linked to an earlier CSRF question:
http://www.redotheweb.com/2015/11/09/api-security.html
That document says that CSRF protection happens because you're adding an additional token to the request body. Specifically, it says a server "should expect both a session cookie header, and a token in the response body.", and the example has the token in the JSON payload of a post.
In your csrf_protection_with_cookies example, I understand that the access_token is HTTP only, and the crsf_access_token is not, but if they're both sent with any request by the browser to the server as cookies, I'm not sure how CSRF protection is achieved?
I note that you also have examples of not sending the CSRF token as a cookie, but that certainly takes a bit more effort for the app developer. If the CSRF token as a cookie is effective, that would make my life easier, but I'm not sure how that could be the case.
To be more specific on why I'm confused, I've set up flask_jwt_extended following the example in crsf_protection_with_cookies. To test it, I'm trying an integration test that calls the server using the python requests library roughly as follows:
s = requests.session()
resp = s.post('/login', <login json>)
resp = s.get('/users')
. I would expect this get to fail unless I'd set the CSRF token in the 'X-CSRF-TOKEN' header as described in the comment in csrf_protection_with_cookies, but the request succeeds without any custom header set by me in the test code.If a plain requests.get call works with @jwt_extended in this case, I must be missing how the CRSF protection works somehow. Shouldn't the request fail authentication because the X-CSRF-TOKEN header was not explicitly set in the get call?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.