Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: update samples for new auth #4383

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 18 additions & 16 deletions auth/access_token_from_impersonated_credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,51 +21,53 @@ import (
"io"
"time"

"golang.org/x/oauth2/google"
"google.golang.org/api/impersonate"
"google.golang.org/api/option"
"cloud.google.com/go/auth/credentials"
"cloud.google.com/go/auth/credentials/impersonate"
)

// getAccessTokenFromImpersonatedCredentials uses a service account (SA1) to impersonate
// another service account (SA2) and obtain OAuth2 token for the impersonated account.
// another service account (SA2) and obtain a token for the impersonated account.
// To obtain a token for SA2, SA1 should have the "roles/iam.serviceAccountTokenCreator" permission on SA2.
func getAccessTokenFromImpersonatedCredentials(w io.Writer, impersonatedServiceAccount, scope string) error {
// impersonatedServiceAccount := "[email protected]"
// scope := "https://www.googleapis.com/auth/cloud-platform"

ctx := context.Background()

// Construct the GoogleCredentials object which obtains the default configuration from your
// working environment.
credentials, err := google.FindDefaultCredentials(ctx, scope)
// Construct the Credentials object which obtains the default configuration
// from your working environment.
creds, err := credentials.DetectDefault(&credentials.DetectOptions{
Scopes: []string{scope},
})
if err != nil {
fmt.Fprintf(w, "failed to generate default credentials: %v", err)
return fmt.Errorf("failed to generate default credentials: %w", err)
}

ts, err := impersonate.CredentialsTokenSource(ctx, impersonate.CredentialsConfig{
impCreds, err := impersonate.NewCredentials(&impersonate.CredentialsOptions{
TargetPrincipal: impersonatedServiceAccount,
Scopes: []string{scope},
Lifetime: 300 * time.Second,
// delegates: The chained list of delegates required to grant the final accessToken.
// For more information, see:
// https://cloud.google.com/iam/docs/create-short-lived-credentials-direct#sa-credentials-permissions
// Delegates is NOT USED here.
Delegates: []string{},
}, option.WithCredentials(credentials))
Delegates: []string{},
Credentials: creds,
})
if err != nil {
fmt.Fprintf(w, "CredentialsTokenSource error: %v", err)
return fmt.Errorf("CredentialsTokenSource error: %w", err)
fmt.Fprintf(w, "NewCredentials error: %v", err)
return fmt.Errorf("NewCredentials error: %w", err)
}

// Get the OAuth2 token.
// Once you've obtained the OAuth2 token, you can use it to make an authenticated call.
t, err := ts.Token()
// Get the token. Once you've obtained the token, you can use it to make a
// authenticated call.
t, err := impCreds.Token(ctx)
if err != nil {
fmt.Fprintf(w, "failed to receive token: %v", err)
return fmt.Errorf("failed to receive token: %w", err)
}
fmt.Fprintf(w, "Generated OAuth2 token with length %d.\n", len(t.AccessToken))
fmt.Fprintf(w, "Generated OAuth2 token with length %d.\n", len(t.Value))

return nil
}
Expand Down
8 changes: 5 additions & 3 deletions auth/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ import (
"strings"
"testing"

"cloud.google.com/go/auth/credentials"
"github.com/GoogleCloudPlatform/golang-samples/internal/testutil"
"golang.org/x/oauth2/google"
"google.golang.org/api/idtoken"
"google.golang.org/api/option"
)
Expand Down Expand Up @@ -87,12 +87,14 @@ func TestAuthSnippets(t *testing.T) {
buf.Reset()
want = "ID token verified."

credentials, err := google.FindDefaultCredentials(ctx)
credentials, err := credentials.DetectDefault(&credentials.DetectOptions{
Scopes: []string{"https://www.googleapis.com/auth/cloud-platform"},
})
if err != nil {
t.Fatalf("failed to generate default credentials: %v", err)
}

ts, err := idtoken.NewTokenSource(ctx, audience, option.WithCredentials(credentials))
ts, err := idtoken.NewTokenSource(ctx, audience, option.WithAuthCredentials(credentials))
if err != nil {
t.Fatalf("failed to create NewTokenSource: %v", err)
}
Expand Down
29 changes: 17 additions & 12 deletions auth/authenticate_explicit_with_adc.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ import (
"fmt"
"io"

"cloud.google.com/go/auth/credentials"
"cloud.google.com/go/storage"
"golang.org/x/oauth2/google"
"google.golang.org/api/iterator"
"google.golang.org/api/option"
)
Expand All @@ -31,28 +31,33 @@ import (
func authenticateExplicitWithAdc(w io.Writer) error {
ctx := context.Background()

// Construct the Google credentials object which obtains the default configuration from your
// Construct Credentials which obtains the default configuration from your
// working environment.
// google.FindDefaultCredentials() will give you ComputeEngineCredentials
// if you are on a GCE (or other metadata server supported environments).
credentials, err := google.FindDefaultCredentials(ctx, "https://www.googleapis.com/auth/cloud-platform")
credentials, err := credentials.DetectDefault(&credentials.DetectOptions{
Scopes: []string{"https://www.googleapis.com/auth/cloud-platform"},
})
if err != nil {
return fmt.Errorf("failed to generate default credentials: %w", err)
}
// If you are authenticating to a Cloud API, you can let the library include the default scope,
// https://www.googleapis.com/auth/cloud-platform, because IAM is used to provide fine-grained
// permissions for Cloud.
// For more information on scopes to use,
// see: https://developers.google.com/identity/protocols/oauth2/scopes
// If you are authenticating to a Cloud API, you can let the library include
// the default scope, https://www.googleapis.com/auth/cloud-platform,
// because IAM is used to provide fine-grained permissions for Cloud. For
// more information on scopes to use, see:
// https://developers.google.com/identity/protocols/oauth2/scopes

// Construct the Storage client.
client, err := storage.NewClient(ctx, option.WithCredentials(credentials))
client, err := storage.NewClient(ctx, option.WithAuthCredentials(credentials))
if err != nil {
return fmt.Errorf("NewClient: %w", err)
}
defer client.Close()

it := client.Buckets(ctx, credentials.ProjectID)
projID, err := credentials.ProjectID(ctx)
if err != nil {
return fmt.Errorf("ProjectID: %w", err)
}

it := client.Buckets(ctx, projID)
for {
bucketAttrs, err := it.Next()
if err == iterator.Done {
Expand Down
34 changes: 19 additions & 15 deletions auth/downscoping/credential_initialization.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,31 +20,35 @@ package downscopedoverview
// [START auth_downscoping_initialize_downscoped_cred]

import (
"context"
"fmt"

"golang.org/x/oauth2/google"
"golang.org/x/oauth2/google/downscope"
"cloud.google.com/go/auth/credentials"
"cloud.google.com/go/auth/credentials/downscope"
)

// initializeCredentials will generate a downscoped token using the provided Access Boundary Rules.
// initializeCredentials will generate a downscoped token using the provided
// Access Boundary Rules.
func initializeCredentials(accessBoundary []downscope.AccessBoundaryRule) error {
ctx := context.Background()

// You must provide the "https://www.googleapis.com/auth/cloud-platform" scope.
// This Source can be initialized in multiple ways; the following example uses
// Application Default Credentials.
rootSource, err := google.DefaultTokenSource(ctx, "https://www.googleapis.com/auth/cloud-platform")
// You must provide the "https://www.googleapis.com/auth/cloud-platform"
// scope. This credential can be initialized in multiple ways; the following
// example uses Application Default Credentials.
rootCreds, err := credentials.DetectDefault(&credentials.DetectOptions{
Scopes: []string{"https://www.googleapis.com/auth/cloud-platform"},
})
if err != nil {
return fmt.Errorf("failed to generate rootSource: %w", err)
return fmt.Errorf("failed to generate rootCreds: %w", err)
}

// downscope.NewTokenSource constructs the token source with the configuration provided.
dts, err := downscope.NewTokenSource(ctx, downscope.DownscopingConfig{RootSource: rootSource, Rules: accessBoundary})
// downscope.NewCredentials constructs the credentials with the
// configuration provided.
downscopedCreds, err := downscope.NewCredentials(&downscope.Options{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

observation: this is interesting that it's no longer necessary to provide a context object for new credentials.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The context for the old libraries has one thing they were used for: a way to shove an http.Client into the context that was later used during token exchanges. This is now an explicit param on the options struct. Everything in auth is evaluated lazy so you don't need a context when creating objects as no network calls are made.

Credentials: rootCreds,
Rules: accessBoundary,
})
if err != nil {
return fmt.Errorf("failed to generate downscoped token source: %w", err)
return fmt.Errorf("failed to generate downscoped credentials: %w", err)
}
_ = dts
_ = downscopedCreds
// You can now use dts to access Google Storage resources.
return nil
}
Expand Down
2 changes: 1 addition & 1 deletion auth/downscoping/downscoped_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ import (
"testing"
"time"

"cloud.google.com/go/auth/credentials/downscope"
"cloud.google.com/go/storage"
"github.com/GoogleCloudPlatform/golang-samples/internal/testutil"
"github.com/google/uuid"
"golang.org/x/oauth2/google/downscope"
)

func TestInitializeCredentials(t *testing.T) {
Expand Down
5 changes: 3 additions & 2 deletions auth/downscoping/initialize_CAB.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ package downscopedoverview

// [START auth_downscoping_rules]

import "golang.org/x/oauth2/google/downscope"
import "cloud.google.com/go/auth/credentials/downscope"

// constructCAB shows how to initialize a Credential Access Boundary for downscoping tokens.
// constructCAB shows how to initialize a Credential Access Boundary for
// downscoped tokens.
func constructCAB(bucketName string, prefix string) {
// bucketName := "foo"
// prefix := "profile-picture-"
Expand Down
40 changes: 23 additions & 17 deletions auth/downscoping/token_broker.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ import (
"context"
"fmt"

"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"golang.org/x/oauth2/google/downscope"
"cloud.google.com/go/auth/credentials"
"cloud.google.com/go/auth/credentials/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.
// 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-"
Expand All @@ -50,23 +50,29 @@ func createDownscopedToken(bucketName string, prefix string) error {
},
}

// 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")
// This credential can be initialized in multiple ways; the following example
// uses Application Default Credentials. You must provide the
// "https://www.googleapis.com/auth/cloud-platform" scope.
creds, err := credentials.DetectDefault(&credentials.DetectOptions{
Scopes: []string{
"https://www.googleapis.com/auth/cloud-platform",
},
})
if err != nil {
return fmt.Errorf("failed to generate rootSource: %w", err)
return fmt.Errorf("failed to generate creds: %w", err)
}

// downscope.NewTokenSource constructs the token source with the configuration provided.
dts, err := downscope.NewTokenSource(ctx, downscope.DownscopingConfig{RootSource: rootSource, Rules: accessBoundary})
// downscope.NewCredentials constructs the credential with the configuration
// provided.
downscopedCreds, err := downscope.NewCredentials(&downscope.Options{
Credentials: creds,
Rules: accessBoundary,
})
if err != nil {
return fmt.Errorf("failed to generate downscoped token source: %w", err)
return fmt.Errorf("failed to generate downscoped credentials: %w", err)
}
// Token() uses the previously declared TokenSource to generate a downscoped token.
tok, err := dts.Token()
// Token uses the previously declared Credentials to generate a downscoped token.
tok, err := downscopedCreds.Token(ctx)
if err != nil {
return fmt.Errorf("failed to generate token: %w", err)
}
Expand Down
53 changes: 29 additions & 24 deletions auth/downscoping/token_consumer.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,44 +22,48 @@ import (
"io"
"io/ioutil"

"golang.org/x/oauth2/google"
"golang.org/x/oauth2/google/downscope"
"cloud.google.com/go/auth"
"cloud.google.com/go/auth/credentials"
"cloud.google.com/go/auth/credentials/downscope"

"cloud.google.com/go/storage"
"golang.org/x/oauth2"
"google.golang.org/api/option"
)

// A token consumer should define their own tokenSource. In the Token() method,
// it should send a query to a token broker requesting a downscoped token.
// The token broker holds the root credential that is used to generate the
// downscoped token.
type localTokenSource struct {
ctx context.Context
// A token consumer should define their own auth.Credentials . In the `Token`
// method, it should send a query to a token broker requesting a downscoped
// token. The token broker holds the root credential that is used to generate
// the downscoped token.
type localTokenProvider struct {
bucketName string
brokerURL string
}

func (lts localTokenSource) Token() (*oauth2.Token, error) {
var remoteToken *oauth2.Token
// Usually you would now retrieve remoteToken, an oauth2.Token, from token broker.
// This snippet performs the same functionality locally.
func (lts localTokenProvider) Token(ctx context.Context) (*auth.Token, error) {
var remoteToken *auth.Token
// Usually you would now retrieve remoteToken, an auth.Token, from token
// broker. This snippet performs the same functionality locally.
accessBoundary := []downscope.AccessBoundaryRule{
{
AvailableResource: "//storage.googleapis.com/projects/_/buckets/" + lts.bucketName,
AvailablePermissions: []string{"inRole:roles/storage.objectViewer"},
},
}
rootSource, err := google.DefaultTokenSource(lts.ctx, "https://www.googleapis.com/auth/cloud-platform")
creds, err := credentials.DetectDefault(&credentials.DetectOptions{
Scopes: []string{"https://www.googleapis.com/auth/cloud-platform"},
})
if err != nil {
return nil, fmt.Errorf("failed to generate rootSource: %w", err)
return nil, fmt.Errorf("failed to generate creds: %w", err)
}
dts, err := downscope.NewTokenSource(lts.ctx, downscope.DownscopingConfig{RootSource: rootSource, Rules: accessBoundary})
downscopedCreds, err := downscope.NewCredentials(&downscope.Options{
Credentials: creds,
Rules: accessBoundary,
})
if err != nil {
return nil, fmt.Errorf("failed to generate downscoped token source: %w", err)
return nil, fmt.Errorf("failed to generate downscoped credentials: %w", err)
}
// Token() uses the previously declared TokenSource to generate a downscoped token.
remoteToken, err = dts.Token()
// Token uses the previously declared Credentials to generate a downscoped token.
remoteToken, err = downscopedCreds.Token(ctx)
if err != nil {
return nil, fmt.Errorf("failed to generate token: %w", err)
}
Expand All @@ -75,16 +79,17 @@ func getObjectContents(output io.Writer, bucketName string, objectName string) e

ctx := context.Background()

thisTokenSource := localTokenSource{
ctx: ctx,
tokenProvider := localTokenProvider{
bucketName: bucketName,
brokerURL: "yourURL.com/internal/broker",
}

// Wrap the TokenSource in an oauth2.ReuseTokenSource to enable automatic refreshing.
refreshableTS := oauth2.ReuseTokenSource(nil, thisTokenSource)
// You can now use the token source to access Google Cloud Storage resources as follows.
storageClient, err := storage.NewClient(ctx, option.WithTokenSource(refreshableTS))
storageClient, err := storage.NewClient(ctx, option.WithAuthCredentials(
auth.NewCredentials(&auth.CredentialsOptions{
TokenProvider: auth.NewCachedTokenProvider(tokenProvider, nil),
}),
))
if err != nil {
return fmt.Errorf("failed to create the storage client: %w", err)
}
Expand Down
Loading
Loading