Giter Site home page Giter Site logo

vimalloc / flask-jwt-extended Goto Github PK

View Code? Open in Web Editor NEW
1.5K 1.5K 241.0 929 KB

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

Python 100.00%
flask flask-jwt flask-jwt-extended jwt

flask-jwt-extended's People

Contributors

abyssknight avatar carlegbert avatar decaz avatar deftez avatar dependabot[bot] avatar djailla avatar dunkmann00 avatar faisalabujabal avatar fgblomqvist avatar frnsys avatar illia-v avatar ksmanis avatar luord avatar matthewstory avatar psafont avatar reiven avatar rlam3 avatar robscllc avatar rohitsanj-jovian avatar russellcardullo avatar sammck avatar smarlowucf avatar stefanvdweide avatar steinitzu avatar stephane avatar stephendwolff avatar ticosax avatar vimalloc avatar vimalloc-mavenlink avatar wjjmjh avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

flask-jwt-extended's Issues

Use blacklist with custom store

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 ?

flask_jwt identity_callback feature

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!

(Question) Support for Sanic

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.

Better CSRF errors

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.

Missing options in documentation

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

inquiry regarding the `refreshing` of tokens

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!

Docs - porting from flask-jwt

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:

    • JWT_AUTH_HEADER_PREFIX --> JWT_HEADER_TYPE
    • JWT_EXPIRATION_DELTA --> JWT_ACCESS_TOKEN_EXPIRES
  • No leeway

Is that a good start?

Refresh token should provide a new refresh token as well?

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....

KeyError: 'csrf'

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?

Version Logs?

@vimalloc Should we have a log of changes to clarify if newer releases might break previous versions?

Flask jwt Identity Method

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

Blacklist get_stored_tokens function is very inefficient

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.

Option for double submit verification on protected endpoints

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.

Why configure the JWT location statically?

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?

Is there a way to revoke both refresh token and access token when logout?

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?

Python2 and Python3 support

Currently only supports python3. Use six and tox to get compatibility and unittests for python2 and python3

Add more information to the 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

Add some documentation about porting from flask-jwt

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.

jwt decorator

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?

Add informations about refresh token or add refresh_csrf_header ?

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.

Accessing get_jwt_identity() in another decorator.

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?

Expires header not being set when setting cookies

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.)

create_refresh_token ignores identity

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?

Give a dictionary to create_access_token

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
    }

(Question) CSRF Token

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 ...

Exceptions in production not producing json

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!

Use sub claim instead of identity for interoperatibility

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:

  • Interoperability: allows the user to select a claim for "identity", allowing users of the library to interop with opinionated code.
  • Smooth deprecation policy: default value can be left to be the same as now, while allowing users to change it; the default value can be changed whenever backwards-compatibility isn't a concern.

As for the cons, while decoding should be easy to change, encoding is another matter.

Thoughts?

Revoke current token

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.

Missing Authorization Header

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 :) ?

Logging

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?

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

Support two simultaneous token configs

This is the situation:

  • Users are authenticated via httpOnly JWT cookies with double submit csrf cookies (refresh tokens as well).
  • API clients use Authorization bearer tokens with IP whitelisting and no CSRF tokens (limited scope of operations, public API etc). The bearer token is sent to the API as JSON payload after they request an access token using their secret JWT refresh token.

I'm wondering how to go about supporting this. It's possible I have a stupid idea here though.

Decoding jwt is missing "jti"

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"
}

Follow-up CSRF question

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:

  • Setting up a requests session object: s = requests.session()
  • Calling the /login endpoint with a post to the session: resp = s.post('/login', <login json>)
  • Calling a get method that is marked as protected with @jwt_required using the same session with get: 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?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.