internal/auth/auth.go (72 lines of code) (raw):
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package auth
import (
"context"
"fmt"
"os"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
storagev1 "google.golang.org/api/storage/v1"
)
const universeDomainDefault = "googleapis.com"
func getUniverseDomain(ctx context.Context, contents []byte, scope string) (string, error) {
creds, err := google.CredentialsFromJSON(ctx, contents, scope)
if err != nil {
err = fmt.Errorf("CredentialsFromJSON(): %w", err)
return "", err
}
domain, err := creds.GetUniverseDomain()
if err != nil {
err = fmt.Errorf("GetUniverseDomain(): %w", err)
return "", err
}
return domain, nil
}
// Create token source from the JSON file at the supplide path.
func newTokenSourceFromPath(ctx context.Context, path string, scope string) (oauth2.TokenSource, error) {
// Read the file.
contents, err := os.ReadFile(path)
if err != nil {
err = fmt.Errorf("ReadFile(%q): %w", path, err)
return nil, err
}
// By default, a standard OAuth 2.0 token source is created
// Create a config struct based on its contents.
jwtConfig, err := google.JWTConfigFromJSON(contents, scope)
if err != nil {
err = fmt.Errorf("JWTConfigFromJSON: %w", err)
return nil, err
}
domain, err := getUniverseDomain(ctx, contents, scope)
if err != nil {
return nil, err
}
// By default, a standard OAuth 2.0 token source is created
ts := jwtConfig.TokenSource(ctx)
// For non-GDU universe domains, token exchange is impossible and services
// must support self-signed JWTs with scopes.
// Override the token source to use self-signed JWT.
if domain != universeDomainDefault {
// Create self signed JWT access token.
ts, err = google.JWTAccessTokenSourceWithScope(contents, scope)
if err != nil {
err = fmt.Errorf("JWTAccessTokenSourceWithScope: %w", err)
return nil, err
}
}
return ts, err
}
// GetTokenSource generates the token-source for GCS endpoint by following oauth2.0 authentication
// for key-file and default-credential flow.
// It also supports generating the self-signed JWT tokenSource for key-file authentication which can be
// used by custom-endpoint(e.g. TPC).
func GetTokenSource(
ctx context.Context,
keyFile string,
tokenUrl string,
reuseTokenFromUrl bool,
) (tokenSrc oauth2.TokenSource, err error) {
// Create the oauth2 token source.
const scope = storagev1.DevstorageFullControlScope
var method string
if keyFile != "" {
tokenSrc, err = newTokenSourceFromPath(ctx, keyFile, scope)
method = "newTokenSourceFromPath"
} else if tokenUrl != "" {
tokenSrc, err = newProxyTokenSource(ctx, tokenUrl, reuseTokenFromUrl)
method = "newProxyTokenSource"
} else {
tokenSrc, err = google.DefaultTokenSource(ctx, scope)
method = "DefaultTokenSource"
}
if err != nil {
err = fmt.Errorf("%s: %w", method, err)
return
}
return
}