Freestyle Docs

Freestyle / Docs

Forward Auth

Authorize public HTTP requests at the Freestyle edge before they reach a VM.

Forward auth lets your application decide whether each public HTTP request may reach a VM. Freestyle terminates HTTPS, asks your authorization endpoint, and forwards only allowed requests.

Protect A Public Domain

Create one reusable forwardAuth configuration, then attach it to a public HTTP TLS rule:

const auth = await freestyle.tls.forwardAuth.create({
  url: "https://auth.acme.com/freestyle/check",
  // Authenticates Freestyle to your authorization service. Values are
  // write-only and read back as "***".
  headers: { authorization: "Bearer service-to-service-secret" },
  timeoutMs: 1500,
  // Copy identity established by the authorizer into the VM request.
  authResponseHeaders: ["x-acme-user-id", "x-acme-team-id"],
  // These browser cookies reach the authorizer, but never the untrusted VM.
  protectedCookies: ["__Host-acme-session", "__Host-acme-login"],
});

await freestyle.tls.rules.create({
  action: "allow",
  domain: "preview.acme.com",
  source: { public: true },
  destination: { vmId: "vm-123456", port: 3000 },
  forwardAuth: { id: auth.id },
});

forwardAuth is supported only on terminated HTTP public ingress rules: { public: true } to one VM port. One configuration may protect many rules.

Authorization Request And Response

The authorization URL must be a publicly reachable HTTPS endpoint. For each browser request, Freestyle sends it a bodyless GET with the caller’s Authorization and Cookie headers, the configured static headers, and trusted request metadata in X-Forwarded-Method, X-Forwarded-Proto, X-Forwarded-Host, X-Forwarded-Uri, and X-Forwarded-For, and the id of the matched rule in X-Freestyle-TLS-Rule-Id.

The response controls the request:

  • A 2xx allows it. Headers named by authResponseHeaders replace the caller’s values on the request sent to the VM.
  • A 3xx or 4xx is returned to the browser, including its response body and end-to-end headers. Freestyle does not follow redirects, so the authorizer can redirect a signed-out user to a login flow.
  • A timeout, network or DNS failure, invalid response, or 5xx fails closed; Freestyle returns 503 and does not send the request to the VM.

Every cookie named by protectedCookies remains visible to the authorizer but is removed before the request reaches the VM. The VM also cannot set, replace, or clear one of those cookies. On an allowed request, matching Set-Cookie headers from the authorizer are preserved in the response to the browser.

Read, Update, And Delete Configurations

Get, list, replace, and delete configurations through the same namespace:

const saved = await freestyle.tls.forwardAuth.get(auth.id);
const { configs, totalCount } = await freestyle.tls.forwardAuth.list();

await freestyle.tls.forwardAuth.update(auth.id, {
  url: saved.url,
  headers: { authorization: "Bearer rotated-service-secret" },
  timeoutMs: saved.timeoutMs,
  authResponseHeaders: saved.authResponseHeaders,
  protectedCookies: saved.protectedCookies,
});

// Remove or update every referring TLS rule before deleting the configuration.
await freestyle.tls.forwardAuth.delete(auth.id);

update replaces the complete configuration. Because static header values are write-only, supply them again on every update.

Behind A Reverse Proxy

X-Freestyle-TLS-Rule-Id carries the id of the TLS rule that matched the request. It is the one piece of request identity to trust when your authorizer decides which domain and destination a request is for, because it maps one-to-one to the rule you created and Freestyle stamps it on every check.

Do not key that decision on X-Forwarded-Host. When your authorization endpoint sits behind a reverse proxy — Vercel, Cloudflare, an API gateway, nginx — that proxy terminates the request and commonly overwrites the X-Forwarded-* headers, X-Forwarded-Host included, with its own values before your handler runs. The host you read is then the proxy’s, and every protected domain collapses to the same value, so a lookup keyed on it matches nothing. X-Freestyle-TLS-Rule-Id is not a standard forwarding header, so proxies pass it through untouched.

So resolve the protected domain and its policy by the rule id, and treat X-Forwarded-Host as an untrusted hint at most. If you need the caller’s original host, record it against the rule id when you create the rule and read it back from the rule id on each check, rather than trusting a forwarding header a proxy may have rewritten.

esc