ScuttleBot

scuttlebot / internal / api / middleware.go
Blame History Raw 67 lines
1
package api
2
3
import (
4
"context"
5
"net/http"
6
"strings"
7
8
"github.com/conflicthq/scuttlebot/internal/auth"
9
)
10
11
type ctxKey string
12
13
const ctxAPIKey ctxKey = "apikey"
14
15
// apiKeyFromContext returns the authenticated APIKey from the request context,
16
// or nil if not authenticated.
17
func apiKeyFromContext(ctx context.Context) *auth.APIKey {
18
k, _ := ctx.Value(ctxAPIKey).(*auth.APIKey)
19
return k
20
}
21
22
// authMiddleware validates the Bearer token and injects the APIKey into context.
23
func (s *Server) authMiddleware(next http.Handler) http.Handler {
24
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
25
token := bearerToken(r)
26
if token == "" {
27
writeError(w, http.StatusUnauthorized, "missing authorization header")
28
return
29
}
30
key := s.apiKeys.Lookup(token)
31
if key == nil {
32
writeError(w, http.StatusUnauthorized, "invalid token")
33
return
34
}
35
// Update last-used timestamp in the background.
36
go s.apiKeys.TouchLastUsed(key.ID)
37
38
ctx := context.WithValue(r.Context(), ctxAPIKey, key)
39
next.ServeHTTP(w, r.WithContext(ctx))
40
})
41
}
42
43
// requireScope returns middleware that rejects requests without the given scope.
44
func (s *Server) requireScope(scope auth.Scope, next http.HandlerFunc) http.HandlerFunc {
45
return func(w http.ResponseWriter, r *http.Request) {
46
key := apiKeyFromContext(r.Context())
47
if key == nil {
48
writeError(w, http.StatusUnauthorized, "missing authentication")
49
return
50
}
51
if !key.HasScope(scope) {
52
writeError(w, http.StatusForbidden, "insufficient scope: requires "+string(scope))
53
return
54
}
55
next(w, r)
56
}
57
}
58
59
func bearerToken(r *http.Request) string {
60
auth := r.Header.Get("Authorization")
61
token, found := strings.CutPrefix(auth, "Bearer ")
62
if !found {
63
return ""
64
}
65
return strings.TrimSpace(token)
66
}
67

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button