Setting Authorization header - Flask app using Python Requests and JWT

Viewed 4337

I am just learning to code and this has been driving me crazy. I don't know if I am missing something very simple or have a total misunderstanding about how authentication works (very possibly both), however, i would appreciate someone help.

I have created a login route for my flask app which renders the following and successfully allows a user logging in.

Login Page

If a user successfully logs in, i want to set the Authorization header using a token generated using JWT. I am using the request libary to do this and get a 200 response but everytime i check the network tab, i can not see the token in the 'Authorization' Response Header.

The idea is that once this Header has been set, i can protect my apis by ensuring a token is present and also use that information to ensure that APIs only return data for that user i.e. decrypt the token to work out the user.

Network tab - No Authorization Header

This is my current code. Where am i going wrong? Does my approach sound correct?

@loginsBlueprint.route('/login', methods=['GET', 'POST'])
def login():
    error = None
    if request.method == 'POST':
        user = User.query.filter_by(username=request.form['username']).first()
        if user is not None and bcrypt.check_password_hash(user.password, request.form['password']):
            token = jwt.encode(
                {'user': request.form['username'], 'exp': datetime.datetime.utcnow() + datetime.timedelta(seconds=15)},
                app.config['SECRET_KEY'])

            url = 'http://127.0.0.1:5000/login'
            headers = {'Authorization': 'Bearer ' + token}
            requests.get(url, headers=headers)

            return render_template('landing_page.html')
        else:
            return make_response('Could not verify', 403,
                                 {'WWW-Authenticate': 'Basic realm ="Wrong Password !!"'})
    return render_template('login.html', error=error)

login.html

<html>
  <head>
    <title>Flask Intro - login page</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link href="static/bootstrap.min.css" rel="stylesheet" media="screen">
  </head>
  <body>
    <div class="container">
      <h1>Please login</h1>
      <br>
      <form action="" method="post">
        <input type="text" placeholder="Username" name="username" value="{{
          request.form.username }}">
         <input type="password" placeholder="Password" name="password" value="{{
          request.form.password }}">
        <input class="btn btn-default" type="submit" value="Login">
      </form>
      {% if error %}
        <p class="error"><strong>Error:</strong> {{ error }}
      {% endif %}
    </div>
  </body>
</html>

Note: I have tried changing to requests.post but get a 400:

127.0.0.1 - - [30/Mar/2021 21:53:29] "←[31m←[1mPOST /login HTTP/1.1←[0m" 400 

I thought it might be because i'm using CORS and so added this in:

CORS(app, expose_headers='Authorization')
1 Answers

A few things which are confusing here:

  1. In the login() method, you make a requests.get() call. So it looks like you're calling a new endpoint from your app. Is that how it should work? To be exact, you seem to check the user's credentials, issue a JWT and then call the /login endpoint of an app listening on port 5000, setting the JWT as the bearer token.

  2. Authorization is a request header. You don't use it in responses to the client. If you want to return the JWT to the client use one of the OAuth flows, either the Code flow (preferably) or the Implicit flow.

  3. Once you'll get the token to your client, then you should use it as you said - set in the Authorization header as a Bearer token. As you're using flask, you can use this filter to secure your APIs.

Related