Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
42 changes: 42 additions & 0 deletions prover/server/scripts/test_auth.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#!/bin/bash

# Test script for API key authentication on prover server
# This script demonstrates how to test the API key functionality

echo "Testing Prover Server API Key Authentication"
echo "============================================="

# Test variables
SERVER_URL="http://localhost:3001"
API_KEY="test-api-key-12345"

echo ""
echo "1. Testing /health endpoint (should work without API key):"
curl -s -o /dev/null -w "%{http_code}" $SERVER_URL/health
echo ""

echo ""
echo "2. Testing /prove endpoint without API key (should return 401):"
curl -s -o /dev/null -w "%{http_code}" -X POST $SERVER_URL/prove -H "Content-Type: application/json" -d '{"circuit_type": "inclusion"}'
echo ""

echo ""
echo "3. Testing /prove endpoint with X-API-Key header (should work if server has API key):"
curl -s -o /dev/null -w "%{http_code}" -X POST $SERVER_URL/prove -H "Content-Type: application/json" -H "X-API-Key: $API_KEY" -d '{"circuit_type": "inclusion"}'
echo ""

echo ""
echo "4. Testing /prove endpoint with Authorization Bearer header (should work if server has API key):"
curl -s -o /dev/null -w "%{http_code}" -X POST $SERVER_URL/prove -H "Content-Type: application/json" -H "Authorization: Bearer $API_KEY" -d '{"circuit_type": "inclusion"}'
echo ""

echo ""
echo "To run this test:"
echo "1. Set PROVER_API_KEY environment variable: export PROVER_API_KEY=test-api-key-12345"
echo "2. Start the prover server
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix unclosed double quote.

The double quote on line 36 is not properly closed, causing a syntax error.

Apply this diff to fix the syntax error:

-echo "2. Start the prover server
+echo "2. Start the prover server"
🧰 Tools
🪛 Shellcheck (0.10.0)

[warning] 36-36: Did you forget to close this double quoted string?

(SC1078)

🤖 Prompt for AI Agents
In prover/server/scripts/test_auth.sh at line 36, the echo statement has an
unclosed double quote causing a syntax error. Close the double quote at the end
of the string to fix the syntax error.

echo "3. Run this script: bash test_auth.sh"
echo ""
echo "Expected results:"
echo "- /health: 200 (always accessible)"
echo "- /prove without key: 401 (if API key is set)"
echo "- /prove with key: 400 or other (depends on valid circuit data)"
Comment on lines +40 to +42
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix parsing errors with parentheses.

The parentheses in the expected results comments are causing shell parsing errors.

Apply this diff to fix the syntax errors:

-- /health: 200 (always accessible)
-- /prove without key: 401 (if API key is set)
-- /prove with key: 400 or other (depends on valid circuit data)"
+- /health: 200 \(always accessible\)
+- /prove without key: 401 \(if API key is set\)
+- /prove with key: 400 or other \(depends on valid circuit data\)"

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Shellcheck (0.10.0)

[error] 40-40: '(' is invalid here. Did you forget to escape it?

(SC1036)


[error] 40-40: Parsing stopped here. Invalid use of parentheses?

(SC1088)

🤖 Prompt for AI Agents
In prover/server/scripts/test_auth.sh around lines 40 to 42, the parentheses in
the echo statements are causing shell parsing errors. To fix this, escape the
parentheses by prefixing them with backslashes or enclose the entire string in
single quotes to prevent the shell from misinterpreting them. This will ensure
the echo commands run without syntax errors.

101 changes: 101 additions & 0 deletions prover/server/server/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package server

import (
"crypto/subtle"
"light/light-prover/logging"
"net/http"
"os"
"strings"
)

type authMiddleware struct {
next http.Handler
apiKey string
}

func NewAPIKeyMiddleware(apiKey string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return &authMiddleware{
next: next,
apiKey: apiKey,
}
}
}

func (m *authMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if !m.isAuthenticated(r) {
logging.Logger().Warn().
Str("remote_addr", r.RemoteAddr).
Str("path", r.URL.Path).
Str("method", r.Method).
Msg("Unauthorized API request - missing or invalid API key")

unauthorizedError := &Error{
StatusCode: http.StatusUnauthorized,
Code: "unauthorized",
Message: "Invalid or missing API key. Please provide a valid API key in the Authorization header as 'Bearer <api-key>' or in the X-API-Key header.",
}
unauthorizedError.send(w)
return
}

m.next.ServeHTTP(w, r)
}

func (m *authMiddleware) isAuthenticated(r *http.Request) bool {
if m.apiKey == "" {
return true
}

providedKey := m.extractAPIKey(r)
if providedKey == "" {
return false
}

return subtle.ConstantTimeCompare([]byte(m.apiKey), []byte(providedKey)) == 1
}

func (m *authMiddleware) extractAPIKey(r *http.Request) string {
if apiKey := r.Header.Get("X-API-Key"); apiKey != "" {
return apiKey
}

if authHeader := r.Header.Get("Authorization"); authHeader != "" {
if strings.HasPrefix(authHeader, "Bearer ") {
return strings.TrimPrefix(authHeader, "Bearer ")
}
}

return ""
}

func getAPIKeyFromEnv() string {
return os.Getenv("PROVER_API_KEY")
}

func requiresAuthentication(path string) bool {
publicPaths := []string{
"/health",
}

for _, publicPath := range publicPaths {
if path == publicPath {
return false
}
}

return true
}

func conditionalAuthMiddleware(apiKey string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if requiresAuthentication(r.URL.Path) {
authHandler := NewAPIKeyMiddleware(apiKey)(next)
authHandler.ServeHTTP(w, r)
} else {
next.ServeHTTP(w, r)
}
})
}
}
10 changes: 9 additions & 1 deletion prover/server/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,12 @@ func RunWithQueue(config *Config, redisQueue *RedisQueue, circuits []string, run
}

func RunEnhanced(config *EnhancedConfig, redisQueue *RedisQueue, circuits []string, runMode prover.RunMode, provingSystemsV1 []*prover.ProvingSystemV1, provingSystemsV2 []*prover.ProvingSystemV2) RunningJob {
apiKey := getAPIKeyFromEnv()
if apiKey != "" {
logging.Logger().Info().Msg("API key authentication enabled for prover server")
} else {
logging.Logger().Warn().Msg("No API key configured - server will accept all requests. Set PROVER_API_KEY environment variable to enable authentication.")
}
metricsMux := http.NewServeMux()
metricsServer := &http.Server{Addr: config.MetricsAddress, Handler: metricsMux}
metricsJob := spawnServerJob(metricsServer, "metrics server")
Expand Down Expand Up @@ -531,14 +537,16 @@ func RunEnhanced(config *EnhancedConfig, redisQueue *RedisQueue, circuits []stri
"X-Requested-With",
"Content-Type",
"Authorization",
"X-API-Key",
"X-Async",
"X-Sync",
}),
handlers.AllowedOrigins([]string{"*"}),
handlers.AllowedMethods([]string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}),
)

proverServer := &http.Server{Addr: config.ProverAddress, Handler: corsHandler(proverMux)}
authHandler := conditionalAuthMiddleware(apiKey)
proverServer := &http.Server{Addr: config.ProverAddress, Handler: corsHandler(authHandler(proverMux))}
proverJob := spawnServerJob(proverServer, "prover server")

if redisQueue != nil {
Expand Down
Loading