Andrew Spear
Andrew Spear

Reputation: 101

Flask JWT Extended cookie name overide Flask Session Cookie Name

I am using the Flask JWT Extended extension for flask and have built a login application successfully using JWT. I have gone through the tutorial on JWT in Cookies on the JWT extended documentation site using CSRF protection and everything.

What I can't seem to figure out is when using the set_access_cookies() and set_refresh_cookies() methods the JWTs are not saved in a httponly cookie named using the JWT Extended default configuration setting.

app.config.setdefault('JWT_ACCESS_COOKIE_NAME', 'access_token_cookie')
app.config.setdefault('JWT_REFRESH_COOKIE_NAME', 'refresh_token_cookie')

Instead when I debug the return back from the auth call the cookies are saved in the base Flask default configuration instead.

'SESSION_COOKIE_NAME': 'session',

Shouldn't the set_access_cookies() and set_refresh_cookies() methods override the base Flask default configurations as long as make sure to register my app in the JWTManager()?

uscc_login_app = Flask(__name__)
jwt = JWTManager(uscc_login_app)

Or is there something else I missed in the base Flask JWT Extended documentation to ensure that its configuration defaults are used when appropriate?

Updated code via request.

The code is pretty spread out but here is my best shot to include what I think will help.

In init.py:

from flask import Flask, url_for
from flask_restful import Api
from flask_jwt_extended import JWTManager
from resources.auth import Authenticate
from resources.refresh import Refresh
from temp_app.temp import TempApp
from uscc_login.uscc_app_login import *

uscc_login_app = Flask(__name__)
uscc_login_app.config.from_object(os.environ.get('FLASK_ENV'))
jwt = JWTManager(uscc_login_app)
api = Api(uscc_login_app, prefix='/v1')


# Add resources via the add_resource method

api.add_resource(Authenticate, '/login')
api.add_resource(Refresh, '/refresh_token')

login_view = Login.as_view(name='uscc_login')

uscc_login_app.add_url_rule('/login', view_func=login_view, methods=['POST', 'GET'])

In my app.py:

from uscc_login import uscc_login_app


if __name__ == '__main__':

    uscc_login_app.run(debug=uscc_login_app.config.get('DEBUG'), threaded=uscc_login_app.config.get('THREADED'),
                       port=uscc_login_app.config.get('PORT'), host=uscc_login_app.config.get('HOST'))

In my config.py since I am using the Flask config.from_objects

import os
import datetime

uscc_login_app_dir = os.path.abspath(os.path.dirname(__file__))


class BaseConfig:
    SECRET_KEY = os.environ.get('USCC_SECRET_KEY') or 'you-will-never-guess'
    JWT_SECRET_KEY = os.environ.get('USCC_JWT_KEY') or 'super-secret'
    JWT_TOKEN_LOCATION = ['cookies']
    JWT_COOKIE_CSRF_PROTECT = True
    JWT_HEADER_TYPE = 'JWT'
    PROPAGATE_EXCEPTIONS = True
    THREADED = True


class DevelopmentConfig(BaseConfig):
    DEBUG = True
    PORT = 5000 if os.environ.get("PORT") is None else int(os.environ.get("PORT"))
    HOST = os.environ.get('HOST') or 'localhost'
    if os.environ.get('access_token_expiration') is not None:
        JWT_ACCESS_TOKEN_EXPIRES = datetime.timedelta(seconds=int(os.environ.get('access_token_expiration')))
    if os.environ.get('refresh_token_expiration') is not None:
        JWT_REFRESH_TOKEN_EXPIRES = datetime.timedelta(seconds=int(os.environ.get('refresh_token_expiration')))

So then in my Flask MethodView that contains my login authorization POST I have the following:

auth.py

import sys
import os
from flask import jsonify, request
from flask_restful import Resource
from flask_jwt_extended import create_access_token, create_refresh_token, jwt_refresh_token_required, get_jwt_identity, \
    set_access_cookies, set_refresh_cookies
from utilities import Common


class Authenticate(Resource):
    @staticmethod
    def post():
        """
        :return:
        """

        api_cred_path = os.environ.get('api_cred_path')
        if api_cred_path is None:
            response = jsonify({"msg": "Environment Variable 'api_cred_path' is not set."})
            response.status_code = 500
            return response

        if not request.is_json:
            response = jsonify({'msg': 'Missing JSON in request'})
            response.status_code = 400
            return response

        params = request.get_json()
        user_name = params.get('username')
        user_password = params.get('password')

        if not user_name:
            response = jsonify({'msg': 'Missing username parameter'})
            response.status_code = 400
            return response
        if not user_password:
            response = jsonify({'msg': 'Missing password parameter'})
            response.status_code = 400
            return response

        if Common.check_path_exists(api_cred_path):
            with open(api_cred_path) as afh:
                for line in afh:
                    file_userid, file_password = line.split('=')
                    if file_userid == user_name and file_password.strip('\n') == user_password:
                        access_token = create_access_token(identity=user_name)
                        refresh_token = create_refresh_token(identity=user_name)
                        response = jsonify({'login': True})
                        set_access_cookies(response, access_token)
                        set_refresh_cookies(response, refresh_token)
                        # # Identity can be any data that is json serializable
                        # art = {
                        #     'access_token': create_access_token(identity=user_name),
                        #     'refresh_token': create_refresh_token(identity=user_name)}
                        # response = jsonify(art)
                        response.status_code = 200
                        return response
        else:
            response = jsonify({"msg": "api_cred_path invalid."})
            response.status_code = 500
            return response

        response = jsonify({'msg': 'Bad username or password'})
        response.status_code = 401
        return response

Upvotes: 2

Views: 2562

Answers (2)

Andrew Spear
Andrew Spear

Reputation: 101

So I realized my mistake in this. I was trying to get the access_token_cookie variable to be set from my auth.py which serves as my RESTFUL based microservice of which my login app calls to do the authorization. Realized it won't be available after redirecting back to the caller from the login apps POST method since the cookie was related to the login app UI frontend. So I just base the access and refresh tokens back from the auth.py POST method to the login POST method and let it set the cookies so they are available to the end client.

This was more of design problem than a code problem.

Upvotes: 0

vimalloc
vimalloc

Reputation: 4167

Could you provide some code to duplicate what you are seeing? When I try running the example token in jwt code (https://github.com/vimalloc/flask-jwt-extended/blob/master/examples/jwt_in_cookie.py) I see the expected cookie values when I login:

$ http :5000/token/auth username=test password=test
...
Set-Cookie: access_token_cookie=<jwt>; HttpOnly; Path=/api/
Set-Cookie: refresh_token_cookie=<jwt>; HttpOnly; Path=/token/refresh
...

Upvotes: 1

Related Questions