github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/credentials/oauth/oauth.go (about)

     1  /*
     2   *
     3   * Copyright 2015 gRPC authors.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   *
    17   */
    18  
    19  // Package oauth implements gRPC credentials using OAuth.
    20  package oauth
    21  
    22  import (
    23  	"context"
    24  	"fmt"
    25  	"io/ioutil"
    26  	"net/url"
    27  	"sync"
    28  
    29  	"github.com/hxx258456/ccgo/grpc/credentials"
    30  	"golang.org/x/oauth2"
    31  	"golang.org/x/oauth2/google"
    32  	"golang.org/x/oauth2/jwt"
    33  )
    34  
    35  // TokenSource supplies PerRPCCredentials from an oauth2.TokenSource.
    36  type TokenSource struct {
    37  	oauth2.TokenSource
    38  }
    39  
    40  // GetRequestMetadata gets the request metadata as a map from a TokenSource.
    41  func (ts TokenSource) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
    42  	token, err := ts.Token()
    43  	if err != nil {
    44  		return nil, err
    45  	}
    46  	ri, _ := credentials.RequestInfoFromContext(ctx)
    47  	if err = credentials.CheckSecurityLevel(ri.AuthInfo, credentials.PrivacyAndIntegrity); err != nil {
    48  		return nil, fmt.Errorf("unable to transfer TokenSource PerRPCCredentials: %v", err)
    49  	}
    50  	return map[string]string{
    51  		"authorization": token.Type() + " " + token.AccessToken,
    52  	}, nil
    53  }
    54  
    55  // RequireTransportSecurity indicates whether the credentials requires transport security.
    56  func (ts TokenSource) RequireTransportSecurity() bool {
    57  	return true
    58  }
    59  
    60  // removeServiceNameFromJWTURI removes RPC service name from URI.
    61  func removeServiceNameFromJWTURI(uri string) (string, error) {
    62  	parsed, err := url.Parse(uri)
    63  	if err != nil {
    64  		return "", err
    65  	}
    66  	parsed.Path = "/"
    67  	return parsed.String(), nil
    68  }
    69  
    70  type jwtAccess struct {
    71  	jsonKey []byte
    72  }
    73  
    74  // NewJWTAccessFromFile creates PerRPCCredentials from the given keyFile.
    75  func NewJWTAccessFromFile(keyFile string) (credentials.PerRPCCredentials, error) {
    76  	jsonKey, err := ioutil.ReadFile(keyFile)
    77  	if err != nil {
    78  		return nil, fmt.Errorf("credentials: failed to read the service account key file: %v", err)
    79  	}
    80  	return NewJWTAccessFromKey(jsonKey)
    81  }
    82  
    83  // NewJWTAccessFromKey creates PerRPCCredentials from the given jsonKey.
    84  func NewJWTAccessFromKey(jsonKey []byte) (credentials.PerRPCCredentials, error) {
    85  	return jwtAccess{jsonKey}, nil
    86  }
    87  
    88  func (j jwtAccess) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
    89  	// Remove RPC service name from URI that will be used as audience
    90  	// in a self-signed JWT token. It follows https://google.aip.dev/auth/4111.
    91  	aud, err := removeServiceNameFromJWTURI(uri[0])
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  	// TODO: the returned TokenSource is reusable. Store it in a sync.Map, with
    96  	// uri as the key, to avoid recreating for every RPC.
    97  	ts, err := google.JWTAccessTokenSourceFromJSON(j.jsonKey, aud)
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  	token, err := ts.Token()
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  	ri, _ := credentials.RequestInfoFromContext(ctx)
   106  	if err = credentials.CheckSecurityLevel(ri.AuthInfo, credentials.PrivacyAndIntegrity); err != nil {
   107  		return nil, fmt.Errorf("unable to transfer jwtAccess PerRPCCredentials: %v", err)
   108  	}
   109  	return map[string]string{
   110  		"authorization": token.Type() + " " + token.AccessToken,
   111  	}, nil
   112  }
   113  
   114  func (j jwtAccess) RequireTransportSecurity() bool {
   115  	return true
   116  }
   117  
   118  // oauthAccess supplies PerRPCCredentials from a given token.
   119  type oauthAccess struct {
   120  	token oauth2.Token
   121  }
   122  
   123  // NewOauthAccess constructs the PerRPCCredentials using a given token.
   124  func NewOauthAccess(token *oauth2.Token) credentials.PerRPCCredentials {
   125  	return oauthAccess{token: *token}
   126  }
   127  
   128  func (oa oauthAccess) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
   129  	ri, _ := credentials.RequestInfoFromContext(ctx)
   130  	if err := credentials.CheckSecurityLevel(ri.AuthInfo, credentials.PrivacyAndIntegrity); err != nil {
   131  		return nil, fmt.Errorf("unable to transfer oauthAccess PerRPCCredentials: %v", err)
   132  	}
   133  	return map[string]string{
   134  		"authorization": oa.token.Type() + " " + oa.token.AccessToken,
   135  	}, nil
   136  }
   137  
   138  func (oa oauthAccess) RequireTransportSecurity() bool {
   139  	return true
   140  }
   141  
   142  // NewComputeEngine constructs the PerRPCCredentials that fetches access tokens from
   143  // Google Compute Engine (GCE)'s metadata server. It is only valid to use this
   144  // if your program is running on a GCE instance.
   145  // TODO(dsymonds): Deprecate and remove this.
   146  func NewComputeEngine() credentials.PerRPCCredentials {
   147  	return TokenSource{google.ComputeTokenSource("")}
   148  }
   149  
   150  // serviceAccount represents PerRPCCredentials via JWT signing key.
   151  type serviceAccount struct {
   152  	mu     sync.Mutex
   153  	config *jwt.Config
   154  	t      *oauth2.Token
   155  }
   156  
   157  func (s *serviceAccount) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
   158  	s.mu.Lock()
   159  	defer s.mu.Unlock()
   160  	if !s.t.Valid() {
   161  		var err error
   162  		s.t, err = s.config.TokenSource(ctx).Token()
   163  		if err != nil {
   164  			return nil, err
   165  		}
   166  	}
   167  	ri, _ := credentials.RequestInfoFromContext(ctx)
   168  	if err := credentials.CheckSecurityLevel(ri.AuthInfo, credentials.PrivacyAndIntegrity); err != nil {
   169  		return nil, fmt.Errorf("unable to transfer serviceAccount PerRPCCredentials: %v", err)
   170  	}
   171  	return map[string]string{
   172  		"authorization": s.t.Type() + " " + s.t.AccessToken,
   173  	}, nil
   174  }
   175  
   176  func (s *serviceAccount) RequireTransportSecurity() bool {
   177  	return true
   178  }
   179  
   180  // NewServiceAccountFromKey constructs the PerRPCCredentials using the JSON key slice
   181  // from a Google Developers service account.
   182  func NewServiceAccountFromKey(jsonKey []byte, scope ...string) (credentials.PerRPCCredentials, error) {
   183  	config, err := google.JWTConfigFromJSON(jsonKey, scope...)
   184  	if err != nil {
   185  		return nil, err
   186  	}
   187  	return &serviceAccount{config: config}, nil
   188  }
   189  
   190  // NewServiceAccountFromFile constructs the PerRPCCredentials using the JSON key file
   191  // of a Google Developers service account.
   192  func NewServiceAccountFromFile(keyFile string, scope ...string) (credentials.PerRPCCredentials, error) {
   193  	jsonKey, err := ioutil.ReadFile(keyFile)
   194  	if err != nil {
   195  		return nil, fmt.Errorf("credentials: failed to read the service account key file: %v", err)
   196  	}
   197  	return NewServiceAccountFromKey(jsonKey, scope...)
   198  }
   199  
   200  // NewApplicationDefault returns "Application Default Credentials". For more
   201  // detail, see https://developers.google.com/accounts/docs/application-default-credentials.
   202  func NewApplicationDefault(ctx context.Context, scope ...string) (credentials.PerRPCCredentials, error) {
   203  	creds, err := google.FindDefaultCredentials(ctx, scope...)
   204  	if err != nil {
   205  		return nil, err
   206  	}
   207  
   208  	// If JSON is nil, the authentication is provided by the environment and not
   209  	// with a credentials file, e.g. when code is running on Google Cloud
   210  	// Platform. Use the returned token source.
   211  	if creds.JSON == nil {
   212  		return TokenSource{creds.TokenSource}, nil
   213  	}
   214  
   215  	// If auth is provided by env variable or creds file, the behavior will be
   216  	// different based on whether scope is set. Because the returned
   217  	// creds.TokenSource does oauth with jwt by default, and it requires scope.
   218  	// We can only use it if scope is not empty, otherwise it will fail with
   219  	// missing scope error.
   220  	//
   221  	// If scope is set, use it, it should just work.
   222  	//
   223  	// If scope is not set, we try to use jwt directly without oauth (this only
   224  	// works if it's a service account).
   225  
   226  	if len(scope) != 0 {
   227  		return TokenSource{creds.TokenSource}, nil
   228  	}
   229  
   230  	// Try to convert JSON to a jwt config without setting the optional scope
   231  	// parameter to check if it's a service account (the function errors if it's
   232  	// not). This is necessary because the returned config doesn't show the type
   233  	// of the account.
   234  	if _, err := google.JWTConfigFromJSON(creds.JSON); err != nil {
   235  		// If this fails, it's not a service account, return the original
   236  		// TokenSource from above.
   237  		return TokenSource{creds.TokenSource}, nil
   238  	}
   239  
   240  	// If it's a service account, create a JWT only access with the key.
   241  	return NewJWTAccessFromKey(creds.JSON)
   242  }