Auth — Authentication & Access Control

Gateway Security Layer

The Auth subsystem guards every connection entering the Gateway. It resolves auth modes from configuration, validates credentials across six methods, enforces rate limiting with sliding-window counters, manages device-token pairing, and applies install-time auth policies.

6
Auth Methods
4
Rate Limit Scopes
5
Core Files
26
Connect Params

Security-Critical TypeScript Rate Limiting Multi-Method

What Is This & Why Does It Exist?

Without authentication, any client could connect to the Gateway and execute arbitrary agent commands. The Auth subsystem ensures that every connection is verified before it reaches internal services.

💡 Real-world analogy

Think of Auth as a multi-layered building security system. The resolveGatewayAuth() function is the security policy that determines which type of badge (token, password, Tailscale, device-token) is required today. The AuthRateLimiter is the turnstile that locks after too many failed badge swipes. authorizeGatewayConnect() is the guard who checks your badge against the policy. auth-install-policy.ts is the building code that mandates what security must be installed before opening day.

Table of Contents

Architecture

The Auth layer is the first subsystem touched by every incoming connection. It coordinates between credential resolution, auth mode policy, rate limiting, and multiple authentication strategies.

flowchart TB
    subgraph Input
        REQ["Incoming Request
ConnectParams"] end subgraph AuthLayer["Auth Layer"] RESOLVE["resolveGatewayAuth()
Mode Resolution"] POLICY["auth-mode-policy.ts
Ambiguity Detection"] CRED["credentials.ts
Credential Resolution"] RL["AuthRateLimiter
Sliding-Window Counter"] subgraph Methods["Auth Methods"] NONE["none"] TOKEN["token"] PASS["password"] TAIL["tailscale"] DEV["device-token"] PROXY["trusted-proxy"] end end subgraph Output OK["GatewayAuthResult
{ok: true, method, user}"] FAIL["GatewayAuthResult
{ok: false, reason}"] end REQ --> RL RL -->|allowed| RESOLVE RL -->|locked out| FAIL RESOLVE --> POLICY POLICY --> CRED CRED --> Methods Methods --> OK Methods -->|failure| FAIL

Data Flow

Sequence of operations when authorizeGatewayConnect() processes an incoming connection request.

sequenceDiagram
    participant C as Client
    participant RL as AuthRateLimiter
    participant Resolve as resolveGatewayAuth
    participant Policy as AuthModePolicy
    participant Auth as authorizeGatewayConnect
    participant Cred as CredentialResolver

    C->>RL: check(clientIp, scope)
    alt rate limited
        RL-->>C: {allowed: false, retryAfterMs}
    end
    RL-->>Auth: {allowed: true, remaining}
    Auth->>Resolve: resolveGatewayAuth(cfg, overrides)
    Resolve->>Policy: assertExplicitGatewayAuthMode()
    Resolve-->>Auth: ResolvedGatewayAuth {mode, token, password}
    Auth->>Cred: resolveGatewayCredentialsFromConfig()
    Cred-->>Auth: {token?, password?}
    Auth->>Auth: evaluate method (none|token|password|tailscale|device-token|trusted-proxy)
    alt success
        Auth-->>C: {ok: true, method, user}
    else failure
        Auth->>RL: recordFailure(clientIp, scope)
        Auth-->>C: {ok: false, reason}
    end

Key Classes & Types

NameFileDescription
ResolvedGatewayAuthModeauth.tsUnion: "none" | "token" | "password" | "trusted-proxy"
ResolvedGatewayAuthauth.tsResolved config: mode, modeSource, token, password, allowTailscale, trustedProxy
GatewayAuthResultauth.tsAuth outcome: ok, method, user, reason, rateLimited, retryAfterMs
GatewayAuthSurfaceauth.tsSurface type: "http" | "ws-control-ui"
AuthorizeGatewayConnectParamsauth.tsFull auth params (26 fields): auth, connectAuth, req, trustedProxies, rateLimiter, etc.
AuthRateLimiterauth-rate-limit.tsRate limiter interface: check(), recordFailure(), reset(), prune(), dispose()
RateLimitConfigauth-rate-limit.tsConfig: maxAttempts (10), windowMs (60s), lockoutMs (5min), exemptLoopback
RateLimitCheckResultauth-rate-limit.tsCheck result: allowed, remaining, retryAfterMs
RateLimitEntryauth-rate-limit.tsPer-IP state: attempts[], lockedUntil?
ResolvedGatewayCredentialscredentials.tsResolved credentials: {token?, password?}
GatewaySecretRefUnavailableErrorcredentials.tsError thrown when secret reference file is unavailable

Auth Methods

MethodIdentifierMechanismUse Case
noneNo authAll connections acceptedLocal development, trusted networks
tokenBearer tokenStatic token compared via authorizeTokenAuth()CLI automation, service-to-service
passwordPasswordPassword verified from config or envWeb UI access
tailscaleTailscale whoisresolveVerifiedTailscaleUser()Zero-trust mesh network
device-tokenDevice pairingPaired device token exchangeMobile apps, persistent devices
trusted-proxyProxy headerauthorizeTrustedProxy() + browser origin checkReverse proxy setups (nginx, Caddy)

Rate Limit Scopes

Scope ConstantValueDescription
AUTH_RATE_LIMIT_SCOPE_DEFAULT"default"General auth attempts
AUTH_RATE_LIMIT_SCOPE_SHARED_SECRET"shared-secret"Shared secret auth attempts
AUTH_RATE_LIMIT_SCOPE_DEVICE_TOKEN"device-token"Device token pairing attempts
AUTH_RATE_LIMIT_SCOPE_HOOK_AUTH"hook-auth"Hook authentication attempts

Code Examples

Resolving Auth Mode

import { resolveGatewayAuth, ResolvedGatewayAuth } from './auth';

const auth: ResolvedGatewayAuth = resolveGatewayAuth({
  config: openClawConfig,
  overrideMode: undefined,
  overrideToken: process.env.GATEWAY_TOKEN,
});
// auth.mode => "token" | "password" | "none" | "trusted-proxy"
// auth.modeSource => "config" | "override" | "default"

Creating a Rate Limiter

import { createAuthRateLimiter, RateLimitConfig } from './auth-rate-limit';

const config: RateLimitConfig = {
  maxAttempts: 10,
  windowMs: 60_000,
  lockoutMs: 300_000,
  exemptLoopback: true,
  pruneIntervalMs: 60_000,
};

const limiter = createAuthRateLimiter(config);
const result = limiter.check('192.168.1.100', 'default');
// result => { allowed: true, remaining: 10, retryAfterMs: 0 }

Authorizing a Connection

import { authorizeGatewayConnect, GatewayAuthResult } from './auth';

const result: GatewayAuthResult = await authorizeGatewayConnect({
  auth: resolvedAuth,
  connectAuth: { token: clientToken },
  req: incomingRequest,
  trustedProxies: ['10.0.0.0/8'],
  authSurface: 'ws-control-ui',
  rateLimiter: limiter,
  clientIp: '192.168.1.100',
  rateLimitScope: 'default',
  allowRealIpFallback: false,
  browserOriginPolicy: 'strict',
});

if (!result.ok) {
  console.error(`Auth failed: ${result.reason}`);
  if (result.rateLimited) {
    console.error(`Retry after ${result.retryAfterMs}ms`);
  }
}

Configuration

ParameterTypeDefaultDescription
auth.modeResolvedGatewayAuthMode"none"Active auth mode
auth.tokenstring—Bearer token value
auth.passwordstring—Password value
auth.allowTailscalebooleanfalseAccept Tailscale identity
auth.trustedProxyGatewayTrustedProxyConfig—Trusted proxy CIDR ranges
rateLimit.maxAttemptsnumber10Max failed attempts per window
rateLimit.windowMsnumber60000Sliding window duration
rateLimit.lockoutMsnumber300000Lockout duration (5 min)
rateLimit.exemptLoopbackbooleantrueSkip rate limiting for 127.0.0.1
rateLimit.pruneIntervalMsnumber60000Stale entry cleanup interval

Deep-Dive Focus Pages

🔗 Device Pairing Flow Focus

The device-token pairing protocol that enables persistent mobile & IoT device authentication without shared secrets.

⏱ Rate Limiting Focus

Sliding-window rate limiter with per-scope counters, configurable lockouts, and loopback exemptions.

📜 Install Policies Focus

Install-time and auth-mode policies that enforce security requirements during initial deployment.

Directory Map

Source files for Auth
src/gateway/
├── auth.ts                     ← Core: resolveGatewayAuth, authorizeGatewayConnect, 6 auth methods
├── auth-rate-limit.ts          ← AuthRateLimiter, createAuthRateLimiter, RateLimitConfig
├── auth-mode-policy.ts         ← hasAmbiguousGatewayAuthModeConfig, assertExplicitMode
├── auth-install-policy.ts      ← shouldRequireGatewayTokenForInstall
├── auth-config-utils.ts        ← withGatewayAuthPassword, resolveGatewayPasswordSecretRef
├── credentials.ts              ← ResolvedGatewayCredentials, GatewaySecretRefUnavailableError
├── connection-auth.ts          ← resolveGatewayConnectionAuth (async credential resolution)
└── connection-details.ts       ← GatewayConnectionDetails