Skip to content

JWT Auth

Auth wires up JWT-based authentication — registration, login, refresh, logout — backed by your ORM's User model, with almost no boilerplate.

Setup

from shakti import Auth, Config
from shakti.orm.database import Database

config = Config()
db = Database(config.require("db.url"))
auth = Auth(db, secret_key=config.require("auth.secret_key"))
auth.init_app(app)

init_app does two things: registers Auth in the DI container (so Depends(...) and container injection can find it), and mounts an auth router at prefix (default /auth).

Constructor options:

Parameter Default Meaning
secret_key — required HMAC signing key for JWTs
algorithm "HS256" PyJWT algorithm
access_token_expire_minutes 30 access token lifetime
refresh_token_expire_days 7 refresh token lifetime
prefix "/auth" mount point for the built-in routes

Built-in routes

Mounted automatically under prefix:

Route Body Returns
POST /auth/register {email, username, password, role?} {message, user}
POST /auth/login {email, password} {access_token, refresh_token, token_type, user}
POST /auth/refresh {refresh_token} {access_token, refresh_token, token_type}
POST /auth/logout — (Bearer token) {message}
GET /auth/me — (Bearer token) the current user

login and refresh persist the issued refresh token on the User row, so refresh and logout can invalidate it server-side (revoking a refresh token means it no longer matches what's stored). Passwords are hashed with bcrypt; they're never stored or returned in plaintext.

Protecting routes

auth.get_current_user() returns a dependency that verifies the Authorization: Bearer <token> header and resolves the User:

from shakti import Depends
from shakti.auth.models import User

@app.get("/me")
async def me(user: User = Depends(auth.get_current_user())) -> dict:
    return user.to_dict()

It raises 401 if the header is missing/malformed, the token is expired or invalid, the token isn't an access token (refresh tokens are rejected here), or the user no longer exists / is inactive.

The User model

shakti.auth.models.User (SQLAlchemy model, via Shakti's ORM — see ORM: Models) has id, email, username, hashed_password, is_active, role, refresh_token, plus created_at/updated_at from TimestampMixin. user.to_dict() returns the public-safe fields (never hashed_password or refresh_token).

Managing users programmatically

user = await auth.register_user(email="a@b.com", username="alice", password="s3cret", role="admin")

Raises 409 if the email or username is already taken.

Custom claims

Access tokens are issued with sub (user id) and role claims; refresh tokens carry just sub. If you need more in the token, use shakti.auth.tokens.create_access_token(payload, secret, ...) directly rather than Auth's built-in login flow.

See RBAC for role-based restrictions and API Keys for non-JWT service-to-service auth.