Skip to content

Intermittent "Error getting an UberMint" error when downscoping token for use in GCS #14482

@markraymondbnfx

Description

@markraymondbnfx

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.

Metadata

Metadata

Assignees

Labels

api: storageIssues related to the Cloud Storage API.triage meI really want to be triaged.

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions