Routing Overview
This page summarizes the HTTP routes generated by the ActingWeb library when integrating with Flask or FastAPI, and how base paths are handled.
Integration
# Flask
from flask import Flask
from actingweb.interface import ActingWebApp
app = Flask(__name__)
ActingWebApp(...).with_web_ui().integrate_flask(app)
# FastAPI
from fastapi import FastAPI
api = FastAPI()
ActingWebApp(...).integrate_fastapi(api, templates_dir="templates")
Core Actor Routes
/: Root factory (create actor via web or API)
/<actor_id>: Actor root endpoint (see Actor Root Endpoint Behavior)
/<actor_id>/meta: Discovery metadata
/<actor_id>/properties: Get/update/delete properties
/<actor_id>/resources: Fetch external resources (read-only)
/<actor_id>/methods: RPC-style methods (GET returns metadata, POST invokes)
/<actor_id>/actions: Side-effecting actions (GET returns metadata, POST invokes)
/<actor_id>/trust: Trust relationships and permissions
/<actor_id>/subscriptions: Subscriptions to peer updates
/<actor_id>/callbacks: Inbound callbacks (peer/webhooks)
Web UI Routes (when with_web_ui(True))
/<actor_id>/www: Dashboard
/<actor_id>/www/properties and /<actor_id>/www/properties/<name>
/<actor_id>/www/trust and /<actor_id>/www/trust/new
/<actor_id>/www/init: Initialization helpers
Authentication
/oauth: OAuth2 callback and session handling (when configured via with_oauth)
Web UI and protected routes enforce authentication according to configuration (basic or OAuth2)
Base Paths
Deployments under a base path (e.g., /my-server) are supported by both integrations.
Templates receive actor_root and actor_www to generate correct links regardless of base path.
Avoid relative links in templates; prefer the provided variables.
Actor Root Endpoint Behavior
The actor root endpoint (GET /<actor_id>) supports content negotiation based on
the Accept header and authentication status.
API Clients
API clients (sending Accept: application/json or no specific Accept header) receive
JSON with actor information:
{
"id": "actor-id",
"creator": "user@example.com",
"passphrase": "actor-passphrase",
"trustee_root": "optional-trustee-root"
}
If not authenticated, API clients receive a 401 or 403 error response.
Browser Redirect Behavior
Browsers (sending Accept: text/html) are redirected based on authentication status
and the with_web_ui() configuration:
Authenticated |
|
Redirect Target |
Use Case |
|---|---|---|---|
No |
Any |
|
Consistent login experience |
Yes |
|
|
Traditional server-rendered UI |
Yes |
|
|
Single Page Applications (SPAs) |
Key behaviors:
Unauthenticated browsers always redirect to
/login, providing a consistent login experience rather than triggering OAuth directly.Authenticated browsers redirect to the appropriate UI endpoint based on whether the traditional web UI (
/www) or SPA mode (/app) is configured.
Note
“Authenticated” for the browser UI means an OAuth2 browser session. HTTP
Basic auth (creator + passphrase) authenticates the JSON API
(/properties, /trust, /actions, …) but is not a browser
session: a Basic-auth request to /www is still redirected to /login /
OAuth. To view the web UI you must complete the OAuth flow, so a working
OAuth provider must be configured (with_oauth(...)).
OAuth Callback Redirect Behavior
After successful OAuth authentication, the callback handler redirects users to their
UI endpoint based on the same with_web_ui() configuration:
|
Redirect Target |
Use Case |
|---|---|---|
|
|
Traditional web UI with server templates |
|
|
SPA that handles its own routing |
This ensures users land on the correct UI after logging in, without applications needing to override OAuth callback behavior.
SPA Route Requirements
When using with_web_ui(False) for SPA mode, your application must provide:
``/login`` - Login page with OAuth buttons (served by your SPA)
``/<actor_id>/app`` - Main SPA entry point for authenticated users
Example SPA route registration (FastAPI):
@app.get("/login", response_class=HTMLResponse)
async def login_page(request: Request):
return HTMLResponse(content=render_spa_shell())
@app.get("/{actor_id}/app", response_class=HTMLResponse)
@app.get("/{actor_id}/app/{path:path}", response_class=HTMLResponse)
async def spa_app(actor_id: str, request: Request, path: str = ""):
return HTMLResponse(content=render_spa_shell(actor_id=actor_id))
The SPA shell is served unconditionally - the JavaScript application handles
authentication state and redirects unauthenticated users to /login.
Configuration Examples
Traditional Web UI (server-rendered templates):
app = ActingWebApp(...)
.with_web_ui(enable=True)
.with_oauth(...)
# Browsers → /<actor_id>/www (ActingWeb provides templates)
# OAuth callback → /<actor_id>/www
Single Page Application:
app = ActingWebApp(...)
.with_web_ui(enable=False) # Disable server templates
.with_oauth(...)
# Browsers → /<actor_id>/app (your SPA handles rendering)
# OAuth callback → /<actor_id>/app
# You must provide /login and /<actor_id>/app routes
OAuth Provider Endpoints
GET /oauth/callback— standard callback for Google/GitHub (query-mode).POST /oauth/callback/apple— Sign in with Apple callback. Apple usesresponse_mode=form_post(a cross-site POST), protected by a server-side single-use state nonce. See Sign in with Apple.POST /oauth/spa/tokensupports thesegrant_typevalues:authorization_code— mobile code+PKCE exchange (existing). PKCE (code_verifier) is required for-mobile/-nativeproviders or custom-schemeredirect_urivalues.refresh_token— refresh with rotation (existing).urn:ietf:params:oauth:grant-type:jwt-bearer— nativeid_token(assertion+nonce+provider) exchange for an ActingWeb session.mobile_ticket— redeem an opaque deep-link ticket for a server-side code exchange (Apple-on-Android, GitHub mobile).apple_mobile_ticketis a backward-compatible alias.
MCP OAuth2 Server Endpoints
ActingWeb is the OAuth2 authorization server for MCP clients (enabled with
with_mcp()). Errors follow RFC 6749 §5.2 and RFC 7591 §3.2.2: the body is
{"error": <code>, "error_description": ...} with the real code.
POST /oauth/register— dynamic client registration (RFC 7591). 201 on success.token_endpoint_auth_methodmay benone(public client, noclient_secretin the response),client_secret_post(default) orclient_secret_basic(the response carriesclient_secretandclient_secret_expires_at: 0). Anything else, or aclient_namethat is empty after sanitising, is 400invalid_client_metadata.GET /oauth/authorize— shows the sign-in form;POST /oauth/authorize— the form submission, which redirects to the chosen provider.code_challengewithcode_challenge_method=S256binds the code;plainor a missing method is 400invalid_request, and a public client must send a challenge. Errors on this endpoint are pre-redirect validation failures.POST /oauth/token—authorization_code,refresh_tokenandclient_credentialsgrants. Client credentials in the body or anAuthorization: Basicheader (a bodyclient_idthat differs from the header’s isinvalid_request). Errors: 400invalid_request,invalid_grant(dead, expired, reused or foreign code or refresh token),unsupported_grant_type,unauthorized_client(a public client asking forclient_credentials),invalid_scope; 401invalid_clientwithWWW-Authenticate; 500server_error(including a token-store fault). Every refresh returns a newrefresh_token; the presented one is single-use./mcpanswers 401invalid_tokenwithout a valid bearer token and 503 withRetry-Afterwhen the token store cannot be read.
Notes
Optional MCP endpoints are controlled by with_mcp(enable=…); see Building MCP Applications with ActingWeb.
Development routes are available when with_devtest(True) is set.