JSON Body Tokens
HTTP routes can read tokens from JSON request bodies as well as headers. This is useful when the client already sends a JSON payload and you want the token to travel inside that document instead of a header.
The HTTP transport settings that control this behavior are:
authpaseto_token_locationauthpaseto_json_keyauthpaseto_json_type
You can configure JSON transport globally, or opt into it per route with
paseto_required(location="json").
from fastapi import Depends, FastAPI, HTTPException, Request
from fastapi.responses import JSONResponse
from pydantic import BaseModel
from fastapi_paseto import AuthPASETO
from fastapi_paseto.exceptions import AuthPASETOException
app = FastAPI()
class User(BaseModel):
username: str
password: str
class AccessTokenPayload(BaseModel):
access_token: str
class RefreshTokenPayload(BaseModel):
refresh_token: str
@AuthPASETO.load_config
def get_config():
return {
"authpaseto_secret_key": "secret",
"authpaseto_token_location": ["headers", "json"],
"authpaseto_json_key": "access_token",
"authpaseto_json_type": None,
}
@app.exception_handler(AuthPASETOException)
def authpaseto_exception_handler(request: Request, exc: AuthPASETOException):
return JSONResponse(status_code=exc.status_code, content={"detail": exc.message})
@app.post("/login")
def login(user: User, Authorize: AuthPASETO = Depends()):
if user.username != "test" or user.password != "test":
raise HTTPException(status_code=401, detail="Bad username or password")
access_token = Authorize.create_access_token(subject=user.username)
refresh_token = Authorize.create_refresh_token(subject=user.username)
return {"access_token": access_token, "refresh_token": refresh_token}
@app.post("/json-protected")
def json_protected(
payload: AccessTokenPayload,
Authorize: AuthPASETO = Depends(),
):
Authorize.paseto_required(location="json")
return {"user": Authorize.get_subject(), "token_length": len(payload.access_token)}
@app.post("/json-refresh")
def json_refresh(
payload: RefreshTokenPayload,
Authorize: AuthPASETO = Depends(),
):
Authorize.paseto_required(
location="json",
token_key="refresh_token",
refresh_token=True,
)
new_access_token = Authorize.create_access_token(subject=Authorize.get_subject())
return {"access_token": new_access_token, "refresh_token_length": len(payload.refresh_token)}
With the example above:
POST /json-protectedexpects{"access_token": "<PASETO>"}in the JSON bodyPOST /json-refreshexpects{"refresh_token": "<PASETO>"}in the JSON body
If you set a JSON prefix, the token value must include it exactly:
{"access_token": "Bearer <PASETO>"}
When both header and JSON transport are enabled, header lookup runs first and JSON lookup runs second.