Authentication Guide¶
This guide covers implementing authentication using imbi-common's auth primitives.
Overview¶
The auth package has these parts:
- JWT Tokens: Stateless authentication with access/refresh tokens
- Token Encryption: Fernet encryption for sensitive data at rest
- Config Encryption: Fernet encryption for stored configuration
secrets, with its own key (
IMBI_CONFIG_ENCRYPTION_KEY). See Config Encryption. - Permissions: FastAPI dependencies that authenticate a request (JWT, API key or agent API token) and check a permission
- Password Hashing: Argon2id hashes for passwords and API key secrets
Most services need only the permission dependencies:
import typing
import fastapi
from imbi.common.auth import permissions
@app.get('/projects')
async def list_projects(
auth: typing.Annotated[
permissions.AuthContext,
fastapi.Depends(permissions.require_permission('project:read')),
],
) -> list[dict]:
...
See Permissions for the token types, the API key cache and the auth settings provider, and Authentication for password hashing.
JWT Tokens¶
Creating Tokens¶
Generate access and refresh tokens after successful login:
from imbi.common.auth import core
# Create access token (short-lived, e.g., 1 hour)
access_token = core.create_access_token(
subject="user@example.com",
extra_claims={
"display_name": "Example User",
"is_admin": False
}
)
# Create refresh token (long-lived, e.g., 30 days)
refresh_token = core.create_refresh_token(
subject="user@example.com"
)
# Return both tokens to client
return {
"access_token": access_token,
"refresh_token": refresh_token,
"token_type": "bearer"
}
Verifying Tokens¶
Verify and decode tokens on protected endpoints:
from imbi.common.auth import core
try:
# Verify the token
payload = core.verify_token(access_token)
# Extract claims
user_email = payload["sub"]
is_admin = payload.get("is_admin", False)
# Proceed with authenticated request
print(f"Authenticated as: {user_email}")
except Exception as e:
# Token is invalid, expired, or malformed
print(f"Authentication failed: {e}")
# Return 401 Unauthorized
Token Refresh Flow¶
Implement token refresh to avoid frequent re-authentication:
from imbi.common.auth import core
try:
# Verify refresh token
payload = core.verify_token(refresh_token)
user_email = payload["sub"]
# Issue new access token with current claims
new_access_token = core.create_access_token(
subject=user_email,
extra_claims={"display_name": "Example User"}
)
return {"access_token": new_access_token, "token_type": "bearer"}
except Exception as e:
print(f"Token refresh failed: {e}")
# Return 401 Unauthorized - user must login again
Token Encryption¶
Encrypting Sensitive Tokens¶
Use Fernet encryption for storing OAuth tokens or other sensitive data:
from imbi.common.auth import encryption
# Encrypt a sensitive value before storing
encrypted_token = encryption.encrypt_token("oauth_access_token_abc123")
# Store the encrypted value in the database
Decrypting Tokens¶
Decrypt tokens when needed:
from imbi.common.auth import encryption
# Decrypt when you need to use the value
decrypted_token = encryption.decrypt_token(encrypted_value)
Using the TokenEncryption Class¶
For more control, use the singleton TokenEncryption class directly:
from imbi.common.auth.encryption import TokenEncryption
# Get the singleton instance
encryptor = TokenEncryption.get_instance()
# Encrypt/decrypt
encrypted = encryptor.encrypt("sensitive-value")
decrypted = encryptor.decrypt(encrypted)
Configuration¶
Configure auth settings in your config file or environment:
[auth]
# JWT
jwt_secret = "your-secret-key-change-in-production"
jwt_algorithm = "HS256"
access_token_expire_seconds = 3600 # 1 hour
refresh_token_expire_seconds = 2592000 # 30 days
# Encryption (generate with: python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())")
encryption_key = "your-fernet-key-here"
Security Notes
- Always use strong, randomly-generated secrets in production
- Store secrets in a secrets manager, not in config files
- Rotate secrets regularly
- Use HTTPS for all authentication endpoints
- Implement rate limiting on login endpoints