Gateway sign-in testCube Community Gateway · test-auth

Signed out

01 /Identity

Nobody signed in on this request: the Gateway sent no identity.

Sign inSign outMembers only page

TypeScript

Network › Settings › Gateway › Sign-in: set up sign-in,
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'

02 /Load balancing

Ten calls to /api/whoami through the Gateway.

Call it 10 times

TypeScript

Network › Settings › Gateway › Backends: api → api-1:80, api-2:80,
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

MethodGET
Path/sitemap.xml
From10.60.8.10 (the Gateway)

Every header received

Accept*/*
Accept-Encodinggzip, br, zstd, deflate
Hosttestauthgateway.projects.cube.community
User-AgentMozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com)
X-Cc-Signed-Infalse
X-Forwarded-For216.73.217.0
X-Forwarded-Hosttestauthgateway.projects.cube.community
X-Forwarded-Port443
X-Forwarded-Protohttps
X-Forwarded-Serverprojects-proxy
X-Real-Ip216.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.