Gateway sign-in testCube Community Gateway · test-auth
Signed out01 /Identity
Nobody signed in on this request: the Gateway sent no identity.
Sign inSign outMembers only pageTypeScript
Network › Settings › Gateway › Sign-in: set up sign-in,
then in Routing: path / → Sign-in Optional,
path /members → Sign-in Required.
then in Routing: path / → Sign-in Optional,
path /members → Sign-in Required.
// Behind the Gateway, who is signed in comes in headers // the proxy sets on every request (never the visitor's). export function viewerOf(request: Request) { if (request.headers.get('x-cc-signed-in') !== 'true') return null return { // The same on every visit: key your own data on it. id: request.headers.get('x-cc-user-id')!, name: request.headers.get('x-cc-username') ?? '', avatar: request.headers.get('x-cc-avatar'), } } // Sign-in and sign-out are the Gateway's own paths. export const signInUrl = (back: string) => `/.cc-auth/login?redirect_uri=${encodeURIComponent(back)}` // Out of this site only: still signed in on cube.community. export const signOutUrl = '/.cc-auth/logout'
TypeScript
Network › Settings › Gateway › Backends: api → api-1:80, api-2:80,
health check /health; Routing: path /api → api.
health check /health; Routing: path /api → api.
// api-1 and api-2 run the same server: the Gateway's // `api` backend sends each request to one in turn. import { hostname } from 'node:os' Bun.serve({ port: 80, fetch(request) { const { pathname } = new URL(request.url) // The backend's health check, asked every 15 seconds: // a target not answering 2xx or 3xx gets no request. if (pathname === '/health') return Response.json({ status: 'ok' }) return Response.json({ server: hostname(), path: pathname }) }, }) // In the page: same origin, no CORS, sign-in included. const answer = await (await fetch('/api/whoami')).json()
03 /Claims
None: sign in to see them.
TypeScript
// All of it as one JSON object, with what the sign-in // asks for besides (Sign-in › Claims), allowed by the // visitor on their next sign-in. type Claims = { sub: string name?: string preferred_username?: string picture?: string linked_accounts?: unknown[] game_account?: unknown discord_id?: string } export function claimsOf(request: Request): Claims | null { const raw = request.headers.get('x-cc-claims') return raw ? JSON.parse(raw) : null }
04 /Request
| Method | GET |
| Path | /sitemap.xml |
| From | 10.60.8.10 (the Gateway) |
Every header received
| Accept | */* |
| Accept-Encoding | gzip, br, zstd, deflate |
| Host | testauthgateway.projects.cube.community |
| User-Agent | Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com) |
| X-Cc-Signed-In | false |
| X-Forwarded-For | 216.73.217.0 |
| X-Forwarded-Host | testauthgateway.projects.cube.community |
| X-Forwarded-Port | 443 |
| X-Forwarded-Proto | https |
| X-Forwarded-Server | projects-proxy |
| X-Real-Ip | 216.73.217.0 |
TypeScript
// The proxy forwards over HTTP and says where the request // came from in the usual headers. export function originOf(request: Request) { return { ip: request.headers.get('x-forwarded-for')?.split(',')[0]?.trim(), host: request.headers.get('x-forwarded-host'), proto: request.headers.get('x-forwarded-proto'), // https } } // Trust X-CC-* only on requests coming through the Gateway: // it strips any a visitor sends, look-alikes included.