references/authentication.md
1,336 tokens · o200k_base · 5,035 bytes
Authentication Patterns
Login flows, session persistence, OAuth, and 2FA for cmux browser surfaces. Related: session-management.md, ../SKILL.md.
Set SURFACE from surface discovery or from the JSON
returned by browser open. Never guess a default surface or log credentials.
Saved browser state contains cookies and storage. Use a private directory with restrictive permissions before saving it:
STATE_DIR="${XDG_RUNTIME_DIR:-${TMPDIR:-/tmp}}/cmux-browser-state"
umask 077
mkdir -p "$STATE_DIR"
chmod 700 "$STATE_DIR"
STATE_FILE="$STATE_DIR/auth-state.json"
Basic login
OPEN_JSON="$(cmux --json browser open https://app.example.com/login --focus false)"
SURFACE="$(printf '%s' "$OPEN_JSON" | jq -r '.surface_ref // .surface_id // empty')"
[ -n "$SURFACE" ] || { printf '%s\n' 'browser open did not return a surface ref' >&2; exit 1; }
cmux browser --surface "$SURFACE" wait --load-state complete --timeout-ms 15000
cmux browser --surface "$SURFACE" snapshot --interactive
cmux browser --surface "$SURFACE" fill e1 "$APP_USERNAME"
cmux browser --surface "$SURFACE" fill e2 "$APP_PASSWORD"
cmux browser --surface "$SURFACE" click e3 --snapshot-after --json
cmux browser --surface "$SURFACE" wait --url-contains "/dashboard" --timeout-ms 20000
Saving authentication state
cmux browser --surface "$SURFACE" state save "$STATE_FILE"
chmod 600 "$STATE_FILE"
State includes cookies, localStorage, sessionStorage, and open tab metadata for that surface.
Restoring authentication
STATE_DIR="${XDG_RUNTIME_DIR:-${TMPDIR:-/tmp}}/cmux-browser-state"
umask 077
mkdir -p "$STATE_DIR"
chmod 700 "$STATE_DIR"
STATE_FILE="$STATE_DIR/auth-state.json"
OPEN_JSON="$(cmux --json browser open https://app.example.com --focus false)"
SURFACE="$(printf '%s' "$OPEN_JSON" | jq -r '.surface_ref // .surface_id // empty')"
[ -n "$SURFACE" ] || { printf '%s\n' 'browser open did not return a surface ref' >&2; exit 1; }
cmux browser --surface "$SURFACE" state load "$STATE_FILE"
cmux browser --surface "$SURFACE" goto https://app.example.com/dashboard
cmux browser --surface "$SURFACE" snapshot --interactive
OAuth / SSO
Same shape as basic login, waiting on the provider host and then the return host, with generous timeouts:
OAUTH_STATE_DIR="${XDG_RUNTIME_DIR:-${TMPDIR:-/tmp}}/cmux-browser-state"
umask 077
mkdir -p "$OAUTH_STATE_DIR"
chmod 700 "$OAUTH_STATE_DIR"
OAUTH_STATE_FILE="$OAUTH_STATE_DIR/oauth-state.json"
OPEN_JSON="$(cmux --json browser open https://app.example.com/auth/provider --focus false)"
SURFACE="$(printf '%s' "$OPEN_JSON" | jq -r '.surface_ref // .surface_id // empty')"
[ -n "$SURFACE" ] || { printf '%s\n' 'browser open did not return a surface ref' >&2; exit 1; }
cmux browser --surface "$SURFACE" wait --url-contains "login.example.com" --timeout-ms 30000
cmux browser --surface "$SURFACE" snapshot --interactive
# fill and click the provider's fields
cmux browser --surface "$SURFACE" wait --url-contains "app.example.com" --timeout-ms 45000
cmux browser --surface "$SURFACE" state save "$OAUTH_STATE_FILE"
chmod 600 "$OAUTH_STATE_FILE"
Two-factor
Drive the password step, let the user complete 2FA in the webview, then wait with a long timeout (--url-contains "/dashboard" --timeout-ms 120000) and save state.
Cookie-based auth
cmux browser --surface "$SURFACE" cookies set session_cookie "$SESSION_COOKIE"
cmux browser --surface "$SURFACE" goto https://app.example.com/dashboard
Token refresh
Load saved state, navigate, and re-login only when the URL bounced to /login:
#!/usr/bin/env bash
set -euo pipefail
STATE_DIR="${XDG_RUNTIME_DIR:-${TMPDIR:-/tmp}}/cmux-browser-state"
umask 077
mkdir -p "$STATE_DIR"
chmod 700 "$STATE_DIR"
STATE_FILE="${STATE_FILE:-$STATE_DIR/auth-state.json}"
: "${SURFACE:?set SURFACE from browser open or surface discovery}"
[ -f "$STATE_FILE" ] && cmux browser --surface "$SURFACE" state load "$STATE_FILE"
cmux browser --surface "$SURFACE" goto https://app.example.com/dashboard
if cmux browser --surface "$SURFACE" get url | grep -q '/login'; then
cmux browser --surface "$SURFACE" snapshot --interactive
cmux browser --surface "$SURFACE" fill e1 "$APP_USERNAME"
cmux browser --surface "$SURFACE" fill e2 "$APP_PASSWORD"
cmux browser --surface "$SURFACE" click e3
cmux browser --surface "$SURFACE" wait --url-contains "/dashboard" --timeout-ms 20000
cmux browser --surface "$SURFACE" state save "$STATE_FILE"
chmod 600 "$STATE_FILE"
fi
Security
Never commit state files; they contain auth tokens. Take credentials from environment variables. Clear state after sensitive tasks:
cmux browser --surface "$SURFACE" cookies clear --all
STATE_DIR="${XDG_RUNTIME_DIR:-${TMPDIR:-/tmp}}/cmux-browser-state"
STATE_FILE="${STATE_FILE:-$STATE_DIR/auth-state.json}"
OAUTH_STATE_FILE="${OAUTH_STATE_FILE:-$STATE_DIR/oauth-state.json}"
rm -f "$STATE_FILE"
rm -f "$OAUTH_STATE_FILE"