security: add BearerPrefix constant for Authorization headers (#10101)

Introduce security.BearerPrefix ("Bearer ", RFC 6750) and use it
everywhere an "Authorization: Bearer <token>" header is constructed,
replacing the scattered "BEARER "/"Bearer " string literals. SeaweedFS
matches the scheme case-insensitively when parsing (security.GetJwt), so
behavior is unchanged; this removes the magic string and settles the
casing on the standard form. The parser's upper-case comparison stays as
is on purpose.
This commit is contained in:
Chris Lu
2026-06-24 19:36:42 -07:00
committed by GitHub
parent 4d3e5d94a9
commit 95427b5573
17 changed files with 32 additions and 22 deletions
+1 -1
View File
@@ -94,7 +94,7 @@ func (store *IamGrpcStore) withIamClient(ctx context.Context, fn func(ctx contex
if len(signingKey) > 0 {
token := security.GenJwtForFilerAdmin(signingKey, expiresAfterSec)
if token != "" {
ctx = metadata.AppendToOutgoingContext(ctx, "authorization", "Bearer "+string(token))
ctx = metadata.AppendToOutgoingContext(ctx, "authorization", security.BearerPrefix+string(token))
}
}
+2 -1
View File
@@ -24,6 +24,7 @@ import (
"github.com/golang-jwt/jwt/v5"
"github.com/seaweedfs/seaweedfs/weed/glog"
"github.com/seaweedfs/seaweedfs/weed/iam/providers"
"github.com/seaweedfs/seaweedfs/weed/security"
)
// OIDCProvider implements OpenID Connect authentication
@@ -595,7 +596,7 @@ func (p *OIDCProvider) getUserInfoWithToken(ctx context.Context, userID, accessT
// Set authorization header if access token is provided
if accessToken != "" {
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("Authorization", security.BearerPrefix+accessToken)
}
req.Header.Set("Accept", "application/json")
+1 -2
View File
@@ -427,7 +427,7 @@ func (wfs *WFS) copyEntryViaFiler(cancel <-chan struct{}, copyRequest wholeFileS
return nil, serverSideWholeFileCopyNotCommitted, fmt.Errorf("create filer copy request: %w", err)
}
if jwt := wfs.filerCopyJWT(); jwt != "" {
req.Header.Set("Authorization", "Bearer "+string(jwt))
req.Header.Set("Authorization", security.BearerPrefix+string(jwt))
}
resp, err := httpClient.Do(req)
@@ -504,4 +504,3 @@ func (wfs *WFS) filerCopyJWT() security.EncodedJwt {
}
return security.GenJwtForFilerServer(wfs.option.FilerSigningKey, wfs.option.FilerSigningExpiresAfterSec)
}
+2 -1
View File
@@ -12,6 +12,7 @@ import (
"time"
"github.com/seaweedfs/seaweedfs/weed/glog"
"github.com/seaweedfs/seaweedfs/weed/security"
util_http "github.com/seaweedfs/seaweedfs/weed/util/http"
)
@@ -84,7 +85,7 @@ func (h *httpClient) sendMessageWithRetry(message *webhookMessage, depth int) er
req.Header.Set("Content-Type", "application/json")
if h.token != "" {
req.Header.Set("Authorization", "Bearer "+h.token)
req.Header.Set("Authorization", security.BearerPrefix+h.token)
}
// Apply timeout via context (not on client) to avoid redundancy
+1 -1
View File
@@ -458,7 +458,7 @@ func (uploader *Uploader) upload_content(ctx context.Context, fillBufferFunction
req.Header.Set(k, v)
}
if option.Jwt != "" {
req.Header.Set("Authorization", "BEARER "+string(option.Jwt))
req.Header.Set("Authorization", security.BearerPrefix+string(option.Jwt))
}
request_id.InjectToRequest(ctx, req)
+3 -2
View File
@@ -14,6 +14,7 @@ import (
"github.com/seaweedfs/seaweedfs/weed/iam/providers"
"github.com/seaweedfs/seaweedfs/weed/iam/sts"
"github.com/seaweedfs/seaweedfs/weed/s3api/s3err"
"github.com/seaweedfs/seaweedfs/weed/security"
)
// privateNetworks contains pre-parsed private IP ranges for efficient lookups
@@ -87,11 +88,11 @@ func (s3iam *S3IAMIntegration) AuthenticateJWT(ctx context.Context, r *http.Requ
// Extract bearer token from Authorization header
authHeader := r.Header.Get("Authorization")
if !strings.HasPrefix(authHeader, "Bearer ") {
if !strings.HasPrefix(authHeader, security.BearerPrefix) {
return nil, s3err.ErrAccessDenied
}
sessionToken := strings.TrimPrefix(authHeader, "Bearer ")
sessionToken := strings.TrimPrefix(authHeader, security.BearerPrefix)
if sessionToken == "" {
return nil, s3err.ErrAccessDenied
}
+4 -3
View File
@@ -21,6 +21,7 @@ import (
"github.com/seaweedfs/seaweedfs/weed/filer"
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
"github.com/seaweedfs/seaweedfs/weed/security"
"github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
"github.com/seaweedfs/seaweedfs/weed/s3api/s3err"
@@ -1830,7 +1831,7 @@ func (s3a *S3ApiServer) fetchFullChunk(ctx context.Context, fileId string) (io.R
// Set JWT for authentication
if jwt != "" {
req.Header.Set("Authorization", "BEARER "+jwt)
req.Header.Set("Authorization", security.BearerPrefix+jwt)
}
// Use shared HTTP client
@@ -1877,7 +1878,7 @@ func (s3a *S3ApiServer) fetchChunkViewData(ctx context.Context, chunkView *filer
// Set JWT for authentication
if jwt != "" {
req.Header.Set("Authorization", "BEARER "+jwt)
req.Header.Set("Authorization", security.BearerPrefix+jwt)
}
// Use shared HTTP client with connection pooling
@@ -2940,7 +2941,7 @@ func (s3a *S3ApiServer) createEncryptedChunkReader(ctx context.Context, chunk *f
// Attach volume server JWT for authentication (uses config loaded once at startup)
jwt := filer.JwtForVolumeServer(chunk.GetFileIdString())
if jwt != "" {
req.Header.Set("Authorization", "BEARER "+jwt)
req.Header.Set("Authorization", security.BearerPrefix+jwt)
}
// Use shared HTTP client with connection pooling
+1 -1
View File
@@ -1635,7 +1635,7 @@ func (s3a *S3ApiServer) downloadChunkData(srcUrl, fileId string, offset, size in
req, err := http.NewRequest(http.MethodHead, srcUrl, nil)
if err == nil {
if jwt != "" {
req.Header.Set("Authorization", "BEARER "+string(jwt))
req.Header.Set("Authorization", security.BearerPrefix+string(jwt))
}
resp, err := util_http.GetGlobalHttpClient().Do(req)
if err == nil {
@@ -106,7 +106,7 @@ func (s3a *S3ApiServer) streamCopyChunkRange(
return fmt.Errorf("create source GET: %w", err)
}
if srcJwt != "" {
srcReq.Header.Set("Authorization", "BEARER "+srcJwt)
srcReq.Header.Set("Authorization", security.BearerPrefix+srcJwt)
}
if isFullChunk {
// Manually setting Accept-Encoding tells Go's http.Transport that
@@ -188,7 +188,7 @@ func (s3a *S3ApiServer) streamCopyChunkRange(
}
req.Header.Set("Content-Type", contentType)
if dstJwt != "" {
req.Header.Set("Authorization", "BEARER "+string(dstJwt))
req.Header.Set("Authorization", security.BearerPrefix+string(dstJwt))
}
resp, err := util_http.GetGlobalHttpClient().Do(req)
+5
View File
@@ -13,6 +13,11 @@ import (
type EncodedJwt string
type SigningKey []byte
// BearerPrefix is the RFC 6750 Authorization header scheme prefix for bearer
// tokens. Used when constructing "Authorization: Bearer <token>" headers; the
// scheme name itself is matched case-insensitively when parsing (see GetJwt).
const BearerPrefix = "Bearer "
// SeaweedFileIdClaims is created by Master server(s) and consumed by Volume server(s),
// restricting the access this JWT allows to only a single file.
type SeaweedFileIdClaims struct {
+3 -2
View File
@@ -18,6 +18,7 @@ import (
"github.com/seaweedfs/seaweedfs/weed/glog"
"github.com/seaweedfs/seaweedfs/weed/operation"
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
"github.com/seaweedfs/seaweedfs/weed/security"
"github.com/seaweedfs/seaweedfs/weed/util"
)
@@ -669,7 +670,7 @@ func (fs *FilerServer) uploadData(ctx context.Context, reader io.Reader, urlLoca
}
if auth != "" {
req.Header.Set("Authorization", "Bearer "+auth)
req.Header.Set("Authorization", security.BearerPrefix+auth)
}
resp, err := client.Do(req)
@@ -795,7 +796,7 @@ func (fs *FilerServer) performStreamCopy(ctx context.Context, srcUrl, dstUrl, au
// Set authorization header if provided
if auth != "" {
dstReq.Header.Set("Authorization", "Bearer "+auth)
dstReq.Header.Set("Authorization", security.BearerPrefix+auth)
}
dstReq.Header.Set("Content-Type", "application/octet-stream")
+2 -1
View File
@@ -5,6 +5,7 @@ import (
"sync"
"github.com/seaweedfs/seaweedfs/weed/glog"
"github.com/seaweedfs/seaweedfs/weed/security"
util_http "github.com/seaweedfs/seaweedfs/weed/util/http"
"github.com/seaweedfs/seaweedfs/weed/util/mem"
"github.com/seaweedfs/seaweedfs/weed/util/request_id"
@@ -99,7 +100,7 @@ func (fs *FilerServer) proxyToVolumeServer(w http.ResponseWriter, r *http.Reques
// volume server may require a read JWT even though the proxy endpoint doesn't
if jwt := fs.maybeGetVolumeReadJwtAuthorizationToken(fileId); jwt != "" {
proxyReq.Header.Set("Authorization", "BEARER "+jwt)
proxyReq.Header.Set("Authorization", security.BearerPrefix+jwt)
}
proxyResponse, postErr := util_http.GetGlobalHttpClient().Do(proxyReq)
+1 -1
View File
@@ -221,5 +221,5 @@ func (ms *MasterServer) maybeAddJwtAuthorization(w http.ResponseWriter, fileId s
return
}
w.Header().Set("Authorization", "BEARER "+string(encodedJwt))
w.Header().Set("Authorization", security.BearerPrefix+string(encodedJwt))
}
+1 -1
View File
@@ -345,7 +345,7 @@ func (fs *SftpServer) putFile(filepath string, reader io.Reader, user *user.User
if len(fs.filerSigningKey) > 0 {
jwt := security.GenJwtForFilerServer(security.SigningKey(fs.filerSigningKey), fs.filerSigningExpiresAfter)
if jwt != "" {
req.Header.Set("Authorization", "Bearer "+string(jwt))
req.Header.Set("Authorization", security.BearerPrefix+string(jwt))
}
}
+1 -1
View File
@@ -41,5 +41,5 @@ func iamAdminAuthContext(ctx context.Context) context.Context {
if token == "" {
return ctx
}
return metadata.AppendToOutgoingContext(ctx, "authorization", "Bearer "+string(token))
return metadata.AppendToOutgoingContext(ctx, "authorization", security.BearerPrefix+string(token))
}
+1 -1
View File
@@ -671,7 +671,7 @@ func (c *commandVolumeFsck) httpDelete(path util.FullPath) {
if c.filerSigningKey != "" {
encodedJwt := security.GenJwtForFilerServer(security.SigningKey(c.filerSigningKey), jwtFilerTokenExpirationSeconds)
req.Header.Set("Authorization", "BEARER "+string(encodedJwt))
req.Header.Set("Authorization", security.BearerPrefix+string(encodedJwt))
}
if *c.verbose {
+1 -1
View File
@@ -152,7 +152,7 @@ func Head(url string) (http.Header, error) {
func maybeAddAuth(req *http.Request, jwt string) {
if jwt != "" {
req.Header.Set("Authorization", "BEARER "+string(jwt))
req.Header.Set("Authorization", security.BearerPrefix+string(jwt))
}
}