PythonAuth

JWT

Issue and verify HS256 JSON Web Tokens with JWTModule and JwtGuard

Use JWTs when you sign your users in yourself: your server issues a token after login, and the guard checks it on every call. For sign-in through an identity provider, use OAuth 2.1 instead.

Set up JWTModule

Import the module in your root module.

app_module.py
from nitrostack import module, JWTModule

@module(
    name="app",
    imports=[
        JWTModule.for_root(
            secret_env_var="JWT_SECRET",
            expires_in="24h",
            audience="my-server",
            issuer="https://auth.example.com",
        ),
    ],
)
class AppModule:
    pass
OptionDefaultDescription
secret_env_var"JWT_SECRET"The environment variable that holds the signing secret
expires_in"24h"Token lifetime, as a number followed by h, m, or s, or plain seconds
audiencenoneIf set, added to new tokens and required when verifying
issuernoneIf set, added to new tokens and required when verifying

Tokens are signed with HS256.

Set JWT_SECRET

If the environment variable isn't set, the module falls back to a built-in development secret so local runs don't fail. Anyone can sign a token with that secret, so always set JWT_SECRET outside development.

Protect a tool

Put JwtGuard on the tools that need a signed-in user. It reads Authorization: Bearer <token>, verifies the signature, expiry, audience, and issuer, and fills context.auth.

from nitrostack import tool, use_guards, JwtGuard, ExecutionContext

@tool(name="my_orders", description="List my orders", input_schema=EmptyInput)
@use_guards(JwtGuard)
async def my_orders(self, input: EmptyInput, context: ExecutionContext) -> dict:
    user_id = context.auth.subject
    ...

context.auth.subject comes from the token's sub claim, scopes from its scopes claim, and claims holds the whole payload. A call with a missing, malformed, expired, or wrongly signed token is rejected.

Issue tokens

Inject JWTService into a login tool and call create_token with the claims you want.

from nitrostack import injectable, tool
from nitrostack.auth.jwt import JWTService

@injectable(deps=[JWTService])
class AuthController:
    def __init__(self, jwt: JWTService):
        self.jwt = jwt

    @tool(name="login", description="Sign in", input_schema=LoginInput)
    async def login(self, input: LoginInput, context) -> dict:
        # check the credentials first
        token = self.jwt.create_token({"sub": input.username, "scopes": ["orders:read"]})
        return {"token": token}

create_token adds iat and exp for you, and aud and iss when you configured them. verify_token(token) returns the payload, or raises ValueError with the reason.

Next steps

On this page