diff --git a/weed/credential/grpc/grpc_store.go b/weed/credential/grpc/grpc_store.go index 944a09c13..520a94e43 100644 --- a/weed/credential/grpc/grpc_store.go +++ b/weed/credential/grpc/grpc_store.go @@ -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)) } } diff --git a/weed/iam/oidc/oidc_provider.go b/weed/iam/oidc/oidc_provider.go index d85ef065e..b5c3c1969 100644 --- a/weed/iam/oidc/oidc_provider.go +++ b/weed/iam/oidc/oidc_provider.go @@ -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") diff --git a/weed/mount/weedfs_file_copy_range.go b/weed/mount/weedfs_file_copy_range.go index 1c9bdfb2d..da5fd7f50 100644 --- a/weed/mount/weedfs_file_copy_range.go +++ b/weed/mount/weedfs_file_copy_range.go @@ -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) } - diff --git a/weed/notification/webhook/http.go b/weed/notification/webhook/http.go index 82a8f8e71..4a044ae12 100644 --- a/weed/notification/webhook/http.go +++ b/weed/notification/webhook/http.go @@ -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 diff --git a/weed/operation/upload_content.go b/weed/operation/upload_content.go index 99a8a57d6..493d1c2c9 100644 --- a/weed/operation/upload_content.go +++ b/weed/operation/upload_content.go @@ -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) diff --git a/weed/s3api/s3_iam_middleware.go b/weed/s3api/s3_iam_middleware.go index 90caa3b4c..84de97f44 100644 --- a/weed/s3api/s3_iam_middleware.go +++ b/weed/s3api/s3_iam_middleware.go @@ -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 } diff --git a/weed/s3api/s3api_object_handlers.go b/weed/s3api/s3api_object_handlers.go index ffd9a4b40..5730cb920 100644 --- a/weed/s3api/s3api_object_handlers.go +++ b/weed/s3api/s3api_object_handlers.go @@ -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 diff --git a/weed/s3api/s3api_object_handlers_copy.go b/weed/s3api/s3api_object_handlers_copy.go index d76f0a95c..3da0370de 100644 --- a/weed/s3api/s3api_object_handlers_copy.go +++ b/weed/s3api/s3api_object_handlers_copy.go @@ -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 { diff --git a/weed/s3api/s3api_object_handlers_copy_stream.go b/weed/s3api/s3api_object_handlers_copy_stream.go index 98ea6da7f..a25a631c8 100644 --- a/weed/s3api/s3api_object_handlers_copy_stream.go +++ b/weed/s3api/s3api_object_handlers_copy_stream.go @@ -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) diff --git a/weed/security/jwt.go b/weed/security/jwt.go index 85b5163f9..22fbacf54 100644 --- a/weed/security/jwt.go +++ b/weed/security/jwt.go @@ -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 " 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 { diff --git a/weed/server/filer_server_handlers_copy.go b/weed/server/filer_server_handlers_copy.go index 041c19d89..35a5d1108 100644 --- a/weed/server/filer_server_handlers_copy.go +++ b/weed/server/filer_server_handlers_copy.go @@ -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") diff --git a/weed/server/filer_server_handlers_proxy.go b/weed/server/filer_server_handlers_proxy.go index 5126d421a..e06c5f5a9 100644 --- a/weed/server/filer_server_handlers_proxy.go +++ b/weed/server/filer_server_handlers_proxy.go @@ -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) diff --git a/weed/server/master_server_handlers.go b/weed/server/master_server_handlers.go index 731052601..102eb6669 100644 --- a/weed/server/master_server_handlers.go +++ b/weed/server/master_server_handlers.go @@ -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)) } diff --git a/weed/sftpd/sftp_filer.go b/weed/sftpd/sftp_filer.go index ac9f83d6c..718290ea4 100644 --- a/weed/sftpd/sftp_filer.go +++ b/weed/sftpd/sftp_filer.go @@ -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)) } } diff --git a/weed/shell/command_s3_iam_client.go b/weed/shell/command_s3_iam_client.go index 3bf405fd1..eb619100a 100644 --- a/weed/shell/command_s3_iam_client.go +++ b/weed/shell/command_s3_iam_client.go @@ -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)) } diff --git a/weed/shell/command_volume_fsck.go b/weed/shell/command_volume_fsck.go index 62d75092d..120885145 100644 --- a/weed/shell/command_volume_fsck.go +++ b/weed/shell/command_volume_fsck.go @@ -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 { diff --git a/weed/util/http/http_global_client_util.go b/weed/util/http/http_global_client_util.go index dc778b56e..c57c0a489 100644 --- a/weed/util/http/http_global_client_util.go +++ b/weed/util/http/http_global_client_util.go @@ -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)) } }