flask-jwt-extended current_user identity = Нет при создании не свежего токена доступа из токена обновления - PullRequest
0 голосов
/ 09 января 2019

В моем приложении фляги (python 2.7) я пытаюсь вызвать обновление маркера доступа через токен обновления, когда он истекает с помощью декоратора @ jwt.expired_token_loader. И токен доступа, и токен обновления хранятся в куки (JWT_TOKEN_LOCATION = 'куки'). Я использую тот же код, приведенный в документации (https://flask -jwt-extended.readthedocs.io / en / latest / tokens_in_cookies.html ), чтобы сделать это, и я могу успешно сгенерировать новый доступ маркер. Однако при создании нового токена доступа заявка на идентификацию токена доступа равна None (get_raw_jwt ()). Независимо от того, что я делаю с токеном обновления или токеном доступа, всякий раз, когда я печатаю jwt_claims или пытаюсь получить текущего пользователя с помощью current_user = get_jwt_identity (), он возвращает идентичность как None. Мне важно знать, какой пользователь отправляет запросы в нейронную сеть, чтобы я мог правильно отслеживать, какие запросы были отправлены каким пользователям (отношение один ко многим).

Я попытался устранить неполадки (декодировать) refresh_token, и я столкнулся с отдельной проблемой: когда я пытаюсь декодировать refresh_token с помощью decode_token (), я получаю длинную трассировку, которая заканчивается InvalidSignatureError: Ошибка проверки подписи. Я взял refresh_token и пропустил его через https://jwt.io, и он расшифровал токен. Я могу видеть в декодированном токене, что утверждение «идентичность» предоставляет мне личность пользователя, но говорит, что токен не проверен. Тем не менее, как только я проверяю на экране секретное закодированное поле base64, подпись становится проверенной, и часть подписи jwt изменяется вместе с ней. Я пытаюсь декодировать этот измененный jwt, который https://jwt.io предоставил мне функцию decode_token, и она все еще выдает мне ту же ошибку: InvalidSignatureError: Ошибка проверки подписи.

Я потратил часы на чтение всего, что предоставляет мне google на flask-jet-extended и PyJWT, и я не могу понять, как это исправить. Я попытался изменить мою конфигурацию JWT_SECRET_KEY для разных строк и даже кодировать ее с base64, и ни одна из этих проблем не решает проблемы. Я включил и выключил конфигурацию JWT_COOKIE_CSRF_PROTECT. Я включил и выключил JWT_ACCESS_COOKIE_PATH и JWT_REFRESH_COOKIE_PATH. Я попытался декодировать оба файла cookie refresh_token_cookie и CSRF_refresh_token. Я попытался запустить decode_token () с файлом cookie refresh_token_cookie и CSRF_refresh_token, указав аргумент csrf_value = request.cookies.get ('csrftoken'). Я попытался с помощью функции decode () из jwt напрямую ( из JWT импорта декодирования).

Я просто не знаю, что еще делать, и не могу найти никаких дополнительных онлайн-ресурсов. Любая помощь очень ценится!

Мой следующий шаг - перевести мою систему аутентификации на flask-jet-simple или PyJWT. Я действительно хочу использовать JWT для аутентификации моих пользователей. Я не знаю, как совместить JWT с флеш-логином или вообще возможно ли это. Мне не удается найти какие-либо ресурсы в Интернете, где кто-то использовал флеш-логин с JWT Я нашел довольно недавнее хранилище с именем flask-jwt-login, которое я мог бы попробовать использовать, если не могу понять это. В конечном итоге я бы хотел остаться с flask-jwt-extended. У меня есть другие части этого веб-приложения, на которых я хочу сосредоточиться и хочу, чтобы его часть была в квадрате.

В любом случае, вот мой код, рабочий процесс начинается на странице / login. Это перенаправит вас на страницу / NN. По истечении срока действия токена доступа, если вы попытаетесь перезагрузить страницу / NN, она перенаправит себя на страницу / token / refresh. После обновления токена он вернется на страницу / NN.

Пожалуйста, дайте мне знать, если мне нужно загрузить дополнительные файлы.

P.S. Это мой первый пост о переполнении стека, так что простите меня за любые проблемы с форматированием.

application.py

from flask import url_for,render_template, redirect,request, jsonify,flash,\
                    make_response, session
from flask_jwt_extended import (create_access_token, create_refresh_token,
                            jwt_required, get_jwt_identity, get_jwt_claims,get_current_user,
                            set_access_cookies,set_refresh_cookies,
                            unset_jwt_cookies, get_raw_jwt, jwt_refresh_token_required,decode_token)


from jwt import decode
from forms import RegisterForm, LoginForm, NNForm
from models import Users

from website import app,db,jwt



#ToDo When the token expires I get an HTTP status code of 401 I can use expired_token_loader refresh token.

@app.route('/token/refresh', methods=['GET','POST'])
@jwt_refresh_token_required
@jwt.expired_token_loader
def refresh():
    #Create the new access token
    ref_token = request.cookies.get('refresh_token_cookie')
    csrftoken = request.cookies.get('csrftoken')
    decode_ref_token = decode_token(ref_token)
    current_user = get_jwt_identity()
    print('ref_token:', ref_token)
    print('current_user:', current_user, get_raw_jwt())
    access_token = create_access_token(identity=current_user)

    #Set the JWT access cookie in the response
    print('from refresh():', request.url)
    response = make_response(redirect(request.url))
    set_access_cookies(response,access_token)
    #set_refresh_cookies()
    return response




@app.route('/token/remove', methods=['POST'])
def logout():
    #ToDo Still need to build the logout page.
    response = make_response(redirect(url_for('logout_page')))
    unset_jwt_cookies(response)
    return response




@app.route('/register/', methods=['GET','POST'])
def register_page():
    form = RegisterForm(request.form)
    print( request.method, form.validate_on_submit())
    if request.method == "POST" and form.validate_on_submit():
        user = Users(form.first_name.data, form.last_name.data, \
                     form.email.data, form.password.data, form.organization.data)
        user.save_to_db()
        flash("Thanks for Registering. Please login")

        return redirect(url_for("NN_page"))

    return render_template('register.html',form=form)




@app.route('/login/', methods=['GET','POST'])
def login_page():
    form = LoginForm(request.form)
    print(request.method, request.form)
    if request.method == "POST":
        #This checks if the user is in the db and returns the user obj.
        user = form.validate_on_submit()
        if user:
            access_token = create_access_token(identity=user.email, fresh=True)
            refresh_token = create_refresh_token(identity=user.email)

            response = make_response(redirect(url_for('NN_page')))
            set_access_cookies(response, access_token)
            set_refresh_cookies(response, refresh_token)
            #response.headers['Authorization'] = 'Bearer {}'.format(access_token)
            print(response)
            return response
            #return jsonify({'access_token':access_token})
            #return redirect((url_for("NN_page")))

    return render_template('login_page.html', form=form)






@jwt.invalid_token_loader #This allows me to stop people who have not logged in yet.
def missing_JWT_token(msg):
    print('from missing_JWT_token:', msg)
    return redirect(url_for('login_page'))
    # return "The site being accessed requires a valid JWT to view." \
    #        "Error: {}".format(msg)




@app.route('/NN/', methods=['GET','POST'])
@jwt_required
def NN_page():
    jwt_claims = get_raw_jwt()
    print(jwt_claims)
    print('cookie keys:', request.cookies.get('refresh_token_cookie'))
    user = get_jwt_identity()
    print('User:',user)
    form = NNForm(request.form, headers=request.headers)
    print(request.form, form.validate_on_submit())
    if request.method == "POST" and form.validate_on_submit():

        return redirect((url_for("success_NN_submission")))

    return render_template('NN_page.html', form=form)

config.py

import os
from datetime import timedelta
from base64 import b64encode

secret_key = os.urandom(24)
jwt_secret_key = b64encode('I_love_my_smokes!')

class BaseConfig(object):

    SECRET_KEY = secret_key

    SQLALCHEMY_DATABASE_URI = 'sqlite:///Protein_NN.db'
    SQLALCHEMY_TRACK_MODIFICATION = False

    #JWT_SECRET_KEY = jwt_secret_key
    JWT_ACCESS_TOKEN_EXPIRES = timedelta(minutes=10)
    JWT_REFRESH_TOKEN_EXPIRES = timedelta(minutes=1)
    JWT_TOKEN_LOCATION = 'cookies'
    #JWT_ACCESS_COOKIE_PATH = '/NN/'
    #JWT_REFRESH_COOKIE_PATH ='/token/refresh'
    JWT_COOKIE_CSRF_PROTECT = False

    SESSION_COOKIE_SECURE = True




class DevelopmentConfig(BaseConfig):

    DEBUG = True

    JWT_ACCESS_TOKEN_EXPIRES = timedelta(seconds=5)

    SESSION_COOKIE_SECURE = False

    #PROPOGATE_EXCEPTION = True

    #EMAIL SETTINGS
    MAIL_SERVER = 'smtp.gmail.com'
    MAIL_PORT = 465
    #MAIL_PORT = 587  # This is for TLS
    MAIL_USE_TLS = False
    MAIL_USE_SSL = True
    #MAIL_USERNAME = os.environ['EMAIL_USER']
    #MAIL_PASSWORD = os.environ['EMAIL_PASSWORD']

    #BOOTSTRAP_SERVE_LOCAL = True

Это то, что get_raw_jwt () возвращает после обновления токена доступа токеном обновления.

    {'user_claims': {}, u'jti': u'9fb01b6c-619b-4fe6-91d3-73f8609f2f61',
 u'exp': 1547022397, u'iat': 1547022392, u'fresh': False, 
u'type': u'access', u'nbf': 1547022392, u'identity': None}

Как видите, заявка на идентичность равна None.

Вот трассировка, которую я вижу:

Traceback (most recent call last):
  File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/flask/app.py", line 2309, in __call__
    return self.wsgi_app(environ, start_response)
  File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/flask/app.py", line 2295, in wsgi_app
    response = self.handle_exception(e)
  File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/flask/app.py", line 1741, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/flask/app.py", line 2292, in wsgi_app
    response = self.full_dispatch_request()
  File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/flask/app.py", line 1815, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/flask/app.py", line 1719, in handle_user_exception
    return handler(e)
  File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/flask_jwt_extended/jwt_manager.py", line 93, in handle_expired_error
    return self._expired_token_callback()
  File "/Users/Danny/Documents/Codes/Ellington/NN App/website/application.py", line 43, in refresh
    print('current_user:', current_user, decode('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIzNzVlNWExMy1mNjRiLTQxNmItOTY0ZC0wMDg5ODI4NGY2NGQiLCJleHAiOjE1NDcwMTk5ODUsImlhdCI6MTU0NzAxOTkyNSwidHlwZSI6InJlZnJlc2giLCJuYmYiOjE1NDcwMTk5MjUsImlkZW50aXR5IjoiZGFubnlAbWUuY29tIn0.LVEj6As2Uh_xgTbjm94b0M6mJeD0YLkf9KpgNKTZJOw'))
  File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/jwt/api_jwt.py", line 92, in decode
    jwt, key=key, algorithms=algorithms, options=options, **kwargs
  File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/jwt/api_jws.py", line 156, in decode
    key, algorithms)
  File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/jwt/api_jws.py", line 223, in _verify_signature
    raise InvalidSignatureError('Signature verification failed')
InvalidSignatureError: Signature verification failed

1 Ответ

0 голосов
/ 09 января 2019

Наличие этих двух отдельных декораторов в функции обновления не будет работать так, как вы этого хотите. У декоратора загрузчика с истекшим сроком действия не будет установлен текущий пользователь, потому что jwt недопустим при вызове этой функции обратного вызова.

Вместо этого попробуйте разбить код обновления на вспомогательную функцию, используемую обоими декораторами независимо:

def refresh_token(username):
    # return flask response from here

@jwt.expired_token_loader
def handle_expired_token():
    # get username here from raw jwt
    username = 'todo'
    return refresh_token(username)

@app.route(‘/refresh)
@jwt_refresh_token_required
def refresh_endpoint():
    username = get_current_identity()
    return refresh_token(username)

Вы также можете использовать собственный декоратор вместо декоратора jwt_required и достичь аналогичной цели. Некоторые примеры этого обсуждаются здесь: https://gitter.im/flask-jwt-extended/Lobby?at=5c1a9b37c35a3002474ddf3d

...