Security — First Steps
This tutorial builds a simple authentication system step by step. By the end you’ll understand the full OAuth2 password flow and how to protect routes.
The Problem
Section titled “The Problem”You need to protect a route so only authenticated users can access it. Unauthenticated requests should get a 401 error.
Step 1: Add a Security Dependency
Section titled “Step 1: Add a Security Dependency”from justapi import JustAPIApp, Securityfrom fastapi.security import OAuth2PasswordBearer
app = JustAPIApp()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/users/me")def read_users_me(token: str = Security(oauth2_scheme)): return {"token": token}What happens here?
Section titled “What happens here?”OAuth2PasswordBearer(tokenUrl="token")creates a dependency that expects anAuthorization: Bearer <token>header.Security(oauth2_scheme)tells JustAPI this is a security requirement — it appears in the OpenAPI docs with an “Authorize” button.- The
tokenparameter receives the raw token string from the header.
If the request has no Authorization header, JustAPI returns a 401 error automatically — no code needed.
Try it
Section titled “Try it”Start the server and go to http://localhost:8000/docs. You’ll see an Authorize button at the top.
Click it, enter any value, and then call /users/me. The request includes the header automatically.
Without the header:
{ "detail": "Not authenticated"}Step 2: Validate the Token
Section titled “Step 2: Validate the Token”The token is just a string. We need to verify it:
from justapi import JustAPIApp, Security, HTTPExceptionfrom fastapi.security import OAuth2PasswordBearer
app = JustAPIApp()oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
fake_users_db = { "alice": {"user_id": 1, "name": "Alice"}, "bob": {"user_id": 2, "name": "Bob"},}
def get_current_user(token: str = Security(oauth2_scheme)): if token not in fake_users_db: raise HTTPException(status_code=401, detail="Invalid token") return fake_users_db[token]
@app.get("/users/me")def read_users_me(current_user: dict = Security(get_current_user)): return current_userNow the flow is:
- Client sends
Authorization: Bearer alice oauth2_schemeextracts"alice"as the tokenget_current_userlooks up"alice"in the database- If found, returns the user dict — which becomes
current_userin the handler - If not found, raises
401
Step 3: Add a Token Endpoint
Section titled “Step 3: Add a Token Endpoint”The frontend needs a way to log in and get a token. Create a /token endpoint:
from justapi import JustAPIApp, Security, HTTPExceptionfrom fastapi.security import OAuth2PasswordBearerfrom pydantic import BaseModel
app = JustAPIApp()oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
class TokenRequest(BaseModel): username: str password: str
fake_users_db = { "alice": {"user_id": 1, "name": "Alice", "password": "secret123"}, "bob": {"user_id": 2, "name": "Bob", "password": "pass456"},}
def get_current_user(token: str = Security(oauth2_scheme)): if token not in fake_users_db: raise HTTPException(status_code=401, detail="Invalid token") return fake_users_db[token]
@app.post("/token")def login(request: TokenRequest): user = fake_users_db.get(request.username) if not user or user["password"] != request.password: raise HTTPException(status_code=401, detail="Wrong username or password") return {"access_token": request.username, "token_type": "bearer"}
@app.get("/users/me")def read_users_me(current_user: dict = Security(get_current_user)): return current_userThe full flow
Section titled “The full flow”- Client sends
POST /tokenwith{"username": "alice", "password": "secret123"} - Server returns
{"access_token": "alice", "token_type": "bearer"} - Client sends
GET /users/mewith headerAuthorization: Bearer alice - Server returns
{"user_id": 1, "name": "Alice"}
Step 4: Add User Roles
Section titled “Step 4: Add User Roles”Extend the dependency to check permissions:
def get_current_user(token: str = Security(oauth2_scheme)): if token not in fake_users_db: raise HTTPException(status_code=401, detail="Invalid token") return fake_users_db[token]
def require_admin(current_user: dict = Security(get_current_user)): if current_user.get("role") != "admin": raise HTTPException(status_code=403, detail="Admin access required") return current_user
@app.get("/admin/dashboard")def admin_dashboard(admin: dict = Security(require_admin)): return {"message": f"Welcome admin {admin['name']}"}Now /admin/dashboard requires both a valid token AND admin role.
What JustAPI Does Under the Hood
Section titled “What JustAPI Does Under the Hood”When you use Security(oauth2_scheme):
- Before the request: JustAPI checks for the
Authorizationheader - Missing header: Returns
401with{"detail": "Not authenticated"} - Invalid header format: Returns
401with details about the expected format - Valid header: Extracts the token and passes it to your dependency
- In the docs: Adds an “Authorize” button to Swagger UI
OAuth2PasswordBearer(tokenUrl="token")creates the auth dependencySecurity()marks it as a security requirement in the docs- Your dependency validates the token and returns the user
- Unauthenticated requests get a
401automatically - Chain dependencies for role-based access control
See Also
Section titled “See Also”- Get Current User — extracting user from token
- Simple OAuth2 — password-based OAuth2
- OAuth2 + JWT Tokens — production JWT implementation
- Advanced Security — OAuth2 scopes, API keys