Metadata-Version: 2.4
Name: ulfblk-auth
Version: 0.1.0
Summary: Authentication and authorization: JWT RS256, RBAC, brute force protection, credential encryption
Project-URL: Homepage, https://github.com/abelardodiaz/web25-991-bloques-reciclables
Project-URL: Documentation, https://github.com/abelardodiaz/web25-991-bloques-reciclables/tree/main/packages/python/ulfblk-auth
Project-URL: Repository, https://github.com/abelardodiaz/web25-991-bloques-reciclables
Project-URL: Issues, https://github.com/abelardodiaz/web25-991-bloques-reciclables/issues
Author-email: Abelardo Diaz <abelardo@bloques.dev>
License-Expression: MIT
Keywords: authentication,authorization,fastapi,jwt,rbac,security
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: FastAPI
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Security
Classifier: Topic :: Software Development :: Libraries
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: cryptography>=42.0
Requires-Dist: pyjwt[crypto]>=2.8
Requires-Dist: ulfblk-core
Provides-Extra: dev
Requires-Dist: httpx>=0.27; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Description-Content-Type: text/markdown

# ulfblk-auth

Authentication and authorization for FastAPI: JWT RS256, RBAC, brute force protection, and credential encryption.

Part of [Bloques Reciclables](https://github.com/abelardodiaz/web25-991-bloques-reciclables) - an open source ecosystem of reusable code blocks.

## Installation

```bash
uv add ulfblk-auth
# or
pip install ulfblk-auth
```

## Features

### JWT Manager (RS256)

Asymmetric JWT tokens with tenant and role support:

```python
from ulfblk_auth.jwt import JWTManager

jwt_manager = JWTManager(
    private_key=PRIVATE_KEY_PEM,
    public_key=PUBLIC_KEY_PEM,
    access_token_expire_minutes=30,
    refresh_token_expire_days=7,
)

# Create tokens
access_token = jwt_manager.create_access_token(
    user_id="user-123",
    tenant_id="acme",
    roles=["admin"],
    permissions=["users:read", "users:write"],
)

refresh_token = jwt_manager.create_refresh_token(
    user_id="user-123",
    tenant_id="acme",
)

# Decode and validate
token_data = jwt_manager.decode_token(access_token)
# token_data.user_id, token_data.tenant_id, token_data.roles, token_data.permissions
```

### RBAC (Role-Based Access Control)

FastAPI dependency injection for permission and role checks:

```python
from ulfblk_auth.rbac import configure, require_permissions, require_roles, get_current_user

# Configure once at startup
configure(jwt_manager)

# Require specific permissions
@app.delete("/users/{user_id}")
async def delete_user(user=Depends(require_permissions("users:delete"))):
    ...

# Require specific roles
@app.get("/admin/stats")
async def admin_stats(user=Depends(require_roles("admin", "manager"))):
    ...

# Get current user (any authenticated user)
@app.get("/me")
async def me(user=Depends(get_current_user)):
    return {"user_id": user.user_id, "tenant": user.tenant_id}
```

### Brute Force Protection

Storage-agnostic login attempt tracking with account lockout:

```python
from ulfblk_auth.brute_force import BruteForceProtection, LoginAttemptState

protection = BruteForceProtection(max_attempts=5, lockout_minutes=30)

# Check if locked
state = LoginAttemptState()  # load from your storage
if protection.is_locked(state):
    raise HTTPException(status_code=429, detail="Account locked")

# Record attempts
state = protection.record_failed_attempt(state, ip_address="1.2.3.4")
# or on success:
state = protection.record_successful_login(state, ip_address="1.2.3.4")
# persist state to your storage
```

### Credential Encryption (Fernet AES-256)

Encrypt sensitive credentials (API keys, tokens) for storage:

```python
from ulfblk_auth.credentials import CredentialEncryptor

# Generate a key (store securely, e.g., in env vars)
key = CredentialEncryptor.generate_key()

encryptor = CredentialEncryptor(key=key)
encrypted = encryptor.encrypt("sk-my-secret-api-key")
decrypted = encryptor.decrypt(encrypted)

# Key rotation
new_key = CredentialEncryptor.generate_key()
re_encrypted = encryptor.rotate_key(encrypted, new_key)
```

## Dependencies

- [ulfblk-core](https://github.com/abelardodiaz/web25-991-bloques-reciclables/tree/main/packages/python/ulfblk-core)
- PyJWT (RS256)
- cryptography (Fernet)

## Requirements

- Python 3.11+
- FastAPI 0.115+

## License

[MIT](https://github.com/abelardodiaz/web25-991-bloques-reciclables/blob/main/LICENSE)
