-
Notifications
You must be signed in to change notification settings - Fork 90
feat: add API key authentication to prover server #1880
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 | ||
| 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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 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\)"
🧰 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 |
||
| 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) | ||
| } | ||
| }) | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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:
🧰 Tools
🪛 Shellcheck (0.10.0)
[warning] 36-36: Did you forget to close this double quoted string?
(SC1078)
🤖 Prompt for AI Agents