Client
- GCS
- cloud.google.com/go/storage
- OAuth
- golang.org/x/oauth2
- golang.org/x/oauth2/google
- golang.org/x/oauth2/google/downscope
Environment
Go version: 1.24.7
Package versions:
- cloud.google.com/go/storage@v1.50.0
- golang.org/x/oauth2@v0.30.0
Code and Dependencies
import (
"context"
"fmt"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"golang.org/x/oauth2/google/downscope"
)
// createDownscopedToken would be run on the token broker in order to generate
// a downscoped access token that only grants access to objects whose name begins with prefix.
// The token broker would then pass the newly created token to the requesting token consumer for use.
func createDownscopedToken(bucketName string, prefix string) error {
// bucketName := "foo"
// prefix := "profile-picture-"
ctx := context.Background()
// A condition can optionally be provided to further restrict access permissions.
condition := downscope.AvailabilityCondition{
Expression: "resource.name.startsWith('projects/_/buckets/" + bucketName + "/objects/" + prefix + "')",
Title: prefix + " Only",
Description: "Restricts a token to only be able to access objects that start with `" + prefix + "`",
}
// Initializes an accessBoundary with one Rule which restricts the downscoped
// token to only be able to access the bucket "bucketName" and only grants it the
// permission "storage.objectViewer".
accessBoundary := []downscope.AccessBoundaryRule{
{
AvailableResource: "//storage.googleapis.com/projects/_/buckets/" + bucketName,
AvailablePermissions: []string{"inRole:roles/storage.objectViewer"},
Condition: &condition, // Optional
},
}
// This Source can be initialized in multiple ways; the following example uses
// Application Default Credentials.
var rootSource oauth2.TokenSource
// You must provide the "https://www.googleapis.com/auth/cloud-platform" scope.
rootSource, err := google.DefaultTokenSource(ctx, "https://www.googleapis.com/auth/cloud-platform")
if err != nil {
return fmt.Errorf("failed to generate rootSource: %w", err)
}
// downscope.NewTokenSource constructs the token source with the configuration provided.
dts, err := downscope.NewTokenSource(ctx, downscope.DownscopingConfig{RootSource: rootSource, Rules: accessBoundary})
if err != nil {
return fmt.Errorf("failed to generate downscoped token source: %w", err)
}
// Token() uses the previously declared TokenSource to generate a downscoped token.
tok, err := dts.Token()
if err != nil {
// <-- HERE returns error:
// "failed to generate token: downscope: unable to exchange token; 400. Server responded: {\"error\":\"invalid_grant\",\"error_description\":\"Error getting an UberMint\"}"
return fmt.Errorf("failed to generate token: %w", err)
}
// Pass this token back to the token consumer.
_ = tok
return nil
}
Expected behavior
Description indicating whether the error is server or client-side.
The error message refers to an internal service error but the status code is 4xx, indicating a client-side error.
What is "UberMint"?
Can the request be retried?
What's the corrective action here?
Actual behavior
An opaque internal error message is returned.
Client
Environment
Go version: 1.24.7
Package versions:
Code and Dependencies
Expected behavior
Description indicating whether the error is server or client-side.
The error message refers to an internal service error but the status code is 4xx, indicating a client-side error.
What is "UberMint"?
Can the request be retried?
What's the corrective action here?
Actual behavior
An opaque internal error message is returned.