In this section, we will explore how to implement authentication for RESTful APIs in Flask. Authentication is crucial for securing your API endpoints and ensuring that only authorized users can access certain resources. We will cover the following topics:

  1. Understanding API Authentication
  2. Token-Based Authentication
  3. Implementing Token-Based Authentication in Flask
  4. Securing API Endpoints
  5. Practical Example
  6. Exercises

  1. Understanding API Authentication

API authentication is the process of verifying the identity of a user or system trying to access your API. Common methods include:

  • API Keys: Simple tokens that are passed along with the request.
  • OAuth: A more complex and secure method that involves token exchange.
  • JWT (JSON Web Tokens): Tokens that are signed and can be verified without storing session information on the server.

  1. Token-Based Authentication

Token-based authentication involves issuing a token to a user upon successful login. This token is then included in the header of subsequent requests to authenticate the user. JWT is a popular choice for token-based authentication due to its stateless nature and ease of use.

Advantages of Token-Based Authentication:

  • Stateless: No need to store session information on the server.
  • Scalable: Easy to scale across multiple servers.
  • Secure: Tokens can be signed and encrypted.

  1. Implementing Token-Based Authentication in Flask

We will use the Flask-JWT-Extended extension to implement JWT authentication in our Flask application.

Step-by-Step Implementation:

  1. Install Flask-JWT-Extended:

    pip install Flask-JWT-Extended
    
  2. Configure Flask-JWT-Extended:

    from flask import Flask, jsonify, request
    from flask_jwt_extended import JWTManager, create_access_token, jwt_required, get_jwt_identity
    
    app = Flask(__name__)
    app.config['JWT_SECRET_KEY'] = 'your_jwt_secret_key'  # Change this to a random secret key
    jwt = JWTManager(app)
    
  3. Create User Login Endpoint:

    users = {
        "user1": "password1",
        "user2": "password2"
    }
    
    @app.route('/login', methods=['POST'])
    def login():
        username = request.json.get('username', None)
        password = request.json.get('password', None)
        if username not in users or users[username] != password:
            return jsonify({"msg": "Bad username or password"}), 401
    
        access_token = create_access_token(identity=username)
        return jsonify(access_token=access_token)
    
  4. Protect API Endpoints:

    @app.route('/protected', methods=['GET'])
    @jwt_required()
    def protected():
        current_user = get_jwt_identity()
        return jsonify(logged_in_as=current_user), 200
    

  1. Securing API Endpoints

To secure your API endpoints, you can use the @jwt_required() decorator provided by Flask-JWT-Extended. This ensures that only requests with a valid JWT token can access the protected endpoints.

Example:

@app.route('/data', methods=['GET'])
@jwt_required()
def get_data():
    current_user = get_jwt_identity()
    return jsonify(data=f"Sensitive data for {current_user}")

  1. Practical Example

Let's put everything together in a complete example:

from flask import Flask, jsonify, request
from flask_jwt_extended import JWTManager, create_access_token, jwt_required, get_jwt_identity

app = Flask(__name__)
app.config['JWT_SECRET_KEY'] = 'your_jwt_secret_key'
jwt = JWTManager(app)

users = {
    "user1": "password1",
    "user2": "password2"
}

@app.route('/login', methods=['POST'])
def login():
    username = request.json.get('username', None)
    password = request.json.get('password', None)
    if username not in users or users[username] != password:
        return jsonify({"msg": "Bad username or password"}), 401

    access_token = create_access_token(identity=username)
    return jsonify(access_token=access_token)

@app.route('/protected', methods=['GET'])
@jwt_required()
def protected():
    current_user = get_jwt_identity()
    return jsonify(logged_in_as=current_user), 200

if __name__ == '__main__':
    app.run(debug=True)

  1. Exercises

Exercise 1: Implement a Registration Endpoint

Create an endpoint /register that allows new users to register by providing a username and password. Store the user credentials in a dictionary.

Solution:

@app.route('/register', methods=['POST'])
def register():
    username = request.json.get('username', None)
    password = request.json.get('password', None)
    if username in users:
        return jsonify({"msg": "Username already exists"}), 400

    users[username] = password
    return jsonify({"msg": "User registered successfully"}), 201

Exercise 2: Add Role-Based Access Control

Modify the /protected endpoint to allow access only to users with a specific role. Add a role attribute to the user dictionary.

Solution:

users = {
    "admin": {"password": "adminpass", "role": "admin"},
    "user1": {"password": "password1", "role": "user"}
}

@app.route('/login', methods=['POST'])
def login():
    username = request.json.get('username', None)
    password = request.json.get('password', None)
    if username not in users or users[username]['password'] != password:
        return jsonify({"msg": "Bad username or password"}), 401

    access_token = create_access_token(identity={"username": username, "role": users[username]['role']})
    return jsonify(access_token=access_token)

@app.route('/admin', methods=['GET'])
@jwt_required()
def admin():
    current_user = get_jwt_identity()
    if current_user['role'] != 'admin':
        return jsonify({"msg": "Admins only!"}), 403
    return jsonify(logged_in_as=current_user), 200

Conclusion

In this section, we covered the basics of API authentication using JWT in Flask. We learned how to implement token-based authentication, secure API endpoints, and handle user roles. These concepts are crucial for building secure and scalable APIs. In the next module, we will explore how to deploy Flask applications to production environments.

© Copyright 2024. All rights reserved