User model and schema library for authentication services
Project description
keylin
Features
- User model and schema library for authentication services
- Designed for integration with FastAPI and fastapi-users
- Reusable across multiple services
Installation
pip install keylin
Usage
from keylin.models import User
from fastapi import FastAPI, Depends
from sqlalchemy.ext.asyncio import AsyncSession
from keylin.db import get_async_session
app = FastAPI()
@app.get("/users/{user_id}")
async def get_user(user_id: str, session: AsyncSession = Depends(get_async_session)):
result = await session.execute(
User.__table__.select().where(User.id == user_id)
)
user = result.fetchone()
if user:
return user
return {"error": "User not found"}
Architecture & Integration Guide
Typical Architecture
A recommended architecture for a login/authentication service using keylin:
graph TD
A[Client App / Frontend]
B[API Gateway / HTTPS Proxy]
C[FastAPI Login Service - uses keylin]
D[Database - PostgreSQL, etc.]
A --> B
B --> C
C --> D
- keylin provides the user model, schemas, and authentication logic.
- The login service is a FastAPI app that imports keylin and wires up routers.
- Other services can import keylin to share the user model and schemas for consistent user data handling.
Example FastAPI Login Service
from fastapi import FastAPI
from keylin.auth import auth_backend, fastapi_users, get_user_manager
from keylin.schemas import UserRead, UserCreate
app = FastAPI()
# Auth routes
app.include_router(fastapi_users.get_auth_router(auth_backend), prefix="/auth/jwt", tags=["auth"])
app.include_router(fastapi_users.get_register_router(UserRead, UserCreate), prefix="/auth", tags=["auth"])
app.include_router(fastapi_users.get_users_router(UserRead, UserRead), prefix="/users", tags=["users"])
Environment Variables (Required for Production)
JWT_SECRET: Secret key for JWT signing (required; application will raise an error if not set)RESET_PASSWORD_SECRET: Secret for password reset tokens (optional, defaults to JWT secret)VERIFICATION_SECRET: Secret for verification tokens (optional, defaults to JWT secret)DATABASE_URL: SQLAlchemy DB URL (e.g.,postgresql+asyncpg://user:pass@host/dbname)
Note: If
JWT_SECRETis not set, the application will raise aRuntimeErrorat startup.
Security & Deployment Best Practices
- CORS: Configure CORS in your FastAPI app using
CORSMiddlewareto restrict allowed origins. - HTTPS: Always deploy behind a TLS-terminating proxy (e.g., NGINX, Caddy, cloud load balancer).
- Secret Management: Never hardcode secrets. Use environment variables or a secrets manager.
- Rate Limiting: Add rate limiting at the API gateway or with middleware to prevent brute-force attacks.
- Email Verification: Enable and configure email verification in your login service for new users.
- Audit Logging: Log authentication events for security monitoring.
Extending the User Model
- You can add fields to the
Usermodel and corresponding Pydantic schemas in your own code or by forking/extending keylin. - All models and schemas use type annotations and are compatible with FastAPI and Pydantic.
User Model & Database Schema
Default User Model Fields
| Field | Type | Description | Required | Notes |
|---|---|---|---|---|
| id | UUID | Unique user identifier | Yes | Primary key |
| str | User email (unique) | Yes | Unique, indexed | |
| hashed_password | str | Hashed password | Yes | Not exposed in API responses |
| full_name | str/None | User's full name | No | Optional |
| is_active | bool | Is the user active? | Yes | |
| is_superuser | bool | Is the user an admin? | Yes | |
| is_verified | bool | Has the user verified email | Yes |
Note: The table is named
"user", which is a reserved word in some databases (e.g., PostgreSQL). Consider renaming to"users"if you encounter issues.
Example User Schema
{
"id": "uuid",
"email": "user@example.com",
"full_name": "User Name",
"is_active": true,
"is_superuser": false,
"is_verified": true
}
Extending the User Model
- To add fields, update the
UserSQLAlchemy model and the corresponding Pydantic schemas. - Run a new Alembic migration to update the database schema.
- Update the UI to handle new fields as needed.
- Keep the UI and backend in sync regarding user data shape.
- Tip: If you need roles or permissions, add fields (e.g.,
role,permissions) to the model and schemas, and update your business logic accordingly.
Configuration Usage
The keylin.config.Settings class provides all configuration via environment variables or a .env file. There is no longer a global settings singleton.
- To use configuration, import and instantiate
Settingsdirectly:
from keylin.config import Settings
settings = Settings()
- If you are building a service that needs additional configuration, subclass
Settings:
from keylin.config import Settings as KeylinSettings
class MyServiceSettings(KeylinSettings):
MY_SERVICE_API_KEY: str
settings = MyServiceSettings()
- All environment variables must be set before instantiating
Settings. JWT_SECRETmust be set, or aRuntimeErrorwill be raised at startup.
Testing
- The test suite sets
JWT_SECRETautomatically for all tests (seetests/conftest.py). - All code and tests should instantiate
Settingsdirectly as needed. - There is no global singleton; each test or module should create its own
Settingsinstance if configuration is needed. - See
tests/unit/test_keylin.pyfor example tests and mocking patterns.
Setting Up the User Database
Before running your login service, you need to ensure the user table exists in your database.
Recommended: Use Alembic for Migrations
For production and team environments, use Alembic to manage database migrations:
alembic revision --autogenerate -m "create user table"
alembic upgrade head
Make sure your Alembic env.py includes the keylin model's metadata:
from keylin.models import Base
target_metadata = Base.metadata
Quick Local Setup: Create Tables Programmatically
For local development or quick tests, you can create the tables directly using SQLAlchemy:
from keylin.models import Base
from sqlalchemy import create_engine
engine = create_engine("sqlite:///./test.db") # Or your DB URL
Base.metadata.create_all(engine)
Note: For production, always use migrations to avoid data loss and ensure schema consistency.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file keylin-0.3.1.tar.gz.
File metadata
- Download URL: keylin-0.3.1.tar.gz
- Upload date:
- Size: 11.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a0c1d56587877763c115dc68212fbb7f7af54b04180dff321cfcdf9ddf7d73fa
|
|
| MD5 |
d3f7a4efea313ebe211bf54a4df95ad8
|
|
| BLAKE2b-256 |
308401e6b9b7da56beeed003afc081816c2a29724d800efa830a5a87d3c4866e
|
Provenance
The following attestation bundles were made for keylin-0.3.1.tar.gz:
Publisher:
publish.yml on beanone/keylin
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
keylin-0.3.1.tar.gz -
Subject digest:
a0c1d56587877763c115dc68212fbb7f7af54b04180dff321cfcdf9ddf7d73fa - Sigstore transparency entry: 207761992
- Sigstore integration time:
-
Permalink:
beanone/keylin@f94418a87acbfb2c2516beb9a5fd5c52d30e8c2b -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/beanone
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f94418a87acbfb2c2516beb9a5fd5c52d30e8c2b -
Trigger Event:
push
-
Statement type:
File details
Details for the file keylin-0.3.1-py3-none-any.whl.
File metadata
- Download URL: keylin-0.3.1-py3-none-any.whl
- Upload date:
- Size: 8.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
322efcad320e7ca47c5450ae7c7c0218bd7090af92e3d9678f88a7702ca9a1a1
|
|
| MD5 |
e97d301e23f26e962ac637f8ff1b2e74
|
|
| BLAKE2b-256 |
d2d5c768c589c26ee8e915974cb5bcbd8f3dfce34cb410b8668c150a6ecf5fad
|
Provenance
The following attestation bundles were made for keylin-0.3.1-py3-none-any.whl:
Publisher:
publish.yml on beanone/keylin
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
keylin-0.3.1-py3-none-any.whl -
Subject digest:
322efcad320e7ca47c5450ae7c7c0218bd7090af92e3d9678f88a7702ca9a1a1 - Sigstore transparency entry: 207761993
- Sigstore integration time:
-
Permalink:
beanone/keylin@f94418a87acbfb2c2516beb9a5fd5c52d30e8c2b -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/beanone
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f94418a87acbfb2c2516beb9a5fd5c52d30e8c2b -
Trigger Event:
push
-
Statement type: