477 lines
16 KiB
Bash
Executable File
477 lines
16 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# NIP-42 Authentication Test Script
|
|
# Tests the complete NIP-42 authentication flow for the C Nostr Relay
|
|
|
|
set -e
|
|
|
|
RELAY_URL="ws://localhost:8888"
|
|
HTTP_URL="http://localhost:8888"
|
|
TEST_DIR="$(dirname "$0")"
|
|
LOG_FILE="${TEST_DIR}/nip42_test.log"
|
|
|
|
# Colors for output
|
|
RED='\033[31m'
|
|
GREEN='\033[32m'
|
|
YELLOW='\033[33m'
|
|
BLUE='\033[34m'
|
|
BOLD='\033[1m'
|
|
RESET='\033[0m'
|
|
|
|
# Logging function
|
|
log() {
|
|
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
|
|
}
|
|
|
|
log_success() {
|
|
echo -e "${GREEN}${BOLD}[SUCCESS]${RESET} $1" | tee -a "$LOG_FILE"
|
|
}
|
|
|
|
log_error() {
|
|
echo -e "${RED}${BOLD}[ERROR]${RESET} $1" | tee -a "$LOG_FILE"
|
|
}
|
|
|
|
log_info() {
|
|
echo -e "${BLUE}${BOLD}[INFO]${RESET} $1" | tee -a "$LOG_FILE"
|
|
}
|
|
|
|
log_warning() {
|
|
echo -e "${YELLOW}${BOLD}[WARNING]${RESET} $1" | tee -a "$LOG_FILE"
|
|
}
|
|
|
|
# Initialize test log
|
|
echo "=== NIP-42 Authentication Test Started ===" > "$LOG_FILE"
|
|
log "Starting NIP-42 authentication tests"
|
|
|
|
# Check if required tools are available
|
|
check_dependencies() {
|
|
log_info "Checking dependencies..."
|
|
|
|
if ! command -v nak &> /dev/null; then
|
|
log_error "nak client not found. Please install: go install github.com/fiatjaf/nak@latest"
|
|
exit 1
|
|
fi
|
|
|
|
if ! command -v jq &> /dev/null; then
|
|
log_error "jq not found. Please install jq for JSON processing"
|
|
exit 1
|
|
fi
|
|
|
|
if ! command -v wscat &> /dev/null; then
|
|
log_warning "wscat not found. Some manual WebSocket tests will be skipped"
|
|
log_warning "Install with: npm install -g wscat"
|
|
fi
|
|
|
|
log_success "Dependencies check complete"
|
|
}
|
|
|
|
# Test 1: Check NIP-42 in supported NIPs
|
|
test_nip42_support() {
|
|
log_info "Test 1: Checking NIP-42 support in relay info"
|
|
|
|
local response
|
|
response=$(curl -s -H "Accept: application/nostr+json" "$HTTP_URL")
|
|
|
|
if echo "$response" | jq -e '.supported_nips | contains([42])' > /dev/null; then
|
|
log_success "NIP-42 is advertised in supported NIPs"
|
|
log "Supported NIPs: $(echo "$response" | jq -r '.supported_nips | @csv')"
|
|
return 0
|
|
else
|
|
log_error "NIP-42 not found in supported NIPs"
|
|
log "Response: $response"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Test 2: Check if relay responds with AUTH challenge when auth is required
|
|
test_auth_challenge_generation() {
|
|
log_info "Test 2: Testing AUTH challenge generation"
|
|
|
|
# First, enable NIP-42 authentication for events using configuration
|
|
local admin_privkey
|
|
admin_privkey=$(grep "Admin Private Key:" relay.log 2>/dev/null | tail -1 | cut -d' ' -f4 || echo "")
|
|
|
|
if [[ -z "$admin_privkey" ]]; then
|
|
log_warning "Could not extract admin private key from relay.log - using manual test approach"
|
|
log_info "Manual test: Connect to relay and send an event without auth to trigger challenge"
|
|
return 0
|
|
fi
|
|
|
|
log_info "Found admin private key, configuring NIP-42 authentication..."
|
|
|
|
# Create configuration event to enable NIP-42 auth for events
|
|
local config_event
|
|
# Get relay pubkey for d tag
|
|
local relay_pubkey
|
|
relay_pubkey=$(nak key --pub "$admin_privkey" 2>/dev/null || echo "")
|
|
|
|
if [[ -n "$relay_pubkey" ]]; then
|
|
config_event=$(nak event -k 33334 --content "C Nostr Relay Configuration" \
|
|
--tag "d,$relay_pubkey" \
|
|
--tag "nip42_auth_required_events,1" \
|
|
--tag "nip42_auth_required_subscriptions,0" \
|
|
--sec "$admin_privkey" 2>/dev/null || echo "")
|
|
else
|
|
config_event=""
|
|
fi
|
|
|
|
if [[ -n "$config_event" ]]; then
|
|
log_info "Publishing configuration to enable NIP-42 auth for events..."
|
|
echo "$config_event" | nak event "$RELAY_URL" 2>/dev/null || true
|
|
sleep 2 # Allow time for configuration to be processed
|
|
log_success "Configuration sent - NIP-42 auth should now be required for events"
|
|
else
|
|
log_warning "Failed to create configuration event - proceeding with manual test"
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# Test 3: Test authentication flow with nak
|
|
test_nip42_auth_flow() {
|
|
log_info "Test 3: Testing complete NIP-42 authentication flow"
|
|
|
|
# Generate test keypair
|
|
local test_privkey test_pubkey
|
|
test_privkey=$(nak key --gen 2>/dev/null || openssl rand -hex 32)
|
|
test_pubkey=$(nak key --pub "$test_privkey" 2>/dev/null || echo "test_pubkey")
|
|
|
|
log_info "Generated test keypair: $test_pubkey"
|
|
|
|
# Try to publish an event (should trigger auth challenge)
|
|
log_info "Attempting to publish event without authentication..."
|
|
|
|
local test_event
|
|
test_event=$(nak event -k 1 --content "NIP-42 test event - should require auth" \
|
|
--sec "$test_privkey" 2>/dev/null || echo "")
|
|
|
|
if [[ -n "$test_event" ]]; then
|
|
log_info "Publishing test event to relay..."
|
|
local result
|
|
result=$(echo "$test_event" | timeout 10s nak event "$RELAY_URL" 2>&1 || true)
|
|
|
|
log "Event publish result: $result"
|
|
|
|
# Check if we got an auth challenge or notice
|
|
if echo "$result" | grep -q "AUTH\|auth\|authentication"; then
|
|
log_success "Relay requested authentication as expected"
|
|
elif echo "$result" | grep -q "OK.*true"; then
|
|
log_warning "Event was accepted without authentication (auth may be disabled)"
|
|
else
|
|
log_warning "Unexpected response: $result"
|
|
fi
|
|
else
|
|
log_error "Failed to create test event"
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# Test 4: Test WebSocket AUTH message handling
|
|
test_websocket_auth_messages() {
|
|
log_info "Test 4: Testing WebSocket AUTH message handling"
|
|
|
|
if ! command -v wscat &> /dev/null; then
|
|
log_warning "Skipping WebSocket tests - wscat not available"
|
|
return 0
|
|
fi
|
|
|
|
log_info "Testing WebSocket connection and AUTH message..."
|
|
|
|
# Test WebSocket connection
|
|
local ws_test_file="/tmp/nip42_ws_test.json"
|
|
cat > "$ws_test_file" << 'EOF'
|
|
["EVENT",{"kind":1,"content":"Test message for auth","tags":[],"created_at":1234567890,"pubkey":"0000000000000000000000000000000000000000000000000000000000000000","id":"0000000000000000000000000000000000000000000000000000000000000000","sig":"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}]
|
|
EOF
|
|
|
|
log_info "Sending test message via WebSocket..."
|
|
timeout 5s wscat -c "$RELAY_URL" < "$ws_test_file" > /tmp/ws_response.log 2>&1 || true
|
|
|
|
if [[ -f /tmp/ws_response.log ]]; then
|
|
local ws_response
|
|
ws_response=$(cat /tmp/ws_response.log)
|
|
log "WebSocket response: $ws_response"
|
|
|
|
if echo "$ws_response" | grep -q "AUTH\|NOTICE.*auth"; then
|
|
log_success "WebSocket AUTH challenge detected"
|
|
else
|
|
log_info "No AUTH challenge in WebSocket response"
|
|
fi
|
|
|
|
rm -f /tmp/ws_response.log
|
|
fi
|
|
|
|
rm -f "$ws_test_file"
|
|
return 0
|
|
}
|
|
|
|
# Test 5: Configuration verification
|
|
test_nip42_configuration() {
|
|
log_info "Test 5: Testing NIP-42 configuration options"
|
|
|
|
# Check current configuration
|
|
log_info "Retrieving current relay configuration..."
|
|
|
|
local config_events
|
|
config_events=$(nak req -k 33334 "$RELAY_URL" 2>/dev/null | jq -s '.' || echo "[]")
|
|
|
|
if [[ "$config_events" != "[]" ]] && [[ -n "$config_events" ]]; then
|
|
log_success "Retrieved configuration events from relay"
|
|
|
|
# Check for NIP-42 related configuration
|
|
local nip42_config
|
|
nip42_config=$(echo "$config_events" | jq -r '.[].tags[]? | select(.[0] | startswith("nip42")) | join("=")' 2>/dev/null || echo "")
|
|
|
|
if [[ -n "$nip42_config" ]]; then
|
|
log_success "Found NIP-42 configuration:"
|
|
echo "$nip42_config" | while read -r line; do
|
|
log " $line"
|
|
done
|
|
else
|
|
log_info "No specific NIP-42 configuration found (may use defaults)"
|
|
fi
|
|
else
|
|
log_warning "Could not retrieve configuration events"
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# Test 6: Performance and stability test
|
|
test_nip42_performance() {
|
|
log_info "Test 6: Testing NIP-42 performance and stability"
|
|
|
|
local test_privkey test_pubkey
|
|
test_privkey=$(nak key --gen 2>/dev/null || openssl rand -hex 32)
|
|
test_pubkey=$(nak key --pub "$test_privkey" 2>/dev/null || echo "test_pubkey")
|
|
|
|
log_info "Testing multiple authentication attempts..."
|
|
|
|
local success_count=0
|
|
local total_attempts=5
|
|
|
|
for i in $(seq 1 $total_attempts); do
|
|
local test_event
|
|
test_event=$(nak event -k 1 --content "Performance test event $i" \
|
|
--sec "$test_privkey" 2>/dev/null || echo "")
|
|
|
|
if [[ -n "$test_event" ]]; then
|
|
local start_time end_time duration
|
|
start_time=$(date +%s.%N)
|
|
|
|
local result
|
|
result=$(echo "$test_event" | timeout 5s nak event "$RELAY_URL" 2>&1 || echo "timeout")
|
|
|
|
end_time=$(date +%s.%N)
|
|
duration=$(echo "$end_time - $start_time" | bc -l 2>/dev/null || echo "unknown")
|
|
|
|
log "Attempt $i: ${duration}s - $result"
|
|
|
|
if echo "$result" | grep -q "success\|OK.*true\|AUTH\|authentication"; then
|
|
((success_count++))
|
|
fi
|
|
fi
|
|
done
|
|
|
|
log_success "Performance test completed: $success_count/$total_attempts successful responses"
|
|
|
|
return 0
|
|
}
|
|
|
|
# Test 7: Kind-specific authentication requirements
|
|
test_nip42_kind_specific_auth() {
|
|
log_info "Test 7: Testing kind-specific NIP-42 authentication requirements"
|
|
|
|
# Generate test keypair
|
|
local test_privkey test_pubkey
|
|
test_privkey=$(nak key --gen 2>/dev/null || openssl rand -hex 32)
|
|
test_pubkey=$(nak key --pub "$test_privkey" 2>/dev/null || echo "test_pubkey")
|
|
|
|
log_info "Generated test keypair for kind-specific tests: $test_pubkey"
|
|
|
|
# Test 1: Try to publish a regular note (kind 1) - should work without auth
|
|
log_info "Testing kind 1 event (regular note) - should work without authentication..."
|
|
local kind1_event
|
|
kind1_event=$(nak event -k 1 --content "Regular note - should not require auth" \
|
|
--sec "$test_privkey" 2>/dev/null || echo "")
|
|
|
|
if [[ -n "$kind1_event" ]]; then
|
|
local result1
|
|
result1=$(echo "$kind1_event" | timeout 10s nak event "$RELAY_URL" 2>&1 || true)
|
|
log "Kind 1 event result: $result1"
|
|
|
|
if echo "$result1" | grep -q "OK.*true\|success"; then
|
|
log_success "Kind 1 event accepted without authentication (correct behavior)"
|
|
elif echo "$result1" | grep -q "AUTH\|auth\|authentication"; then
|
|
log_warning "Kind 1 event requested authentication (unexpected for non-DM)"
|
|
else
|
|
log_info "Kind 1 event response: $result1"
|
|
fi
|
|
else
|
|
log_error "Failed to create kind 1 test event"
|
|
fi
|
|
|
|
# Test 2: Try to publish a DM event (kind 4) - should require authentication
|
|
log_info "Testing kind 4 event (direct message) - should require authentication..."
|
|
local kind4_event
|
|
kind4_event=$(nak event -k 4 --content "This is a direct message - should require auth" \
|
|
--tag "p,$test_pubkey" \
|
|
--sec "$test_privkey" 2>/dev/null || echo "")
|
|
|
|
if [[ -n "$kind4_event" ]]; then
|
|
local result4
|
|
result4=$(echo "$kind4_event" | timeout 10s nak event "$RELAY_URL" 2>&1 || true)
|
|
log "Kind 4 event result: $result4"
|
|
|
|
if echo "$result4" | grep -q "AUTH\|auth\|authentication\|restricted"; then
|
|
log_success "Kind 4 event requested authentication (correct behavior for DMs)"
|
|
elif echo "$result4" | grep -q "OK.*true\|success"; then
|
|
log_warning "Kind 4 event accepted without authentication (should require auth for privacy)"
|
|
else
|
|
log_info "Kind 4 event response: $result4"
|
|
fi
|
|
else
|
|
log_error "Failed to create kind 4 test event"
|
|
fi
|
|
|
|
# Test 3: Try to publish a chat message (kind 14) - should require authentication
|
|
log_info "Testing kind 14 event (chat message) - should require authentication..."
|
|
local kind14_event
|
|
kind14_event=$(nak event -k 14 --content "Chat message - should require auth" \
|
|
--tag "p,$test_pubkey" \
|
|
--sec "$test_privkey" 2>/dev/null || echo "")
|
|
|
|
if [[ -n "$kind14_event" ]]; then
|
|
local result14
|
|
result14=$(echo "$kind14_event" | timeout 10s nak event "$RELAY_URL" 2>&1 || true)
|
|
log "Kind 14 event result: $result14"
|
|
|
|
if echo "$result14" | grep -q "AUTH\|auth\|authentication\|restricted"; then
|
|
log_success "Kind 14 event requested authentication (correct behavior for DMs)"
|
|
elif echo "$result14" | grep -q "OK.*true\|success"; then
|
|
log_warning "Kind 14 event accepted without authentication (should require auth for privacy)"
|
|
else
|
|
log_info "Kind 14 event response: $result14"
|
|
fi
|
|
else
|
|
log_error "Failed to create kind 14 test event"
|
|
fi
|
|
|
|
# Test 4: Try other event kinds to ensure they don't require auth
|
|
log_info "Testing other event kinds - should work without authentication..."
|
|
for kind in 0 3 7; do
|
|
local test_event
|
|
test_event=$(nak event -k "$kind" --content "Test event kind $kind - should not require auth" \
|
|
--sec "$test_privkey" 2>/dev/null || echo "")
|
|
|
|
if [[ -n "$test_event" ]]; then
|
|
local result
|
|
result=$(echo "$test_event" | timeout 10s nak event "$RELAY_URL" 2>&1 || true)
|
|
log "Kind $kind event result: $result"
|
|
|
|
if echo "$result" | grep -q "OK.*true\|success"; then
|
|
log_success "Kind $kind event accepted without authentication (correct)"
|
|
elif echo "$result" | grep -q "AUTH\|auth\|authentication"; then
|
|
log_warning "Kind $kind event requested authentication (unexpected)"
|
|
else
|
|
log_info "Kind $kind event response: $result"
|
|
fi
|
|
fi
|
|
done
|
|
|
|
log_info "Kind-specific authentication test completed"
|
|
return 0
|
|
}
|
|
|
|
# Main test execution
|
|
main() {
|
|
log_info "=== Starting NIP-42 Authentication Tests ==="
|
|
|
|
local test_results=()
|
|
local failed_tests=0
|
|
|
|
# Run all tests
|
|
if check_dependencies; then
|
|
test_results+=("Dependencies: PASS")
|
|
else
|
|
test_results+=("Dependencies: FAIL")
|
|
((failed_tests++))
|
|
fi
|
|
|
|
if test_nip42_support; then
|
|
test_results+=("NIP-42 Support: PASS")
|
|
else
|
|
test_results+=("NIP-42 Support: FAIL")
|
|
((failed_tests++))
|
|
fi
|
|
|
|
if test_auth_challenge_generation; then
|
|
test_results+=("Auth Challenge: PASS")
|
|
else
|
|
test_results+=("Auth Challenge: FAIL")
|
|
((failed_tests++))
|
|
fi
|
|
|
|
if test_nip42_auth_flow; then
|
|
test_results+=("Auth Flow: PASS")
|
|
else
|
|
test_results+=("Auth Flow: FAIL")
|
|
((failed_tests++))
|
|
fi
|
|
|
|
if test_websocket_auth_messages; then
|
|
test_results+=("WebSocket AUTH: PASS")
|
|
else
|
|
test_results+=("WebSocket AUTH: FAIL")
|
|
((failed_tests++))
|
|
fi
|
|
|
|
if test_nip42_configuration; then
|
|
test_results+=("Configuration: PASS")
|
|
else
|
|
test_results+=("Configuration: FAIL")
|
|
((failed_tests++))
|
|
fi
|
|
|
|
if test_nip42_performance; then
|
|
test_results+=("Performance: PASS")
|
|
else
|
|
test_results+=("Performance: FAIL")
|
|
((failed_tests++))
|
|
fi
|
|
|
|
if test_nip42_kind_specific_auth; then
|
|
test_results+=("Kind-Specific Auth: PASS")
|
|
else
|
|
test_results+=("Kind-Specific Auth: FAIL")
|
|
((failed_tests++))
|
|
fi
|
|
|
|
# Print summary
|
|
echo ""
|
|
log_info "=== NIP-42 Test Results Summary ==="
|
|
for result in "${test_results[@]}"; do
|
|
if echo "$result" | grep -q "PASS"; then
|
|
log_success "$result"
|
|
else
|
|
log_error "$result"
|
|
fi
|
|
done
|
|
|
|
echo ""
|
|
if [[ $failed_tests -eq 0 ]]; then
|
|
log_success "All NIP-42 tests completed successfully!"
|
|
log_success "NIP-42 authentication implementation is working correctly"
|
|
else
|
|
log_warning "$failed_tests test(s) failed or had issues"
|
|
log_info "Check the log file for detailed output: $LOG_FILE"
|
|
fi
|
|
|
|
log_info "=== NIP-42 Authentication Tests Complete ==="
|
|
|
|
return $failed_tests
|
|
}
|
|
|
|
# Run main function
|
|
main "$@" |