Barazo AppView backend barazo.forum
at main 326 lines 12 kB view raw
1#!/usr/bin/env bash 2# --------------------------------------------------------------------------- 3# test-oauth.sh -- Manual OAuth verification for Barazo API (M3 Checkpoint) 4# 5# This script starts the API (with dependencies) and walks you through 6# testing the full AT Protocol OAuth flow against bsky.social. 7# 8# Prerequisites: 9# - Docker (for PostgreSQL + Valkey) 10# - Node.js 24+, pnpm 11# - A Bluesky account (handle + app password or browser login) 12# 13# Usage: 14# chmod +x scripts/test-oauth.sh 15# ./scripts/test-oauth.sh [handle] 16# 17# handle: Your Bluesky handle (e.g. jay.bsky.team). If omitted, 18# the script will prompt for it. 19# --------------------------------------------------------------------------- 20set -euo pipefail 21 22API_DIR="$(cd "$(dirname "$0")/.." && pwd)" 23API_PORT="${PORT:-3000}" 24API_BASE="http://localhost:${API_PORT}" 25PG_CONTAINER="barazo-test-pg" 26VK_CONTAINER="barazo-test-valkey" 27 28# Colors (if terminal supports them) 29if [ -t 1 ]; then 30 BOLD="\033[1m" 31 GREEN="\033[0;32m" 32 YELLOW="\033[0;33m" 33 RED="\033[0;31m" 34 CYAN="\033[0;36m" 35 RESET="\033[0m" 36else 37 BOLD="" GREEN="" YELLOW="" RED="" CYAN="" RESET="" 38fi 39 40info() { echo -e "${GREEN}[INFO]${RESET} $*"; } 41warn() { echo -e "${YELLOW}[WARN]${RESET} $*"; } 42error() { echo -e "${RED}[ERROR]${RESET} $*"; } 43step() { echo -e "\n${BOLD}${CYAN}>> $*${RESET}"; } 44 45cleanup() { 46 step "Cleaning up" 47 if [ -n "${API_PID:-}" ] && kill -0 "$API_PID" 2>/dev/null; then 48 info "Stopping API (PID $API_PID)" 49 kill "$API_PID" 2>/dev/null || true 50 wait "$API_PID" 2>/dev/null || true 51 fi 52 # Leave Docker containers running so you can re-run quickly. 53 # To stop them: docker stop barazo-test-pg barazo-test-valkey 54 info "Docker containers left running for quick re-runs." 55 info "To stop: docker stop $PG_CONTAINER $VK_CONTAINER" 56} 57trap cleanup EXIT 58 59# --------------------------------------------------------------------------- 60# 1. Check prerequisites 61# --------------------------------------------------------------------------- 62step "Checking prerequisites" 63 64command -v docker >/dev/null 2>&1 || { error "Docker not found. Install Docker first."; exit 1; } 65command -v node >/dev/null 2>&1 || { error "Node.js not found. Install Node.js 24+."; exit 1; } 66command -v pnpm >/dev/null 2>&1 || { error "pnpm not found. Install: npm i -g pnpm"; exit 1; } 67 68NODE_MAJOR=$(node -v | sed 's/v//' | cut -d. -f1) 69if [ "$NODE_MAJOR" -lt 24 ]; then 70 warn "Node.js $NODE_MAJOR detected; Node.js 24+ recommended." 71fi 72 73info "All prerequisites met." 74 75# --------------------------------------------------------------------------- 76# 2. Start PostgreSQL + Valkey via Docker (if not already running) 77# --------------------------------------------------------------------------- 78step "Starting Docker dependencies" 79 80if docker ps --format '{{.Names}}' | grep -q "^${PG_CONTAINER}$"; then 81 info "PostgreSQL container already running." 82else 83 info "Starting PostgreSQL 16 + pgvector..." 84 docker run -d --name "$PG_CONTAINER" \ 85 -e POSTGRES_USER=barazo \ 86 -e POSTGRES_PASSWORD=barazo_dev \ 87 -e POSTGRES_DB=barazo \ 88 -p 5432:5432 \ 89 pgvector/pgvector:pg16 >/dev/null 2>&1 || { 90 # Container might exist but be stopped 91 docker start "$PG_CONTAINER" >/dev/null 2>&1 92 } 93fi 94 95if docker ps --format '{{.Names}}' | grep -q "^${VK_CONTAINER}$"; then 96 info "Valkey container already running." 97else 98 info "Starting Valkey..." 99 docker run -d --name "$VK_CONTAINER" \ 100 -p 6379:6379 \ 101 valkey/valkey:8 >/dev/null 2>&1 || { 102 docker start "$VK_CONTAINER" >/dev/null 2>&1 103 } 104fi 105 106# Wait for PostgreSQL to accept connections 107info "Waiting for PostgreSQL to be ready..." 108for i in $(seq 1 30); do 109 if docker exec "$PG_CONTAINER" pg_isready -U barazo >/dev/null 2>&1; then 110 break 111 fi 112 if [ "$i" -eq 30 ]; then 113 error "PostgreSQL did not become ready in time." 114 exit 1 115 fi 116 sleep 1 117done 118info "PostgreSQL ready." 119 120# --------------------------------------------------------------------------- 121# 3. Install dependencies + run migrations 122# --------------------------------------------------------------------------- 123step "Installing dependencies and running migrations" 124 125cd "$API_DIR" 126pnpm install --frozen-lockfile 2>/dev/null || pnpm install 127 128# Create .env if missing 129if [ ! -f .env ]; then 130 info "Creating .env from .env.example..." 131 cp .env.example .env 132 # Set a real session secret for testing 133 if [[ "$OSTYPE" == "darwin"* ]]; then 134 sed -i '' 's/your-session-secret-minimum-32-characters-long/barazo-dev-session-secret-for-local-testing-only/' .env 135 else 136 sed -i 's/your-session-secret-minimum-32-characters-long/barazo-dev-session-secret-for-local-testing-only/' .env 137 fi 138fi 139 140info "Running database migrations..." 141pnpm db:migrate 2>&1 || { 142 warn "Migration command failed. This may be expected if tables already exist." 143} 144 145# --------------------------------------------------------------------------- 146# 4. Start the API 147# --------------------------------------------------------------------------- 148step "Starting Barazo API on port ${API_PORT}" 149 150# Source .env for the API process 151set -a 152# shellcheck disable=SC1091 153source .env 154set +a 155 156pnpm dev & 157API_PID=$! 158 159# Wait for API to be healthy 160info "Waiting for API to respond..." 161for i in $(seq 1 30); do 162 if curl -sf "${API_BASE}/api/health" >/dev/null 2>&1; then 163 break 164 fi 165 if ! kill -0 "$API_PID" 2>/dev/null; then 166 error "API process exited unexpectedly. Check logs above." 167 exit 1 168 fi 169 if [ "$i" -eq 30 ]; then 170 error "API did not respond within 30 seconds." 171 exit 1 172 fi 173 sleep 1 174done 175info "API is running at ${API_BASE}" 176 177# --------------------------------------------------------------------------- 178# 5. Check setup status 179# --------------------------------------------------------------------------- 180step "Checking community setup status" 181 182SETUP_STATUS=$(curl -sf "${API_BASE}/api/setup/status") 183echo " Response: ${SETUP_STATUS}" 184 185# --------------------------------------------------------------------------- 186# 6. Initiate OAuth login 187# --------------------------------------------------------------------------- 188step "OAuth Login Flow" 189 190HANDLE="${1:-}" 191if [ -z "$HANDLE" ]; then 192 echo -en "\n ${BOLD}Enter your Bluesky handle${RESET} (e.g. jay.bsky.team): " 193 read -r HANDLE 194fi 195 196if [ -z "$HANDLE" ]; then 197 error "No handle provided. Exiting." 198 exit 1 199fi 200 201info "Initiating OAuth login for: ${HANDLE}" 202LOGIN_RESPONSE=$(curl -sf "${API_BASE}/api/auth/login?handle=${HANDLE}" 2>&1) || { 203 error "Login request failed. Response: ${LOGIN_RESPONSE:-empty}" 204 error "Make sure your handle is correct and the API can reach bsky.social." 205 exit 1 206} 207 208AUTH_URL=$(echo "$LOGIN_RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['url'])" 2>/dev/null || true) 209 210if [ -z "$AUTH_URL" ]; then 211 error "Could not extract authorization URL from response: ${LOGIN_RESPONSE}" 212 exit 1 213fi 214 215info "Authorization URL obtained." 216 217# Open in browser 218echo "" 219echo -e " ${BOLD}Opening your browser to authorize with Bluesky...${RESET}" 220echo "" 221echo " If the browser doesn't open, copy this URL manually:" 222echo -e " ${CYAN}${AUTH_URL}${RESET}" 223echo "" 224 225if [[ "$OSTYPE" == "darwin"* ]]; then 226 open "$AUTH_URL" 2>/dev/null || true 227elif command -v xdg-open >/dev/null 2>&1; then 228 xdg-open "$AUTH_URL" 2>/dev/null || true 229fi 230 231# --------------------------------------------------------------------------- 232# 7. Print verification checklist 233# --------------------------------------------------------------------------- 234echo "" 235echo -e "${BOLD}==========================================================================${RESET}" 236echo -e "${BOLD} M3 Authentication -- Manual Verification Checklist${RESET}" 237echo -e "${BOLD}==========================================================================${RESET}" 238echo "" 239echo -e " ${BOLD}Step 1: Authorize in browser${RESET}" 240echo " - Your browser should show the Bluesky authorization page." 241echo " - Log in / authorize the Barazo application." 242echo " - After authorizing, you'll be redirected to the callback URL." 243echo "" 244echo -e " ${BOLD}Step 2: Check the callback response${RESET}" 245echo " - The callback returns JSON with:" 246echo " { accessToken, expiresAt, did, handle }" 247echo " - Copy the accessToken value for the next steps." 248echo "" 249echo -e " If the callback shows a connection error (the frontend isn't running)," 250echo -e " copy the full callback URL from the browser address bar and run:" 251echo "" 252echo -e " ${CYAN}curl -v '\${CALLBACK_URL}'${RESET}" 253echo "" 254echo -e " ${BOLD}Step 3: Verify GET /api/auth/me${RESET}" 255echo " - Replace ACCESS_TOKEN with the token from step 2:" 256echo "" 257echo -e " ${CYAN}curl -s ${API_BASE}/api/auth/me \\\\${RESET}" 258echo -e " ${CYAN} -H 'Authorization: Bearer ACCESS_TOKEN' | python3 -m json.tool${RESET}" 259echo "" 260echo " - Expected: { \"did\": \"did:plc:...\", \"handle\": \"did:plc:...\" }" 261echo " (Handle currently shows DID -- this is a known TODO for M3.)" 262echo "" 263echo -e " ${BOLD}Step 4: Verify POST /api/auth/refresh${RESET}" 264echo " - The callback set an HTTP-only cookie. Use the cookie jar:" 265echo "" 266echo -e " ${CYAN}curl -s -X POST ${API_BASE}/api/auth/refresh \\\\${RESET}" 267echo -e " ${CYAN} -b 'barazo_refresh=SESSION_ID' | python3 -m json.tool${RESET}" 268echo "" 269echo " - Expected: { \"accessToken\": \"...\", \"expiresAt\": ... }" 270echo " - (You'll need to capture the cookie from the callback response" 271echo " headers to test this. Use curl -v on the callback to see it.)" 272echo "" 273echo -e " ${BOLD}Step 5: Verify setup wizard${RESET}" 274echo " - Check status (should be uninitialized):" 275echo "" 276echo -e " ${CYAN}curl -s ${API_BASE}/api/setup/status | python3 -m json.tool${RESET}" 277echo "" 278echo " - Initialize as first admin (requires access token from step 2):" 279echo "" 280echo -e " ${CYAN}curl -s -X POST ${API_BASE}/api/setup/initialize \\\\${RESET}" 281echo -e " ${CYAN} -H 'Authorization: Bearer ACCESS_TOKEN' \\\\${RESET}" 282echo -e " ${CYAN} -H 'Content-Type: application/json' \\\\${RESET}" 283echo -e " ${CYAN} -d '{\"communityName\": \"My Test Forum\"}' | python3 -m json.tool${RESET}" 284echo "" 285echo " - Expected: { \"initialized\": true, \"adminDid\": \"did:plc:...\"," 286echo " \"communityName\": \"My Test Forum\" }" 287echo "" 288echo " - Verify status is now initialized:" 289echo "" 290echo -e " ${CYAN}curl -s ${API_BASE}/api/setup/status | python3 -m json.tool${RESET}" 291echo "" 292echo " - Second initialize attempt should return 409:" 293echo "" 294echo -e " ${CYAN}curl -s -X POST ${API_BASE}/api/setup/initialize \\\\${RESET}" 295echo -e " ${CYAN} -H 'Authorization: Bearer ACCESS_TOKEN' \\\\${RESET}" 296echo -e " ${CYAN} -H 'Content-Type: application/json' \\\\${RESET}" 297echo -e " ${CYAN} -d '{}' | python3 -m json.tool${RESET}" 298echo "" 299echo " - Expected: { \"error\": \"Community already initialized\" }" 300echo "" 301echo -e " ${BOLD}Step 6: Verify logout${RESET}" 302echo "" 303echo -e " ${CYAN}curl -s -X DELETE ${API_BASE}/api/auth/session \\\\${RESET}" 304echo -e " ${CYAN} -b 'barazo_refresh=SESSION_ID' -w '\\nHTTP %{http_code}\\n'${RESET}" 305echo "" 306echo " - Expected: HTTP 204 (no content)" 307echo "" 308echo -e " ${BOLD}Step 7: Verify token is invalidated after logout${RESET}" 309echo "" 310echo -e " ${CYAN}curl -s ${API_BASE}/api/auth/me \\\\${RESET}" 311echo -e " ${CYAN} -H 'Authorization: Bearer ACCESS_TOKEN' | python3 -m json.tool${RESET}" 312echo "" 313echo " - Expected: { \"error\": \"Invalid or expired token\" }" 314echo "" 315echo -e "${BOLD}==========================================================================${RESET}" 316echo -e " ${GREEN}Pass criteria:${RESET} Steps 1-7 all return expected responses." 317echo -e " ${YELLOW}Known limitations:${RESET}" 318echo -e " - Handle shows DID (not resolved yet -- TODO for identity layer)" 319echo -e " - No Tap service required for auth testing (firehose is separate)" 320echo -e "${BOLD}==========================================================================${RESET}" 321echo "" 322echo " The API is still running. Press Ctrl+C when done testing." 323echo "" 324 325# Keep script alive until user terminates 326wait "$API_PID" 2>/dev/null || true