Authentication and Authorisation Cognito in Flask
Authentication and Authorisation Cognito in Flask
org/project/pycognito/
https://chatgpt.com/c/71bc6b74-e2be-4198-a293-872e57bc9d28
bash
Copy code
pip install Flask
2. Install Required Libraries
You'll need additional libraries for decoding and verifying JWT tokens. Install
pycognito and requests for this:
bash
Copy code
pip install pycognito requests
3. Configure Flask to Use Cognito
Create a configuration file or set environment variables for your AWS Cognito
settings. You’ll need the following information:
User Pool ID
App Client ID
Region
python
Copy code
import os
COGNITO_USER_POOL_ID = os.getenv('COGNITO_USER_POOL_ID')
COGNITO_APP_CLIENT_ID = os.getenv('COGNITO_APP_CLIENT_ID')
COGNITO_REGION = os.getenv('COGNITO_REGION')
4. Set Up Token Validation
Create a utility function to validate the JWT tokens. You’ll need to fetch the
public keys from Cognito to verify the token's signature.
python
Copy code
import requests
from pycognito import Cognito
from flask import request, jsonify, Flask
app = Flask(__name__)
def get_public_keys():
jwks_url =
f"https://cognito-idp.{COGNITO_REGION}.amazonaws.com/{COGNITO_USER_POOL_ID}/.well-
known/jwks.json"
response = requests.get(jwks_url)
response.raise_for_status()
return response.json()
def verify_token(token):
keys = get_public_keys()
cognito_user = Cognito(COGNITO_USER_POOL_ID, COGNITO_APP_CLIENT_ID)
return cognito_user.verify_token(token, keys)
5. Create an Authentication Decorator
You can create a decorator to protect your routes and ensure they are accessed only
by authenticated and authorized users.
python
Copy code
from functools import wraps
from flask import request, jsonify
def authenticate(f):
@wraps(f)
def wrapper(*args, **kwargs):
token = request.headers.get('Authorization')
if not token:
return jsonify({"message": "Missing token"}), 401
try:
# Remove "Bearer " prefix if present
if token.startswith("Bearer "):
token = token[7:]
# Verify token
claims = verify_token(token)
if claims is None:
return jsonify({"message": "Invalid token"}), 401
return wrapper
6. Protect Your Routes
Use the authenticate decorator to protect your routes:
python
Copy code
@app.route('/update_fcm_token', methods=['POST'])
@authenticate
def update_fcm_token():
# Your logic here
return jsonify({"message": "Token updated successfully"}), 200
7. Run Your Flask Application
Run your Flask application as usual:
python
Copy code
if __name__ == '__main__':
app.run(debug=True)
8. Testing
Make sure to test your protected routes by sending requests with the Authorization
header set to a valid Cognito token.
Summary
Token Validation: Fetch public keys from Cognito and use them to verify the JWT
tokens.
Decorator: Create a decorator to check for the presence and validity of the token
before granting access to the route.
Integration: Use the decorator on routes you want to protect.
This approach ensures that only authenticated and authorized users can access your
Flask API endpoints.
You said:
what if i have to check groups from cognito and then provide access to authorised
person
like i did in aws chalice
cognito_groups = app.current_request.context["authorizer"]["claims"]
["cognito:groups"]
print(cognito_groups)
groups = ["Superadmin", "Admin"]
if cognito_groups not in groups:
err = {}
err["code"] = "401"
err['message'] = "This Authorization error"
return Response.respond(err)
except Exception as e:
err = {}
err["code"] = "401"
err['message'] = "Authorization error"
ChatGPT said:
ChatGPT
To check Cognito user groups and provide access based on group membership in a
Flask application, you can extend the authentication decorator to include group
validation. Here’s how you can achieve this:
python
Copy code
import jwt
from flask import request, jsonify, Flask
app = Flask(__name__)
def get_public_keys():
jwks_url =
f"https://cognito-idp.{COGNITO_REGION}.amazonaws.com/{COGNITO_USER_POOL_ID}/.well-
known/jwks.json"
response = requests.get(jwks_url)
response.raise_for_status()
return response.json()
def verify_token(token):
keys = get_public_keys()
try:
# Decode token using public keys
header = jwt.get_unverified_header(token)
key = next(key for key in keys['keys'] if key['kid'] == header['kid'])
public_key = jwt.algorithms.RSAAlgorithm.from_jwk(key)
decoded_token = jwt.decode(token, public_key, algorithms=['RS256'],
audience=COGNITO_APP_CLIENT_ID)
return decoded_token
except Exception as e:
print(f"Token verification failed: {e}")
return None
2. Create a Group Validation Decorator
Extend the authenticate decorator to check for specific groups:
python
Copy code
from functools import wraps
def authorize(groups):
def decorator(f):
@wraps(f)
def wrapper(*args, **kwargs):
token = request.headers.get('Authorization')
if not token:
return jsonify({"message": "Missing token"}), 401
try:
# Remove "Bearer " prefix if present
if token.startswith("Bearer "):
token = token[7:]
# Verify token
claims = verify_token(token)
if claims is None:
return jsonify({"message": "Invalid token"}), 401
return wrapper
return decorator
3. Protect Routes with Group Validation
Apply the authorize decorator to your routes, specifying the groups that are
allowed:
python
Copy code
@app.route('/add_m_user', methods=['POST'])
@authorize(groups=["Superadmin", "Admin"])
def create_item():
# Your logic here
return jsonify({"message": "User added successfully"}), 200
4. Testing
Test your protected routes by sending requests with the Authorization header set to
a valid Cognito token. Ensure that only users in the specified groups can access
the routes.
Summary
Token Validation: Decode and verify the JWT token using public keys.
Group Validation: Check the cognito:groups claim to ensure the user belongs to the
required groups.
Decorators: Use decorators to protect routes and enforce both authentication and
authorization.
This approach allows you to handle both authentication and authorization based on
Cognito user groups in your Flask application.
You said:
there may be a possibility that group checking is not necessary for few apis then
how to handle?
ChatGPT said:
ChatGPT
If you have some APIs where group checking is not required, you can design your
decorators to be flexible, allowing group validation to be optional. Here’s how you
can adapt your Flask application to handle both cases:
python
Copy code
from functools import wraps
from flask import request, jsonify
def authorize(groups=None):
def decorator(f):
@wraps(f)
def wrapper(*args, **kwargs):
token = request.headers.get('Authorization')
if not token:
return jsonify({"message": "Missing token"}), 401
try:
# Remove "Bearer " prefix if present
if token.startswith("Bearer "):
token = token[7:]
# Verify token
claims = verify_token(token)
if claims is None:
return jsonify({"message": "Invalid token"}), 401
return wrapper
return decorator
2. Use the Decorator in Routes
Apply the authorize decorator to routes where group checking is required, and omit
the groups parameter for routes where group checking is not necessary:
python
Copy code
@app.route('/add_m_user', methods=['POST'])
@authorize(groups=["Superadmin", "Admin"])
def create_item():
# Your logic here
return jsonify({"message": "User added successfully"}), 200
@app.route('/public_resource', methods=['GET'])
@authorize()
def public_resource():
# This route does not require group checking
return jsonify({"message": "Public resource accessed"}), 200
3. Handle Routes without Token Validation
If you have routes that don’t require any authentication or authorization, simply
omit the authorize decorator:
python
Copy code
@app.route('/open_resource', methods=['GET'])
def open_resource():
# This route is open to everyone
return jsonify({"message": "Open resource accessed"}), 200
Summary
Flexible Decorator: Modify the decorator to handle both group validation and non-
group validation scenarios by making the groups parameter optional.
Selective Application: Apply the authorize decorator with or without groups as
needed based on the route requirements.
No Validation Routes: Simply omit the authorize decorator for routes where
authentication and authorization are not required.
This approach allows you to use a single decorator for both protected and
unprotected routes, streamlining your code and maintaining flexibility.