Lesson 2
Generating JWT Tokens Upon Login
Generating JWT Tokens Upon Login

Welcome to the next step in securing your Flask application! In the last lesson, you learned how to create a basic login endpoint that validates user credentials. Today, we'll take it a step further by introducing JSON Web Tokens (JWT) to enhance the security of your application.

Recap of API Setup

Let's briefly recap our Flask app setup and the initial code we wrote.

Python
1from flask import Flask, request, jsonify 2from marshmallow import Schema, fields, ValidationError 3from marshmallow.validate import Length 4 5# Initialize a Flask app instance 6app = Flask(__name__) 7 8# Mock database of users 9database = [ 10 {"id": 1, "username": "cosmo", "password": "space-corgi"} 11] 12 13# Define a schema for validating login data 14class LoginSchema(Schema): 15 username = fields.Str(required=True, validate=Length(min=1)) 16 password = fields.Str(required=True, validate=Length(min=1)) 17 18# Create an instance of LoginSchema 19login_schema = LoginSchema()

This code initializes a Flask app, sets up a mock database, and uses Marshmallow to validate login data. If you need a refresher on any of these concepts, feel free to revisit the previous lesson.

Understanding Bearer Tokens

Before we dive into JWT, let's understand Bearer Tokens. A bearer token is like a "movie ticket" that you present to gain entry. Whoever "bears" this token can access certain resources.

Instead of sending your username and password with every request—which is risky—you log in once and receive a bearer token. This token is then included in the headers of future requests to prove your identity.

JWT is a type of bearer token. By using tokens, you improve security and efficiency since your credentials are not repeatedly transmitted, and servers can quickly verify token validity.

What is JWT?

A JSON Web Token (JWT) is a compact, URL-safe means of representing claims between two parties. It's primarily used for securely transmitting information between a client and a server. A JWT token consists of three parts: a Header, a Payload, and a Signature.

  • The Header contains metadata about the token, such as the type and hashing algorithm used.
  • The Payload carries the actual data or claims, such as user information and token expiration time.
  • The Signature ensures the token's integrity by verifying that the content hasn't been tampered with, using a secret key.

By encoding this information, JWT protects user data and offers a reliable way to verify user identities.

Installing and Configuring Flask-JWT-Extended

Now, let's introduce the Flask-JWT-Extended library, which we'll use to generate and handle JWT tokens. In case you want to install Flask-JWT-Extended in your machine, you can use the following command:

Bash
1pip install flask-jwt-extended

In the CodeSignal environment, this library is pre-installed, but it's good practice to know how to do this for your local development.

JWTManager Configuration

To manage JWTs in your Flask application, you'll be using the JWTManager from the Flask-JWT-Extended library. This will handle the creation, validation, and revocation of JWTs.

Python
1from flask_jwt_extended import JWTManager 2 3# Set the secret key for signing JWTs 4app.config['JWT_SECRET_KEY'] = 'super-secret' 5 6# Initialize the JWTManager with the Flask app 7jwt = JWTManager(app)

Here’s a step-by-step explanation:

  1. Set the Secret Key: app.config['JWT_SECRET_KEY'] = 'super-secret'

    • The JWT_SECRET_KEY is a critical part of your security. It's used to sign the JWTs so they can be verified by your Flask app. Make sure to use a strong, unpredictable secret key in a production environment.
  2. Initialize JWTManager: jwt = JWTManager(app)

    • By initializing JWTManager with your Flask app, you integrate JWT handling capabilities, like creating and validating tokens.

By configuring JWTManager, you prepare your Flask app to securely handle JWTs. Now, let's enhance our /login endpoint to generate and return a JWT upon successful authentication.

Recap of Login Endpoint

Before we start generating JWT tokens, let's revisit the login endpoint we made in the last lesson. Here, we validate user credentials and return a success message if they are correct.

Python
1# Define a route to receive login credentials 2@app.route('/login', methods=['POST']) 3def login(): 4 try: 5 # Validate and deserialize the input data according to the schema 6 data = login_schema.load(request.get_json()) 7 except ValidationError as err: 8 # If validation fails, return an error message and a 400 status code 9 return jsonify(error=err.messages), 400 10 11 # Extract username and password from the validated data 12 username = data['username'] 13 password = data['password'] 14 15 # Find the user in the mock database 16 user = next((user for user in database if user["username"] == username), None) 17 18 # Check if the user exists and if the password matches 19 if user and user["password"] == password: 20 # Login successful 21 return jsonify(message="Login successful"), 200 22 else: 23 # Return an error if the user does not exist or the password is incorrect 24 return jsonify(error="Bad username or password"), 401
Returning a Generated JWT on Login

Now, let's enhance our /login endpoint to generate and return a JWT token upon successful authentication.

Here’s our adapted endpoint:

Python
1from flask_jwt_extended import create_access_token 2 3# Define a route to receive login credentials 4@app.route('/login', methods=['POST']) 5def login(): 6 # -- previous input validation and user verification code goes here -- 7 8 # Check if the user exists and if the password matches 9 if user and user["password"] == password: 10 # Create an access token for the user 11 access_token = create_access_token(identity=username) 12 # Return the access token with a success message 13 return jsonify(access_token=access_token), 200 14 else: 15 # Return an error if the user does not exist or the password is incorrect 16 return jsonify(error="Bad username or password"), 401

Key changes:

  1. Import create_access_token: To generate a JWT token.
  2. Generate Access Token: Create an access token using create_access_token(identity=username), by setting the identity to the username, the token can later be used to identify the user making requests.
  3. Return Access Token: Return the generated JWT token in the JSON response.

With these changes, our login endpoint now provides a JWT token, enhancing the security and efficiency of our application.

Successful Login Request

When a client successfully logs in by providing valid credentials to the /login endpoint, the response will include the generated JWT token. Here's what the response might look like:

JSON
1{ 2 "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." 3}

The client should keep this token securely and send it in the Authorization header as Bearer <token> for future requests to access protected resources.

Summary and Next Steps

In this lesson, you learned how to enhance your Flask login endpoint by generating and returning JWT tokens. We covered:

  • What JWT is and why it's useful for authentication.
  • How to install and configure Flask-JWT-Extended.
  • Setting up JWTManager in your Flask app.
  • Modifying the login endpoint to generate and return a JWT on successful authentication.

Now that you're equipped with the knowledge of JWT-based authentication, it's time to put it into practice. Keep testing and experimenting with the code, and always double-check the security aspects of your implementation.

Happy coding!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.