Authentication and the JWT Token
Octostar apps authenticate via a JWT token injected by the platform when the app is launched in the iframe. The token must be forwarded to all backend API calls via an Authorization: Bearer header.
Note: Tokens expire hourly. In local dev, refresh your token from the Octostar UI under Profile → Developer Tokens → Out Token when calls start returning 401
Frontend: Getting the Token
The token is initialized once at startup in StateProvider.tsx, before any child component renders. It is sourced from two places depending on the environment:
1. Octostar context — production (running inside the platform iframe)
2. VITE_OS_JWT env — local dev / standalone mode
// state/StateProvider.tsx
const { ContextAPI } = useOctostarContext()
useEffect(() => {
// Standalone / local dev: read token from VITE_OS_JWT
if (!withOctostar || isStandalone() || !ContextAPI) {
api.setTokenFromEnv()
return
}
// Production: get token from the Octostar shell
ContextAPI.getContext()
.then((ctx) => { if (ctx?.token) api.setToken(ctx.token) })
.catch(() => api.setTokenFromEnv()) // fallback on error
}, [withOctostar, ContextAPI])
The API service stores the token in memory and attaches it to every request. sessionStorage and localStorage are not available inside Octostar iframes, so the token must live in-process for the lifetime of the page.
// services/api.ts
class ApiService {
private token: string | null = null
setToken(token: string) { this.token = token }
setTokenFromEnv() {
const t = import.meta.env.VITE_OS_JWT
if (t) this.token = t
}
async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
const headers = {
'Content-Type': 'application/json',
...(this.token && { Authorization: `Bearer ${this.token}` }),
...options.headers,
}
const res = await fetch(`./api${endpoint}`, { ...options, headers })
if (!res.ok) throw new Error((await res.json()).detail ?? `HTTP ${res.status}`)
return res.json()
}
}
export const api = new ApiService()
Backend: Extracting the Token
The minimal template (backend/main.py) does not include JWT extraction. For a full app, the recommended pattern is a FastAPI dependency:
# dependencies.py (add this to your backend)
from fastapi import Header, HTTPException, Depends
from typing import Optional
def get_token(
authorization: Optional[str] = Header(None, alias="Authorization")
) -> str:
"""Extract JWT from the Authorization header."""
if authorization and authorization.startswith("Bearer "):
return authorization[7:].strip()
# Dev/standalone fallback
import os
token = os.getenv("OS_JWT")
if token:
return token
raise HTTPException(status_code=401, detail="Missing Authorization header")
def get_client(token: str = Depends(get_token)):
"""Create an authenticated OctostarClient for this request."""
from octostar.client import OctostarClient
return OctostarClient(
token=token,
api_endpoint=os.getenv("OS_API_ENDPOINT"),
ontology=os.getenv("OS_ONTOLOGY"),
)
OctostarClient is not in requirements.txt — it comes pre-installed in the base Docker image.
Environment Variables
The project uses two separate .env files — one for the backend, one for the Vite dev server:
.env (backend, project root):
OS_API_ENDPOINT=https://your-octostar-instance.com
OS_ONTOLOGY=os_ontology_v1
OS_USER=your_username_here
OS_JWT=your_jwt_token_here # dev/standalone only — platform injects it in production
OS_STANDALONE=true # set to true for local dev (skips platform auth)
frontend/.env (frontend, Vite dev server only):
VITE_OS_JWT=your_jwt_token_here # same token as OS_JWT above
VITE_OS_STANDALONE=true # skips Octostar cont