Skip to main content

vs-security

JWT-based authentication and authorization library for FastAPI services in the Viveka Sutra platform. Drop it in, configure a secret key, and your endpoints are protected in minutes.


Installation

pip install vs-security

With FastAPI guard support (recommended):

pip install "vs-security[fastapi]"

Dependencies: pyjwt, bcrypt, pydantic, vs-common


How It Works

Login Request
     │
     ▼
VsAuthManager.authenticate()
     │
     ▼
VsAuthProvider (e.g. VsUsernamePasswordAuthProvider)
     │  verifies credentials, returns VsAuthContext
     ▼
VsJWTProvider.generate_token()
     │  mints access + refresh JWT pair
     ▼
VsTokenPair  ──────────────────────────────────┐
                                               │
Protected Route Request (Bearer token)         │
     │                                         │
     ▼                                         │
VsSecurity (FastAPI dependency)                │
     │  verifies token, checks roles           │
     ▼                                         │
VsAuthContext available via get_auth_context() ◄┘

Quick Start

1. Initialize at startup

from vs_security.auth.vs_jwt_provider import VsJWTProvider
from vs_security.auth.vs_auth_manager import VsAuthManager
from vs_security.auth.vs_username_password_provider import VsUsernamePasswordAuthProvider
from vs_security.guard.vs_security_factory import VsSecurityFactory

jwt_provider = VsJWTProvider(config=config, token_store=token_store)

auth_manager = VsAuthManager(jwt_provider=jwt_provider)
auth_manager.register("username_password", VsUsernamePasswordAuthProvider(
    jwt_provider=jwt_provider,
    user_loader=my_user_loader,   # see below
))

VsSecurityFactory.init(
    secret_key=config.get("auth.secret_key"),
    algorithm="HS256",
)

2. Implement a user loader

async def my_user_loader(username: str):
    identity = await identity_repo.find_by_username(username)
    if not identity:
        return None
    context = VsAuthContext(
        user_id=identity.user_id,
        username=identity.username,
        roles=identity.roles,
        provider="email",
    )
    return context, identity.hashed_password  # hashed_password is bcrypt hash

3. Add a login endpoint

from vs_security.schema.vs_credentials import VsUsernamePasswordCredentials
from vs_security.error.vs_auth_error import VsAuthenticationError

@post("/auth/login")
async def login(body: LoginRequest):
    try:
        token_pair = await auth_manager.authenticate(
            "username_password",
            VsUsernamePasswordCredentials(username=body.username, password=body.password),
        )
        return {"access_token": token_pair.access_token, "refresh_token": token_pair.refresh_token}
    except VsAuthenticationError as e:
        raise HTTPException(status_code=401, detail=str(e))

4. Protect routes

from vs_security.guard.vs_security_factory import VsSecurityFactory
from vs_security.guard.vs_security import get_auth_context

# Protect all routes in a controller
@controller("/llm", guards=[VsSecurityFactory.get()])
class LlmController:

    @get("/data")
    async def get_data(self):
        context = get_auth_context()   # available anywhere after guard runs
        return {"user_id": str(context.user_id)}

5. Role-based access

# Only users with "admin" role can access
@controller("/admin", guards=[VsSecurityFactory.with_roles(["admin"])])
class AdminController:
    ...

# Or check roles manually inside a handler
context = get_auth_context()
if not context.has_role("admin"):
    raise HTTPException(status_code=403)

Configuration

Set these in your config.ini (read by VsBaseConfig):

Key Default Description
auth.secret_key required JWT signing secret
auth.algorithm HS256 JWT algorithm
auth.access_expiry_minutes 15 Access token lifetime
auth.refresh_expiry_days 7 Refresh token lifetime

Token Lifecycle

# Refresh an access token
new_pair = await jwt_provider.refresh_token(body.refresh_token)

# Revoke a single refresh token (logout)
await jwt_provider.revoke_token(user_id, body.refresh_token)

# Revoke all tokens (logout everywhere)
await jwt_provider.revoke_all_tokens(user_id)

Token revocation requires a VsTokenStore implementation. Implement the interface and pass it to VsJWTProvider:

class VsTokenStore(ABC):
    async def save(self, user_id: UUID, refresh_token: str) -> None: ...
    async def get_all(self, user_id: UUID) -> List[str]: ...
    async def delete(self, user_id: UUID, refresh_token: str) -> None: ...
    async def delete_all(self, user_id: UUID) -> None: ...

Custom Auth Providers

Extend VsAuthProvider to support OAuth, API keys, or any other credential type:

from vs_security.auth.vs_auth_provider import VsAuthProvider
from vs_security.schema.vs_auth_context import VsAuthContext
from vs_security.schema.vs_credentials import VsCredentials

class MyApiKeyCredentials(VsCredentials):
    api_key: str

class ApiKeyAuthProvider(VsAuthProvider):
    async def authenticate(self, credentials: VsCredentials) -> VsAuthContext:
        # validate api_key, return VsAuthContext
        ...

auth_manager.register("api_key", ApiKeyAuthProvider())

Error Reference

Exception HTTP Status When
VsInvalidCredentialsError 401 Wrong username or password
VsTokenExpiredError 401 JWT has expired
VsTokenRevokedError 401 Refresh token was revoked
VsAuthenticationError 401 Base auth failure
VsInsufficientRolesError 403 User lacks required roles

VsSecurity maps these automatically to HTTP responses — you only need to catch them in your login/refresh endpoints.


VsAuthContext Reference

Available inside any protected route via get_auth_context():

context.user_id          # UUID
context.username         # str
context.roles            # List[str]
context.provider         # str  (e.g. "email")

context.has_role("admin")              # bool
context.has_any_role("admin", "mod")   # bool
context.has_all_roles("admin", "mod")  # bool

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

vs_security-0.1.2.tar.gz (10.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

vs_security-0.1.2-py3-none-any.whl (12.1 kB view details)

Uploaded Python 3

File details

Details for the file vs_security-0.1.2.tar.gz.

File metadata

  • Download URL: vs_security-0.1.2.tar.gz
  • Upload date:
  • Size: 10.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.11.15

File hashes

Hashes for vs_security-0.1.2.tar.gz
Algorithm Hash digest
SHA256 2c030b3f1d7cd0d385a43f0d32d453b0f02572592d57d02a4e18e50c2f07466c
MD5 002328f5a4eeaffd09169ab8ce3e24e7
BLAKE2b-256 039cde069645a5a47cf747fd8da5595a706b7a1b31cd380ae28d76566c5c57af

See more details on using hashes here.

File details

Details for the file vs_security-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: vs_security-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 12.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.11.15

File hashes

Hashes for vs_security-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 f416acfc5962ce167b658c3d37bea08e5431be0a01c4983cde1dd94139e81b21
MD5 616329e78fff9234b694f061df599da2
BLAKE2b-256 2331c97c245eeac85c80eebed0e1476b47187080ce149d33e35d2888ae1f33b6

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page