Metadata-Version: 2.4
Name: fastapi-rbac-lite
Version: 0.1.1
Summary: Pluggable RBAC permission checking for FastAPI -- bring your own token verifier and permission store.
Author-email: Athul A <athulbabu.a@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/athul2346/fastapi-rbac-lite
Project-URL: Repository, https://github.com/athul2346/fastapi-rbac-lite
Project-URL: Issues, https://github.com/athul2346/fastapi-rbac-lite/issues
Keywords: fastapi,rbac,jwt,jwks,authorization,authentication,permissions
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Framework :: FastAPI
Classifier: Topic :: Security
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: fastapi>=0.100
Requires-Dist: pyjwt[crypto]>=2.8
Requires-Dist: httpx>=0.24
Provides-Extra: dev
Requires-Dist: pytest>=7.4; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
Requires-Dist: httpx>=0.24; extra == "dev"
Requires-Dist: cryptography>=41.0; extra == "dev"
Requires-Dist: uvicorn>=0.23; extra == "dev"
Requires-Dist: build>=1.0; extra == "dev"
Requires-Dist: twine>=4.0; extra == "dev"
Dynamic: license-file

# fastapi-rbac-lite

Pluggable RBAC (role/permission) checking for FastAPI. Bring your own token
verifier and your own permission store — get a clean `require_any_permission`
dependency for your routes.

Most FastAPI auth libraries assume one specific setup: permissions baked
into the JWT itself, or one specific database schema. `fastapi-rbac-lite`
instead defines two small interfaces — **how to verify a token** and
**how to resolve a group into permissions** — and lets you plug in
whatever fits your infrastructure, with a ready-to-use default for the
common case (JWT + JWKS, HTTP-based RBAC service).

## Install

```bash
pip install fastapi-rbac-lite
```

## Quickstart

```python
from fastapi import FastAPI, Depends
from fastapi_rbac_lite import RBAC, JWKSVerifier, HTTPPermissionResolver

app = FastAPI()

rbac = RBAC(
    verifier=JWKSVerifier(
        trusted_issuers=["https://auth.example.com/application/o/myapp"],
        audiences=["myapp-backend"],
    ),
    resolver=HTTPPermissionResolver(
        base_url="http://rbac-service:8001",
        endpoint="/roles/permissions-bulk",
    ),
    bypass=lambda payload: payload.get("preferred_username") == "internal-service-account",
)

@app.get(
    "/invoices",
    dependencies=[Depends(rbac.require_any_permission(["invoice:read", "invoice:approve"]))],
)
def get_invoices():
    ...
```

`require_any_permission([...])` passes if the caller's groups resolve to
**at least one** of the listed permissions. Call it with no arguments (or
an empty list) to require only a valid token, with no permission check.

Use `rbac.get_current_user` as a dependency when you just need "any
authenticated user," with no permission check at all.

## Why two interfaces instead of one library-shaped solution

- **`TokenVerifier`** — how do you know who's calling? The built-in
  `JWKSVerifier` verifies RS256 JWTs against one or more trusted issuers
  via their published JWKS, with per-issuer key caching. Using a
  non-JWT scheme? Implement your own class with an async
  `verify(token) -> dict` method — no inheritance required.

- **`PermissionResolver`** — where do group → permission mappings live?
  Ship your own resolver, or use one of:
  - `HTTPPermissionResolver` — calls an external RBAC microservice
  - `StaticPermissionResolver` — in-memory dict, useful for tests/local dev

Both are `typing.Protocol`s, so any object with the right method signature
works — you don't need to import or subclass anything from this package.

## Writing your own resolver

```python
class RedisPermissionResolver:
    def __init__(self, redis_client):
        self.redis = redis_client

    async def resolve(self, groups: list[str]) -> set[str]:
        perms = set()
        for g in groups:
            perms |= set(await self.redis.smembers(f"group:{g}:permissions"))
        return perms
```

Pass it straight into `RBAC(resolver=RedisPermissionResolver(...))`.

## License

MIT
