Metadata-Version: 2.4
Name: flask-advanced-auth
Version: 0.1.0
Summary: Advanced JWT authentication for Flask with multi-rule support, token tracking, and blacklisting.
Project-URL: Homepage, https://github.com/Wehda23/Flask-Advanced-Authentication
Project-URL: Repository, https://github.com/Wehda23/Flask-Advanced-Authentication
Project-URL: Bug Tracker, https://github.com/Wehda23/Flask-Advanced-Authentication/issues
Author: Waheed
License: MIT
License-File: LICENSE
Keywords: auth,authentication,blacklist,flask,flask-sqlalchemy,jwt,token
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: Flask
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Internet :: WWW/HTTP :: Session
Classifier: Topic :: Security
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: flask-sqlalchemy>=3.0.0
Requires-Dist: flask>=2.3.0
Requires-Dist: pydantic>=2.0.0
Requires-Dist: pyjwt>=2.8.0
Requires-Dist: python-dotenv>=1.0.0
Provides-Extra: dev
Requires-Dist: black>=23.0.0; extra == 'dev'
Requires-Dist: isort>=5.12.0; extra == 'dev'
Requires-Dist: mypy>=1.5.0; extra == 'dev'
Requires-Dist: pre-commit>=3.4.0; extra == 'dev'
Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
Requires-Dist: pytest-flask>=1.3.0; extra == 'dev'
Requires-Dist: pytest>=7.4.0; extra == 'dev'
Requires-Dist: ruff>=0.1.0; extra == 'dev'
Description-Content-Type: text/markdown

# flask-advanced-auth

