Authentication¶
flarchitect provides several helpers so you can secure your API quickly.
Enable one or more strategies via API_AUTHENTICATE_METHOD. Available
methods are jwt, basic, api_key and custom. Each example below
uses the common setup defined in demo/authentication/app_base.py. Runnable
snippets demonstrating each strategy live in the project repository:
jwt_auth.py, basic_auth.py and api_key_auth.py.
You can also protect routes based on user roles using the
Role-based access decorator.
Error responses¶
Missing or invalid credentials return a 401 response:
{
"errors": {"error": "Authorization header missing"},
"status_code": 401,
"value": null
}
Expired tokens also yield a 401:
{
"errors": {"error": "Token has expired"},
"status_code": 401,
"value": null
}
Refresh failures, such as an invalid refresh token, respond with 403:
{
"errors": {"error": "Invalid or expired refresh token"},
"status_code": 403,
"value": null
}
JWT authentication¶
JSON Web Tokens (JWT) allow a client to prove their identity by including a
signed token with every request. The token typically contains the user’s ID and
an expiry timestamp. Clients obtain an access/refresh pair from a login endpoint
and then send the access token in the Authorization header:
Authorization: Bearer <access-token>
To enable JWT support you must provide ACCESS_SECRET_KEY and
REFRESH_SECRET_KEY values along with a user model. A minimal configuration
looks like:
class Config(BaseConfig):
API_AUTHENTICATE_METHOD = ["jwt"]
ACCESS_SECRET_KEY = "access-secret"
REFRESH_SECRET_KEY = "refresh-secret"
API_USER_MODEL = User
API_USER_LOOKUP_FIELD = "username"
API_CREDENTIAL_CHECK_METHOD = "check_password"
demo/authentication/jwt_auth.py contains a full example including a login
route:
from flask import abort, request
from flask_jwt_extended import (
create_access_token,
create_refresh_token,
)
@app.post("/login")
def login():
user = User.query.filter_by(username=request.json["username"]).first()
if user and user.check_password(request.json["password"]):
return {
"access_token": create_access_token(identity=user.id),
"refresh_token": create_refresh_token(identity=user.id),
}
abort(401)
Send subsequent requests with the Authorization header set to the access
token and refresh it with the refresh token when it expires.
Basic authentication¶
HTTP Basic Auth is the most straightforward option. The client includes a
username and password in the Authorization header on every request. The
credentials are base64 encoded but otherwise sent in plain text, so HTTPS is
strongly recommended.
Provide a lookup field and password check method on your user model:
class Config(BaseConfig):
API_AUTHENTICATE_METHOD = ["basic"]
API_USER_MODEL = User
API_USER_LOOKUP_FIELD = "username"
API_CREDENTIAL_CHECK_METHOD = "check_password"
flarchitect also provides a simple login route for this strategy. POST to
/auth/login with a Basic Authorization header to verify
credentials and receive basic user information:
curl -X POST -u username:password http://localhost:5000/auth/login
You can then access endpoints with tools such as curl:
curl -u username:password http://localhost:5000/api/books
See demo/authentication/basic_auth.py for a runnable snippet.
API key authentication¶
API key auth associates a user with a single token. Clients send the token in
each request, usually via a header like X-API-Key or as a query string
parameter. flarchitect passes the token to a function you provide, and the
function returns the matching user.
If you store hashed tokens on the model, set API_CREDENTIAL_HASH_FIELD to the attribute holding the hash so flarchitect can validate keys.
Attach a function that accepts an API key and returns a user. The function can
also call set_current_user:
def lookup_user_by_token(token: str) -> User | None:
user = User.query.filter_by(api_key=token).first()
if user:
set_current_user(user)
return user
class Config(BaseConfig):
API_AUTHENTICATE_METHOD = ["api_key"]
API_KEY_AUTH_AND_RETURN_METHOD = staticmethod(lookup_user_by_token)
When this method is enabled flarchitect exposes a companion login route. POST
an Api-Key Authorization header to /auth/login to validate the key
and retrieve basic user details:
curl -X POST -H "Authorization: Api-Key <token>" http://localhost:5000/auth/login
Example request:
curl -H "X-API-Key: <token>" http://localhost:5000/api/books
See demo/authentication/api_key_auth.py for more detail.
Custom authentication¶
For complete control supply your own callable. This method lets you support any
authentication strategy you like: session cookies, HMAC signatures or
third-party OAuth flows. Your callable should return True on success and may
call set_current_user to attach the authenticated user to the request.
def custom_auth() -> bool:
token = request.headers.get("X-Token", "")
user = User.query.filter_by(api_key=token).first()
if user:
set_current_user(user)
return True
return False
class Config(BaseConfig):
API_AUTHENTICATE_METHOD = ["custom"]
API_CUSTOM_AUTH = staticmethod(custom_auth)
Clients can then call your API with whatever headers your function expects:
curl -H "X-Token: <token>" http://localhost:5000/api/books
See demo/authentication/custom_auth.py for this approach in context.
Role-based access¶
Use the roles_required decorator to allow only users with specific roles to
access an endpoint. The decorator checks the roles attribute on
current_user which is populated by the active authentication method.
from flarchitect.authentication import roles_required
@app.get("/admin")
@roles_required("admin")
def admin_dashboard():
return {"status": "ok"}
You can require multiple roles by passing more than one name:
@roles_required("admin", "editor")
def update_post():
...
Ensure your user model exposes a list of role names, for example
User.roles = ["admin", "editor"]. If the authenticated user lacks any of
the required roles—or if no user is authenticated—a 403 response is raised.