How NekoImageGallery Verifies Access Tokens and Admin Tokens
NekoImageGallery verifies access tokens and admin tokens using FastAPI dependency injection by comparing incoming x-access-token and x-admin-token headers against plaintext configuration values in app/config.py, automatically returning HTTP 401 errors when protection modes are enabled and credentials are invalid.
NekoImageGallery implements a lightweight, configuration-driven authentication system designed for self-hosted image galleries. The repository hv0905/nekoimagegallery stores token values and enablement flags in a centralized config module, then validates requests through reusable FastAPI dependency functions. This approach allows administrators to selectively protect search endpoints and administrative APIs using simple environment variables without complex database schemas or external identity providers.
Configuration-Driven Token Storage
The authentication system reads runtime settings from app/config.py, where boolean flags and token strings define the security posture:
access_protected: Enables or disables token checking for normal usersaccess_token: Expected string value for thex-access-tokenheaderadmin_api_enable: Toggles the administrative API protection layeradmin_token: Expected string value for thex-admin-tokenheader
These values initialize from environment variables or Docker secrets (lines 90-98 in app/config.py), allowing containerized deployments to inject credentials securely without hardcoding secrets into the source.
Core Verification Logic in app/Services/authentication.py
The app/Services/authentication.py module implements two verification layers: permissive checks that return boolean results for status reporting, and force functions that raise HTTP exceptions to block invalid requests.
Access Token Verification Pipeline
The verify_access_token() function implements the core comparison logic:
def verify_access_token(token: str | None) -> bool:
return (not config.access_protected) or (token is not None and token == config.access_token)
When access_protected is disabled, all requests pass automatically. Otherwise, the function requires both header presence and exact string equality against the configured value.
The permissive_access_token_verify() function wraps this logic as a FastAPI dependency, extracting the header automatically using type annotations:
def permissive_access_token_verify(
x_access_token: Annotated[str | None, Header(
description="Access token set in configuration (if access_protected is enabled)")]=None) -> bool:
return verify_access_token(x_access_token)
Finally, force_access_token_verify() converts the boolean result into an enforced barrier that interrupts request processing:
def force_access_token_verify(token_passed: Annotated[bool, Depends(permissive_access_token_verify)]):
if not token_passed:
raise HTTPException(status_code=401, detail="Access token is not present or invalid.")
Admin Token Verification Pipeline
Admin tokens follow an identical pattern but additionally check the admin_api_enable flag. The permissive_admin_token_verify() function requires both the flag to be true and header equality:
def permissive_admin_token_verify(
x_admin_token: Annotated[str | None, Header(
description="Admin token set in configuration (if admin_api_enable is enabled)")]=None) -> bool:
return config.admin_api_enable and x_admin_token == config.admin_token
The force_admin_token_verify() dependency raises HTTP 401 when the admin token check fails:
def force_admin_token_verify(token_passed: Annotated[bool, Depends(permissive_admin_token_verify)]):
if not token_passed:
raise HTTPException(status_code=401, detail="Admin token is not present or invalid.")
Route-Level Enforcement in FastAPI Routers
Protected endpoints declare these force functions as router dependencies. The system applies verification before any business logic executes, leveraging FastAPI's dependency injection system.
In app/Controllers/search.py, the search router conditionally applies access protection based on the configuration flag:
search_router = APIRouter(
dependencies=([Depends(force_access_token_verify)] if config.access_protected else None)
)
In app/Controllers/admin.py, the admin router unconditionally requires the admin token when the API is enabled:
admin_router = APIRouter(
dependencies=[Depends(force_admin_token_verify)], tags=["Admin"]
)
When a request arrives without valid headers, FastAPI automatically returns a 401 Unauthorized response before the endpoint handler executes.
Practical Implementation Examples
Creating a Protected Custom Endpoint
To protect a new route, import the force verification function and declare it as a router dependency:
from fastapi import APIRouter, Depends
from app.Services.authentication import force_access_token_verify
router = APIRouter(
prefix="/private",
dependencies=[Depends(force_access_token_verify)],
)
@router.get("/data")
def get_private_data():
return {"msg": "You have a valid access token!"}
Testing Token Validation with HTTP Clients
Clients must include the correct header value configured in the environment:
import httpx
BASE = "http://localhost:8000"
headers = {"x-access-token": "secret123"}
resp = httpx.get(f"{BASE}/private/data", headers=headers)
print(resp.json()) # {"msg": "You have a valid access token!"}
Requests missing the header or providing incorrect values receive HTTP 401 errors immediately.
Enabling Protection via Environment Variables
Configure the application at runtime using environment variables before starting Uvicorn:
export APP_ACCESS_PROTECTED=true
export APP_ACCESS_TOKEN=secret123
export APP_ADMIN_API_ENABLE=true
export APP_ADMIN_TOKEN=admin987
uvicorn app.webapp:app
With these settings, search endpoints require x-access-token: secret123 and admin endpoints require x-admin-token: admin987.
Summary
- NekoImageGallery stores authentication settings in
app/config.pywith boolean enablement flags and plaintext token values that initialize from environment variables. - The
app/Services/authentication.pymodule providesverify_access_token()andpermissive_*functions that compare incoming headers against configuration using exact string equality. force_access_token_verify()andforce_admin_token_verify()raiseHTTPExceptionwith status 401 when validation fails, blocking access to protected routes.- Routers in
app/Controllers/search.pyandapp/Controllers/admin.pydeclare these dependencies to enforce protection at the route level before handlers execute. - The home endpoint in
app/webapp.pyuses permissive checks to report authentication status without blocking requests, enabling health checks while confirming token recognition.
Frequently Asked Questions
What happens when access_protected is set to false?
When access_protected is disabled in app/config.py, the verify_access_token() function immediately returns True for all requests regardless of headers, and the search router skips the dependency injection entirely. This allows completely open access to the image search API without requiring authentication headers.
Can the authentication system handle multiple valid tokens or rotating credentials?
The current implementation in app/Services/authentication.py only supports single static tokens via direct string equality comparison (token == config.access_token). The system does not implement token lists, expiration dates, or automatic rotation; changing credentials requires updating the APP_ACCESS_TOKEN or APP_ADMIN_TOKEN environment variables and restarting the application.
How does the system differentiate between regular users and administrators?
Regular users authenticate via the x-access-token header validated by force_access_token_verify(), while administrators use the separate x-admin-token header processed by force_admin_token_verify(). The admin verification additionally requires the admin_api_enable flag to be true, and the two token systems operate independently with different configuration values, allowing separate protection levels for public search and administrative functions.
Why does the home endpoint use permissive verification instead of force verification?
The root route (/) in app/webapp.py intentionally uses permissive_access_token_verify and permissive_admin_token_verify to allow unauthenticated status checks while reporting whether valid tokens were present in the request. This design enables monitoring tools and status pages to test connectivity without credentials, while still confirming that the authentication system correctly recognizes valid tokens when they are provided.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →