Callers now supply their own Gitea PAT as a Bearer token; the server validates it against GET /api/v1/user and threads it through context to all downstream Gitea API calls. GITEA_API_TOKEN env var and the GiteaAPIToken config field are removed.
50 lines
1.4 KiB
Go
50 lines
1.4 KiB
Go
package auth
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type tokenKey struct{}
|
|
|
|
// BearerMiddleware validates the incoming bearer token as a Gitea PAT by
|
|
// calling GET /api/v1/user. The validated token is stored in context for
|
|
// downstream use by the Gitea client.
|
|
func BearerMiddleware(giteaBaseURL string, next http.Handler) http.Handler {
|
|
hc := &http.Client{Timeout: 5 * time.Second}
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
token, ok := strings.CutPrefix(r.Header.Get("Authorization"), "Bearer ")
|
|
if !ok || token == "" {
|
|
http.Error(w, "unauthorized", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
req, err := http.NewRequestWithContext(r.Context(), http.MethodGet, giteaBaseURL+"/api/v1/user", nil)
|
|
if err != nil {
|
|
http.Error(w, "unauthorized", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
req.Header.Set("Authorization", "token "+token)
|
|
resp, err := hc.Do(req)
|
|
if err != nil || resp.StatusCode != http.StatusOK {
|
|
if resp != nil {
|
|
_ = resp.Body.Close()
|
|
}
|
|
http.Error(w, "unauthorized", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
_ = resp.Body.Close()
|
|
ctx := context.WithValue(r.Context(), tokenKey{}, token)
|
|
next.ServeHTTP(w, r.WithContext(ctx))
|
|
})
|
|
}
|
|
|
|
// TokenFromContext returns the validated Gitea PAT stored by BearerMiddleware.
|
|
func TokenFromContext(ctx context.Context) string {
|
|
if v, ok := ctx.Value(tokenKey{}).(string); ok {
|
|
return v
|
|
}
|
|
return ""
|
|
}
|