[![PyPI version](https://img.shields.io/pypi/v/flask-advanced-auth.svg)](https://pypi.org/project/flask-advanced-auth/)
[![Python versions](https://img.shields.io/pypi/pyversions/flask-advanced-auth.svg)](https://pypi.org/project/flask-advanced-auth/)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
[![Tests](https://github.com/Wehda23/Flask-Advanced-Authentication/actions/workflows/ci.yml/badge.svg)](https://github.com/Wehda23/Flask-Advanced-Authentication/actions)
[![Coverage](https://img.shields.io/badge/coverage-88%25-brightgreen.svg)](https://github.com/Wehda23/Flask-Advanced-Authentication)

**flask-advanced-auth** is a production-grade, highly configurable JWT authentication extension for Flask and Flask-SQLAlchemy. It provides multi-rule support (different token lifetimes, secrets, and models for users vs. admins), built-in database token tracking, automatic token revocation (blacklisting), and clean route decorators.

---

## ✨ Features

- 🔑 **Multi-Rule Authentication**: Define distinct rules for different user tiers (e.g. `users`, `managers`, `api_clients`) with independent secrets, algorithms, and lifetimes.
- 🛡️ **Easy Route Protection**: Clean `@authenticate(auth_classes=[AuthenticateJWT])` decorator with customizable 401 response and model injection into `request.instance`.
- 🔄 **Access & Refresh Tokens**: Native support for short-lived access tokens and long-lived refresh tokens with convenient `refresh_access_token()` helper.
- 🚫 **Token Revocation (Blacklisting)**: Built-in revocation mechanism with database persistence and `logout()` helper.
- 📊 **Token Tracking**: Track issued tokens in the database to detect anomalies, enforce single-session limits, or audit activity.
- 🔌 **Multiple Extraction Strategies**: Extract tokens from `Authorization: Bearer <token>` headers, HTTP cookies, or URL query parameters.
- ⚡ **Pydantic Validation**: Strict configuration schema validation powered by Pydantic V2 with flexible aliases.
- 🧪 **Thoroughly Tested**: 100% test pass rate with ≥ 88% test coverage.

---

## 📦 Installation

```bash
pip install flask-advanced-auth
```

Or install with development dependencies:

```bash
pip install flask-advanced-auth[dev]
```

---

## 🚀 Quick Start (5 Minutes)

### 1. Configure Rules & Bootstrap

```python
from datetime import timedelta
from flask import Flask, jsonify, request
from flask_sqlalchemy import SQLAlchemy
from flask_advanced_auth import (
    initialize_jwt_authentication,
    authenticate,
    AuthenticateJWT,
    TokenFactory,
    refresh_access_token,
    logout,
)

app = Flask(__name__)
app.config["SECRET_KEY"] = "your-app-secret"
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///app.db"
db = SQLAlchemy(app)

# Define your JWT rules
JWT_RULES = {
    "default": {
        "description": "Standard user access token",
        "secret_key": "your-jwt-secret-key",
        "algorithm": "HS256",
        "access_token_lifetime": timedelta(minutes=15),
        "refresh_token_lifetime": timedelta(days=7),
        "token_header": "Bearer ",
    }
}

# Initialize flask-advanced-auth
initialize_jwt_authentication(
    database_path="app.db",
    rules=JWT_RULES,
)
```

### 2. Issue Tokens on Login

```python
@app.route("/api/login", methods=["POST"])
def login():
    username = request.json.get("username")
    
    factory = TokenFactory()
    access_token = factory.generate_token(
        rule_name="default",
        extra_payload={"sub": username},
    )
    refresh_token = factory.generate_refresh_token(
        rule_name="default",
        extra_payload={"sub": username},
    )
    
    return jsonify({
        "access_token": access_token,
        "refresh_token": refresh_token,
    })
```

### 3. Protect Routes

```python
@app.route("/api/dashboard")
@authenticate(auth_classes=[AuthenticateJWT], rule_name="default")
def dashboard():
    return jsonify({"message": "Welcome to your protected dashboard!"})
```

### 4. Refresh Tokens & Logout

```python
@app.route("/api/refresh", methods=["POST"])
def refresh():
    token = request.json.get("refresh_token")
    new_access_token = refresh_access_token(token, rule_name="default")
    return jsonify({"access_token": new_access_token})

@app.route("/api/logout", methods=["POST"])
def user_logout():
    token = request.headers.get("Authorization", "").replace("Bearer ", "")
    logout(token, rule_name="default")
    return jsonify({"message": "Token revoked successfully."})
```

---

## ⚙️ Configuration Reference

Rules are passed as a dictionary where each key represents a rule name (e.g. `"default"`, `"admin"`):

| Field | Type | Default | Description |
|---|---|---|---|
| `description` | `str` | *Required* | Description of what this token rule is used for. |
| `secret_key` | `str` | *Required* | Secret signing key for encoding and decoding tokens. |
| `algorithm` | `str` | *Required* | JWT signing algorithm (e.g. `'HS256'`, `'RS256'`). |
| `access_token_lifetime` | `timedelta` | *Required* | Expiry duration for access tokens. |
| `refresh_token_lifetime` | `timedelta` | `None` | Expiry duration for refresh tokens. |
| `token_header` | `str` | `'Bearer '` | Expected header prefix in `Authorization` header. |
| `table` | `bool` | `False` | Whether to link tokens to a database entity. |
| `table_path` | `str` | `None` | Dotted import path to model class (e.g. `'myapp.models.User'`). |
| `track_created` | `bool` | `False` | Persist issued tokens to the database. |
| `track_created_table_path` | `str` | `None` | Dotted import path to TrackModel class. |
| `track_created_allow_duplicates` | `bool` | `True` | Allow multiple active tokens for the same user. |
| `blacklisted` | `bool` | `False` | Enable token blacklist checks on verification. |
| `blacklisted_table_path` | `str` | `None` | Dotted import path to BlackListModel class. |

---

## 🧩 Authentication Strategies

You can choose how tokens are extracted by passing different authentication classes to `@authenticate`:

```python
from flask_advanced_auth import (
    authenticate,
    HeaderAuthentication,   # Reads Authorization: Bearer <token>
    CookieAuthentication,   # Reads token from HTTP cookies
    QueryParamAuthentication, # Reads ?token=<token>
)

# 1. From Authorization header
@app.route("/api/v1/data")
@authenticate(auth_classes=[HeaderAuthentication])
def api_data():
    return {"status": "ok"}

# 2. From Cookie
@app.route("/web/profile")
@authenticate(auth_classes=[CookieAuthentication])
def web_profile():
    return {"status": "ok"}

# 3. From URL parameter (e.g., SSE or WebSocket connection handshake)
@app.route("/stream")
@authenticate(auth_classes=[QueryParamAuthentication])
def stream():
    return {"status": "connected"}
```

---

## 🗄️ Database Model Mixins

When enabling `track_created` or `blacklisted`, use our ready-to-use SQLAlchemy model mixins:

```python
from flask_advanced_auth.models import AbstractTrackModelMixin, AbstractBlackListModelMixin
from myapp.database import db

class TrackedToken(db.Model, AbstractTrackModelMixin):
    __tablename__ = "tracked_tokens"

class BlacklistedToken(db.Model, AbstractBlackListModelMixin):
    __tablename__ = "blacklisted_tokens"
```

---

## 🧪 Running Tests

```bash
# Run tests with coverage
pytest -v

# Run single test module
pytest tests/test_decorators.py -v
```

---

## 🤝 Contributing

Contributions are welcome! Please check out [CONTRIBUTING.md](CONTRIBUTING.md) for setup instructions and guidelines.

---

## 📄 License

This project is licensed under the **MIT License** — see the [LICENSE](LICENSE) file for details.
