go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/auth/auth.go (about)

     1  // Copyright 2015 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package auth implements a wrapper around golang.org/x/oauth2.
    16  //
    17  // Its main improvement is the on-disk cache for authentication tokens, which is
    18  // especially important for 3-legged interactive OAuth flows: its usage
    19  // eliminates annoying login prompts each time a program is used (because the
    20  // refresh token can now be reused). The cache also allows to reduce unnecessary
    21  // token refresh calls when sharing a service account between processes.
    22  //
    23  // The package also implements some best practices regarding interactive login
    24  // flows in CLI programs. It makes it easy to implement a login process as
    25  // a separate interactive step that happens before the main program loop.
    26  //
    27  // The antipattern it tries to prevent is "launch an interactive login flow
    28  // whenever program hits 'Not Authorized' response from the server". This
    29  // usually results in a very confusing behavior, when login prompts pop up
    30  // unexpectedly at random time, random places and from multiple goroutines at
    31  // once, unexpectedly consuming unintended stdin input.
    32  package auth
    33  
    34  import (
    35  	"context"
    36  	"net/http"
    37  	"path/filepath"
    38  	"slices"
    39  	"strings"
    40  	"sync"
    41  	"time"
    42  
    43  	"cloud.google.com/go/compute/metadata"
    44  
    45  	"golang.org/x/oauth2"
    46  	"google.golang.org/grpc/credentials"
    47  
    48  	"go.chromium.org/luci/auth/internal"
    49  	"go.chromium.org/luci/common/clock"
    50  	"go.chromium.org/luci/common/data/stringset"
    51  	"go.chromium.org/luci/common/errors"
    52  	"go.chromium.org/luci/common/logging"
    53  	"go.chromium.org/luci/common/retry"
    54  	"go.chromium.org/luci/common/retry/transient"
    55  	"go.chromium.org/luci/lucictx"
    56  )
    57  
    58  var (
    59  	// ErrLoginRequired is returned by Transport or GetAccessToken in case long
    60  	// term credentials are not cached and the user must go through an interactive
    61  	// login flow.
    62  	ErrLoginRequired = errors.New("interactive login is required")
    63  
    64  	// ErrInsufficientAccess is returned by Login or Transport if an access token
    65  	// can't be minted for given OAuth scopes. For example if a GCE instance
    66  	// wasn't granted access to requested scopes when it was created.
    67  	ErrInsufficientAccess = internal.ErrInsufficientAccess
    68  
    69  	// ErrNoEmail is returned by GetEmail() if the cached credentials are not
    70  	// associated with some particular email. This may happen, for example, when
    71  	// using a refresh token that doesn't have 'userinfo.email' scope.
    72  	ErrNoEmail = errors.New("the token is not associated with an email")
    73  
    74  	// ErrBadOptions is returned by Login() or Transport() if Options passed
    75  	// to authenticator indicate incompatible features. This likely indicates
    76  	// a programming error.
    77  	ErrBadOptions = errors.New("bad authenticator options")
    78  
    79  	// ErrAudienceRequired is returned when UseIDTokens is set without specifying
    80  	// the target audience for ID tokens.
    81  	ErrAudienceRequired = errors.New("using ID tokens requires specifying an audience string")
    82  
    83  	// ErrNoIDToken is returned by GetAccessToken when UseIDTokens option is true,
    84  	// but the authentication method doesn't actually support ID tokens either
    85  	// inherently by its nature (e.g. not implemented) or due to its configuration
    86  	// (e.g. no necessary scopes).
    87  	ErrNoIDToken = errors.New("ID tokens are not supported in this configuration")
    88  
    89  	// ErrNoAccessToken is returned by GetAccessToken when UseIDTokens option is
    90  	// false (i.e. the caller wants access tokens), but the authentication method
    91  	// doesn't actually support access tokens.
    92  	ErrNoAccessToken = errors.New("access tokens are not supported in this configuration")
    93  )
    94  
    95  // Some known Google API OAuth scopes.
    96  const (
    97  	OAuthScopeEmail = "https://www.googleapis.com/auth/userinfo.email"
    98  	OAuthScopeIAM   = "https://www.googleapis.com/auth/iam"
    99  )
   100  
   101  const (
   102  	// GCEServiceAccount is special value that can be passed instead of path to
   103  	// a service account credentials file to indicate that GCE VM credentials
   104  	// should be used instead of a real credentials file.
   105  	GCEServiceAccount = ":gce"
   106  )
   107  
   108  // Method defines a method to use to obtain authentication token.
   109  type Method string
   110  
   111  // Supported authentication methods.
   112  const (
   113  	// AutoSelectMethod can be used to allow the library to pick a method most
   114  	// appropriate for given set of options and the current execution environment.
   115  	//
   116  	// For example, passing ServiceAccountJSONPath or ServiceAccountJSON makes
   117  	// Authenticator to pick ServiceAccountMethod.
   118  	//
   119  	// See SelectBestMethod function for details.
   120  	AutoSelectMethod Method = ""
   121  
   122  	// UserCredentialsMethod is used for interactive OAuth 3-legged login flow.
   123  	//
   124  	// Using this method requires specifying an OAuth client by passing ClientID
   125  	// and ClientSecret in Options when calling NewAuthenticator.
   126  	//
   127  	// Additionally, SilentLogin and OptionalLogin (i.e. non-interactive) login
   128  	// modes rely on a presence of a refresh token in the token cache, thus using
   129  	// these modes with UserCredentialsMethod also requires configured token
   130  	// cache (see SecretsDir field of Options).
   131  	UserCredentialsMethod Method = "UserCredentialsMethod"
   132  
   133  	// ServiceAccountMethod is used to authenticate as a service account using
   134  	// a private key.
   135  	//
   136  	// Callers of NewAuthenticator must pass either a path to a JSON file with
   137  	// service account key (as produced by Google Cloud Console) or a body of this
   138  	// JSON file. See ServiceAccountJSONPath and ServiceAccountJSON fields in
   139  	// Options.
   140  	//
   141  	// Using ServiceAccountJSONPath has an advantage: Authenticator always loads
   142  	// the private key from the file before refreshing the token, it allows to
   143  	// replace the key while the process is running.
   144  	ServiceAccountMethod Method = "ServiceAccountMethod"
   145  
   146  	// GCEMetadataMethod is used on Compute Engine to use tokens provided by
   147  	// Metadata server. See https://cloud.google.com/compute/docs/authentication
   148  	GCEMetadataMethod Method = "GCEMetadataMethod"
   149  
   150  	// LUCIContextMethod is used by LUCI-aware applications to fetch tokens though
   151  	// a local auth server (discoverable via "local_auth" key in LUCI_CONTEXT).
   152  	//
   153  	// This method is similar in spirit to GCEMetadataMethod: it uses some local
   154  	// HTTP server as a provider of OAuth access tokens, which gives an ambient
   155  	// authentication context to apps that use it.
   156  	//
   157  	// There are some big differences:
   158  	//  1. LUCIContextMethod supports minting tokens for multiple different set
   159  	//     of scopes, unlike GCE metadata server that always gives a token with
   160  	//     preconfigured scopes (set when the GCE instance was created).
   161  	//  2. LUCIContextMethod is not GCE-specific. It doesn't use magic link-local
   162  	//     IP address. It can run on any machine.
   163  	//  3. The access to the local auth server is controlled by file system
   164  	//     permissions of LUCI_CONTEXT file (there's a secret in this file).
   165  	//  4. There can be many local auth servers running at once (on different
   166  	//     ports). Useful for bringing up sub-contexts, in particular in
   167  	//     combination with ActAsServiceAccount ("sudo" mode) or for tests.
   168  	//
   169  	// See auth/integration/localauth package for the implementation of the server
   170  	// side of the protocol.
   171  	LUCIContextMethod Method = "LUCIContextMethod"
   172  )
   173  
   174  // LoginMode is used as enum in NewAuthenticator function.
   175  type LoginMode string
   176  
   177  const (
   178  	// InteractiveLogin indicates to Authenticator that it is okay to run an
   179  	// interactive login flow (via Login()) in Transport(), Client() or other
   180  	// factories if there's no cached token.
   181  	//
   182  	// This is typically used with UserCredentialsMethod to generate an OAuth
   183  	// refresh token and put it in the token cache at the start of the program,
   184  	// when grabbing a transport.
   185  	//
   186  	// Has no effect when used with service account credentials.
   187  	InteractiveLogin LoginMode = "InteractiveLogin"
   188  
   189  	// SilentLogin indicates to Authenticator that it must return a transport that
   190  	// implements authentication, but it is NOT OK to run interactive login flow
   191  	// to make it.
   192  	//
   193  	// Transport() and other factories will fail with ErrLoginRequired error if
   194  	// there's no cached token or one can't be generated on the fly in
   195  	// non-interactive mode. This may happen when using UserCredentialsMethod.
   196  	//
   197  	// It is always OK to use SilentLogin mode with service accounts credentials
   198  	// (ServiceAccountMethod mode), since no user interaction is necessary to
   199  	// generate an access token in this case.
   200  	SilentLogin LoginMode = "SilentLogin"
   201  
   202  	// OptionalLogin indicates to Authenticator that it should return a transport
   203  	// that implements authentication, but it is OK to return non-authenticating
   204  	// transport if there are no valid cached credentials.
   205  	//
   206  	// An interactive login flow will never be invoked. An unauthenticated client
   207  	// will be returned if no credentials are present.
   208  	//
   209  	// Can be used when making calls to backends that allow anonymous access. This
   210  	// is especially useful with UserCredentialsMethod: a user may start using
   211  	// the service right away (in anonymous mode), and later login (using Login()
   212  	// method or any other way of initializing credentials cache) to get more
   213  	// permissions.
   214  	//
   215  	// When used with ServiceAccountMethod it is identical to SilentLogin, since
   216  	// it makes no sense to ignore invalid service account credentials when the
   217  	// caller is explicitly asking the authenticator to use them.
   218  	//
   219  	// Has the original meaning when used with GCEMetadataMethod: it instructs to
   220  	// skip authentication if the token returned by GCE metadata service doesn't
   221  	// have all requested scopes.
   222  	OptionalLogin LoginMode = "OptionalLogin"
   223  )
   224  
   225  // Options are used by NewAuthenticator call.
   226  type Options struct {
   227  	// Transport is underlying round tripper to use for requests.
   228  	//
   229  	// Default: http.DefaultTransport.
   230  	Transport http.RoundTripper
   231  
   232  	// Method defines how to grab authentication tokens.
   233  	//
   234  	// Default: AutoSelectMethod.
   235  	Method Method
   236  
   237  	// UseIDTokens indicates to use ID tokens instead of access tokens.
   238  	//
   239  	// All methods that use or return OAuth access tokens would use ID tokens
   240  	// instead. This is useful, for example, when calling APIs that are hosted on
   241  	// Cloud Run or served via Cloud Endpoints.
   242  	//
   243  	// When setting to true, make sure to specify a correct Audience if the
   244  	// default one is not appropriate.
   245  	//
   246  	// When using UserCredentialsMethod implicitly appends OAuthScopeEmail to the
   247  	// list of OAuth scopes, since this scope is needed to get ID tokens in this
   248  	// mode.
   249  	//
   250  	// Default: false.
   251  	UseIDTokens bool
   252  
   253  	// Scopes is a list of OAuth scopes to request.
   254  	//
   255  	// Ignored when using ID tokens.
   256  	//
   257  	// Default: [OAuthScopeEmail].
   258  	Scopes []string
   259  
   260  	// Audience is the audience to put into ID tokens.
   261  	//
   262  	// It will become `aud` claim in the token. Should usually be some "https://"
   263  	// URL. Services that validate ID tokens check this field.
   264  	//
   265  	// Ignored when not using ID tokens or when using UserCredentialsMethod (the
   266  	// audience always matches OAuth2 ClientID in this case).
   267  	//
   268  	// Defaults: the value of ClientID to mimic UserCredentialsMethod.
   269  	Audience string
   270  
   271  	// ActAsServiceAccount is used to act as a specified service account email.
   272  	//
   273  	// When this option is set, there are two identities involved:
   274  	//  1. A service account identity specified by `ActAsServiceAccount`.
   275  	//  2. An identity conveyed by the authenticator options (via cached refresh
   276  	//     token, or via `ServiceAccountJSON`, or other similar ways), i.e. the
   277  	//     identity asserted by the authenticator in case `ActAsServiceAccount` is
   278  	//     not set. It is referred to below as the Actor identity.
   279  	//
   280  	// The resulting authenticator will produce access tokens for service account
   281  	// `ActAsServiceAccount`, using the Actor identity to generate them via some
   282  	// "acting" API.
   283  	//
   284  	// If `ActViaLUCIRealm` is not set, the "acting" API is Google Cloud IAM.
   285  	// The Actor credentials will internally be used to generate access tokens
   286  	// with IAM scope (see `OAuthScopeIAM`). These tokens will then be used to
   287  	// call `generateAccessToken` Cloud IAM RPC to obtain the final tokens that
   288  	// belong to the service account `ActAsServiceAccount`. This requires the
   289  	// Actor to have "iam.serviceAccounts.getAccessToken" Cloud IAM permission,
   290  	// which is usually granted via "Service Account Token Creator" IAM role.
   291  	//
   292  	// If `ActViaLUCIRealm` is set, the "acting" API is the LUCI Token Server.
   293  	// The Actor credentials will internally be used to generate access tokens
   294  	// with just email scope (see `OAuthScopeEmail`). These tokens will then be
   295  	// used to call `MintServiceAccountToken` RPC. This requires the following
   296  	// LUCI permissions in the realm specified by `ActViaLUCIRealm`:
   297  	//  1. The Actor needs "luci.serviceAccounts.mintToken" permission.
   298  	//  2. The target service account needs "luci.serviceAccounts.existInRealm"
   299  	//     permission.
   300  	//  3. The LUCI project the realm belongs to must be authorized to use the
   301  	//     target service account (currently via project_owned_accounts.cfg global
   302  	//     config file).
   303  	//
   304  	// Regardless of what "acting" API is used, `Scopes` parameter specifies what
   305  	// OAuth scopes to request for the final access token belonging to
   306  	// `ActAsServiceAccount`.
   307  	//
   308  	// Default: none.
   309  	ActAsServiceAccount string
   310  
   311  	// ActViaLUCIRealm is a LUCI Realm to use to authorize access to the service
   312  	// account when "acting" as it through a LUCI Token Server.
   313  	//
   314  	// See `ActAsServiceAccount` for a detailed explanation.
   315  	//
   316  	// Should have form "<project>:<realm>" (e.g. "chromium:ci"). It instructs
   317  	// the Token Server to lookup acting permissions in a realm named "<realm>",
   318  	// defined in `realms.cfg` project config file in a LUCI project named
   319  	// "<project>".
   320  	//
   321  	// Using this option requires `TokenServerHost` to be set.
   322  	//
   323  	// Default: none.
   324  	ActViaLUCIRealm string
   325  
   326  	// TokenServerHost is a hostname of a LUCI Token Server to use when acting.
   327  	//
   328  	// Used only when `ActAsServiceAccount` and `ActViaLUCIRealm` are set.
   329  	//
   330  	// Default: none.
   331  	TokenServerHost string
   332  
   333  	// ClientID is OAuth client ID to use with UserCredentialsMethod.
   334  	//
   335  	// See https://developers.google.com/identity/protocols/OAuth2InstalledApp
   336  	// (in particular everything related to "Desktop apps").
   337  	//
   338  	// Together with Scopes forms a cache key in the token cache, which in
   339  	// practical terms means there can be only one concurrently "logged in" user
   340  	// per [ClientID, Scopes] combination. So if multiple binaries use exact same
   341  	// ClientID and Scopes, they'll share credentials cache (a login in one app
   342  	// makes the user logged in in the other app too).
   343  	//
   344  	// If you don't want to share login information between tools, use separate
   345  	// ClientID or SecretsDir values.
   346  	//
   347  	// If not set, UserCredentialsMethod auth method will not work.
   348  	//
   349  	// Default: none.
   350  	ClientID string
   351  
   352  	// ClientSecret is OAuth client secret to use with UserCredentialsMethod.
   353  	//
   354  	// Default: none.
   355  	ClientSecret string
   356  
   357  	// LoginSessionsHost is a hostname of a service that implements LoginSessions
   358  	// pRPC service to use for performing interactive OAuth login flow instead
   359  	// of using OOB redirect URI.
   360  	//
   361  	// Matters only when using UserCredentialsMethod method. When used, the stdout
   362  	// must be attached to a real terminal (i.e. not redirected to a file or
   363  	// pipe). This is a precaution against using this login method on bots or from
   364  	// scripts which is never correct and can be dangerous.
   365  	//
   366  	// Default: none
   367  	LoginSessionsHost string
   368  
   369  	// ServiceAccountJSONPath is a path to a JSON blob with a private key to use.
   370  	//
   371  	// Can also be set to GCEServiceAccount (':gce') to indicate that the GCE VM
   372  	// service account should be used instead. Useful in CLI interfaces. This
   373  	// works only if Method is set to AutoSelectMethod (which is the default for
   374  	// most CLI apps). If GCEServiceAccount is used on a machine without GCE
   375  	// metadata server, authenticator methods return an error.
   376  	//
   377  	// Used only with ServiceAccountMethod.
   378  	ServiceAccountJSONPath string
   379  
   380  	// ServiceAccountJSON is a body of JSON key file to use.
   381  	//
   382  	// Overrides ServiceAccountJSONPath if given.
   383  	ServiceAccountJSON []byte
   384  
   385  	// GCEAccountName is an account name to query to fetch token for from metadata
   386  	// server when GCEMetadataMethod is used.
   387  	//
   388  	// If given account wasn't granted required set of scopes during instance
   389  	// creation time, Transport() call fails with ErrInsufficientAccess.
   390  	//
   391  	// Default: "default" account.
   392  	GCEAccountName string
   393  
   394  	// GCEAllowAsDefault indicates whether it is OK to pick GCE authentication
   395  	// method as default if no other methods apply.
   396  	//
   397  	// Effective only when running on GCE and Method is set to AutoSelectMethod.
   398  	//
   399  	// In theory using GCE metadata server for authentication when it is
   400  	// available looks attractive. In practice, especially if running in a
   401  	// heterogeneous fleet with a mix of GCE and non-GCE machines, automatically
   402  	// enabling GCE-based authentication is very surprising when it happens.
   403  	//
   404  	// Default: false (don't "sniff" GCE environment).
   405  	GCEAllowAsDefault bool
   406  
   407  	// SecretsDir can be used to set the path to a directory where tokens
   408  	// are cached.
   409  	//
   410  	// If not set, tokens will be cached only in the process memory. For refresh
   411  	// tokens it means the user would have to go through the login process each
   412  	// time process is started. For service account tokens it means there'll be
   413  	// HTTP round trip to OAuth backend to generate access token each time the
   414  	// process is started.
   415  	SecretsDir string
   416  
   417  	// DisableMonitoring can be used to disable the monitoring instrumentation.
   418  	//
   419  	// The transport produced by this authenticator sends tsmon metrics IFF:
   420  	//  1. DisableMonitoring is false (default).
   421  	//  2. The context passed to 'NewAuthenticator' has monitoring initialized.
   422  	DisableMonitoring bool
   423  
   424  	// MonitorAs is used for 'client' field of monitoring metrics.
   425  	//
   426  	// The default is 'luci-go'.
   427  	MonitorAs string
   428  
   429  	// MinTokenLifetime defines a minimally acceptable lifetime of access tokens
   430  	// generated internally by authenticating http.RoundTripper, TokenSource and
   431  	// PerRPCCredentials.
   432  	//
   433  	// Not used when GetAccessToken is called directly (it accepts this parameter
   434  	// as an argument).
   435  	//
   436  	// The default is 2 min. There's rarely a need to change it and using smaller
   437  	// values may be dangerous (e.g. if the request gets stuck somewhere or the
   438  	// token is cached incorrectly it may expire before it is checked).
   439  	MinTokenLifetime time.Duration
   440  
   441  	// testingCache is used in unit tests.
   442  	testingCache internal.TokenCache
   443  	// testingBaseTokenProvider is used in unit tests.
   444  	testingBaseTokenProvider internal.TokenProvider
   445  	// testingIAMTokenProvider is used in unit tests.
   446  	testingIAMTokenProvider internal.TokenProvider
   447  }
   448  
   449  // PopulateDefaults populates empty fields of `opts` with default values.
   450  //
   451  // It is called automatically by NewAuthenticator. Use it only if you need to
   452  // normalize and examine auth.Options before passing them to NewAuthenticator.
   453  func (opts *Options) PopulateDefaults() {
   454  	// Set the default scope, sort and dedup scopes.
   455  	if len(opts.Scopes) == 0 {
   456  		opts.Scopes = []string{OAuthScopeEmail} // also implies "openid"
   457  	}
   458  	opts.Scopes = normalizeScopes(opts.Scopes)
   459  
   460  	// Fill in blanks with default values.
   461  	if opts.Audience == "" {
   462  		opts.Audience = opts.ClientID
   463  	}
   464  	if opts.GCEAccountName == "" {
   465  		opts.GCEAccountName = "default"
   466  	}
   467  	if opts.Transport == nil {
   468  		opts.Transport = http.DefaultTransport
   469  	}
   470  	if opts.MinTokenLifetime == 0 {
   471  		opts.MinTokenLifetime = 2 * time.Minute
   472  	}
   473  
   474  	// TODO(vadimsh): Check SecretsDir permissions. It should be 0700.
   475  	if opts.SecretsDir != "" && !filepath.IsAbs(opts.SecretsDir) {
   476  		var err error
   477  		opts.SecretsDir, err = filepath.Abs(opts.SecretsDir)
   478  		if err != nil {
   479  			panic(errors.Annotate(err, "failed to get abs path to token cache dir").Err())
   480  		}
   481  	}
   482  }
   483  
   484  // SelectBestMethod returns a most appropriate authentication method for the
   485  // given set of options and the current execution environment.
   486  //
   487  // Invoked by Authenticator if AutoSelectMethod is passed as Method in Options.
   488  // It picks the first applicable method in this order:
   489  //   - ServiceAccountMethod (if the service account private key is configured).
   490  //   - LUCIContextMethod (if running inside LUCI_CONTEXT with an auth server).
   491  //   - GCEMetadataMethod (if running on GCE and GCEAllowAsDefault is true).
   492  //   - UserCredentialsMethod (if no other method applies).
   493  //
   494  // Beware: it may do relatively heavy calls on first usage (to detect GCE
   495  // environment). Fast after that.
   496  func SelectBestMethod(ctx context.Context, opts Options) Method {
   497  	// Asked to use JSON private key.
   498  	if opts.ServiceAccountJSONPath != "" || len(opts.ServiceAccountJSON) != 0 {
   499  		if opts.ServiceAccountJSONPath == GCEServiceAccount {
   500  			return GCEMetadataMethod
   501  		}
   502  		return ServiceAccountMethod
   503  	}
   504  
   505  	// Have a local auth server and an account we are allowed to pick by default.
   506  	// If no default account is given, don't automatically pick up this method.
   507  	if la := lucictx.GetLocalAuth(ctx); la != nil && la.DefaultAccountId != "" {
   508  		return LUCIContextMethod
   509  	}
   510  
   511  	// Running on GCE and callers are fine with automagically picking up GCE
   512  	// metadata server.
   513  	if opts.GCEAllowAsDefault && metadata.OnGCE() {
   514  		return GCEMetadataMethod
   515  	}
   516  
   517  	return UserCredentialsMethod
   518  }
   519  
   520  // Authenticator is a factory for http.RoundTripper objects that know how to use
   521  // cached credentials and how to send monitoring metrics (if tsmon package was
   522  // imported).
   523  //
   524  // Authenticator also knows how to run interactive login flow, if required.
   525  type Authenticator struct {
   526  	// Immutable members.
   527  	loginMode LoginMode
   528  	opts      *Options
   529  	transport http.RoundTripper
   530  	ctx       context.Context
   531  
   532  	// Mutable members.
   533  	lock sync.RWMutex
   534  	err  error
   535  
   536  	// baseToken is a token (and its provider and cache) whose possession is
   537  	// sufficient to get the final access token used for authentication of user
   538  	// calls (see 'authToken' below).
   539  	//
   540  	// Methods like 'CheckLoginRequired' check that the base token exists in the
   541  	// cache or can be generated on the fly.
   542  	//
   543  	// In actor mode, the base token has scopes necessary for the corresponding
   544  	// acting API to work (e.g. IAM scope when using Cloud's generateAccessToken).
   545  	// The base token is also always using whatever auth method was specified by
   546  	// Options.Method.
   547  	//
   548  	// In non-actor mode, baseToken coincides with authToken: both point to the
   549  	// exact same struct.
   550  	baseToken *tokenWithProvider
   551  
   552  	// authToken is a token (and its provider and cache) that is actually used for
   553  	// authentication of user calls.
   554  	//
   555  	// It is a token returned by 'GetAccessToken'. It is always scoped to 'Scopes'
   556  	// list, as passed to NewAuthenticator via Options.
   557  	//
   558  	// In actor mode, it is derived from the base token by using some "acting" API
   559  	// (which one depends on Options, see ActAsServiceAccount comment). This
   560  	// process is non-interactive and thus can always be performed as long as we
   561  	// have the base token.
   562  	//
   563  	// In non-actor mode it is the main token generated by the authenticator. In
   564  	// this case it coincides with baseToken: both point to the exact same object.
   565  	authToken *tokenWithProvider
   566  }
   567  
   568  // NewAuthenticator returns a new instance of Authenticator given its options.
   569  //
   570  // The authenticator is essentially a factory for http.RoundTripper that knows
   571  // how to use and update cached credentials tokens. It is bound to the given
   572  // context: uses its logger, clock and deadline.
   573  func NewAuthenticator(ctx context.Context, loginMode LoginMode, opts Options) *Authenticator {
   574  	opts.PopulateDefaults()
   575  
   576  	// See ensureInitialized for the rest of the initialization.
   577  	auth := &Authenticator{
   578  		ctx:       ctx,
   579  		loginMode: loginMode,
   580  		opts:      &opts,
   581  	}
   582  	auth.transport = NewModifyingTransport(opts.Transport, auth.authTokenInjector)
   583  
   584  	// Include the token refresh time into the monitored request time.
   585  	if globalInstrumentTransport != nil && !opts.DisableMonitoring {
   586  		monitorAs := opts.MonitorAs
   587  		if monitorAs == "" {
   588  			monitorAs = "luci-go"
   589  		}
   590  		instrumented := globalInstrumentTransport(ctx, auth.transport, monitorAs)
   591  		if instrumented != auth.transport {
   592  			logging.Debugf(ctx, "Enabling monitoring instrumentation (client == %q)", monitorAs)
   593  			auth.transport = instrumented
   594  		}
   595  	}
   596  
   597  	return auth
   598  }
   599  
   600  // Transport optionally performs a login and returns http.RoundTripper.
   601  //
   602  // It is a high level wrapper around CheckLoginRequired() and Login() calls. See
   603  // documentation for LoginMode for more details.
   604  func (a *Authenticator) Transport() (http.RoundTripper, error) {
   605  	switch useAuth, err := a.doLoginIfRequired(false); {
   606  	case err != nil:
   607  		return nil, err
   608  	case useAuth:
   609  		return a.transport, nil // token-injecting transport
   610  	default:
   611  		return a.opts.Transport, nil // original non-authenticating transport
   612  	}
   613  }
   614  
   615  // Client optionally performs a login and returns http.Client.
   616  //
   617  // It uses transport returned by Transport(). See documentation for LoginMode
   618  // for more details.
   619  func (a *Authenticator) Client() (*http.Client, error) {
   620  	transport, err := a.Transport()
   621  	if err != nil {
   622  		return nil, err
   623  	}
   624  	return &http.Client{Transport: transport}, nil
   625  }
   626  
   627  // TokenSource optionally performs a login and returns oauth2.TokenSource.
   628  //
   629  // Can be used for interoperability with libraries that use golang.org/x/oauth2.
   630  //
   631  // It doesn't support 'OptionalLogin' mode, since oauth2.TokenSource must return
   632  // some token. Otherwise its logic is similar to Transport(). In particular it
   633  // may return ErrLoginRequired if interactive login is required, but the
   634  // authenticator is in the silent mode. See LoginMode enum for more details.
   635  func (a *Authenticator) TokenSource() (oauth2.TokenSource, error) {
   636  	if _, err := a.doLoginIfRequired(true); err != nil {
   637  		return nil, err
   638  	}
   639  	return tokenSource{a}, nil
   640  }
   641  
   642  // PerRPCCredentials optionally performs a login and returns PerRPCCredentials.
   643  //
   644  // It can be used to authenticate outbound gPRC RPC's.
   645  //
   646  // Has same logic as Transport(), in particular supports OptionalLogin mode.
   647  // See Transport() for more details.
   648  func (a *Authenticator) PerRPCCredentials() (credentials.PerRPCCredentials, error) {
   649  	switch useAuth, err := a.doLoginIfRequired(false); {
   650  	case err != nil:
   651  		return nil, err
   652  	case useAuth:
   653  		return perRPCCreds{a}, nil // token-injecting PerRPCCredentials
   654  	default:
   655  		return perRPCCreds{}, nil // noop PerRPCCredentials
   656  	}
   657  }
   658  
   659  // GetAccessToken returns a valid token with the specified minimum lifetime.
   660  //
   661  // Returns either an access token or an ID token based on UseIDTokens
   662  // authenticator option.
   663  //
   664  // Does not interact with the user. May return ErrLoginRequired.
   665  func (a *Authenticator) GetAccessToken(lifetime time.Duration) (*oauth2.Token, error) {
   666  	tok, err := a.currentToken()
   667  	if err != nil {
   668  		return nil, err
   669  	}
   670  
   671  	// If interested in using ID tokens, but there's no ID token cached yet, force
   672  	// a refresh. Note that auth methods that don't support ID tokens at all
   673  	// return internal.NoIDToken in place of the ID token (and it is != ""). This
   674  	// case is handled below.
   675  	forceRefresh := tok != nil && a.opts.UseIDTokens && tok.IDToken == ""
   676  
   677  	// Impose some arbitrary limit, since <= 0 lifetime won't work.
   678  	if lifetime < time.Second {
   679  		lifetime = time.Second
   680  	}
   681  
   682  	if tok == nil || forceRefresh || internal.TokenExpiresInRnd(a.ctx, tok, lifetime) {
   683  		// Give 5 sec extra to make sure callers definitely receive a token that
   684  		// has at least 'lifetime' seconds of life left. Without this margin, we
   685  		// can get into an unlucky situation where the token is valid here, but
   686  		// no longer valid (has fewer than 'lifetime' life left) up the stack, due
   687  		// to the passage of time.
   688  		var err error
   689  		tok, err = a.refreshToken(tok, lifetime+5*time.Second)
   690  		if err != nil {
   691  			if err == ErrLoginRequired {
   692  				return nil, err
   693  			}
   694  			return nil, errors.Annotate(err, "failed to refresh auth token").Err()
   695  		}
   696  		// Note: no randomization here. It is a sanity check that verifies
   697  		// refreshToken did its job.
   698  		if internal.TokenExpiresIn(a.ctx, tok, lifetime) {
   699  			return nil, errors.Reason("failed to refresh auth token: still stale even after refresh").Err()
   700  		}
   701  	}
   702  
   703  	if a.opts.UseIDTokens {
   704  		if tok.IDToken == "" || tok.IDToken == internal.NoIDToken {
   705  			return nil, ErrNoIDToken
   706  		}
   707  		return &oauth2.Token{
   708  			AccessToken: tok.IDToken,
   709  			Expiry:      tok.Expiry,
   710  			TokenType:   "Bearer",
   711  		}, nil
   712  	}
   713  
   714  	// This should not be happening, but in case it does, better to return an
   715  	// error instead of a phony access token.
   716  	if tok.Token.AccessToken == internal.NoAccessToken {
   717  		return nil, ErrNoAccessToken
   718  	}
   719  
   720  	return &tok.Token, nil
   721  }
   722  
   723  // GetEmail returns an email associated with the credentials.
   724  //
   725  // In most cases this is a fast call that hits the cache. In some rare cases it
   726  // may do an RPC to the Token Info endpoint to grab an email associated with the
   727  // token.
   728  //
   729  // Returns ErrNoEmail if the email is not available. This may happen, for
   730  // example, when using a refresh token that doesn't have 'userinfo.email' scope.
   731  // Callers must expect this error to show up and should prepare a fallback.
   732  //
   733  // Returns an error if the email can't be fetched due to some other transient
   734  // or fatal error. In particular, returns ErrLoginRequired if interactive login
   735  // is required to get the token in the first place.
   736  func (a *Authenticator) GetEmail() (string, error) {
   737  	// Grab last known token and its associated email. Note that this call also
   738  	// initializes the guts of the authenticator, including a.authToken.
   739  	tok, err := a.currentToken()
   740  	switch {
   741  	case err != nil:
   742  		return "", err
   743  	case tok != nil && tok.Email == internal.NoEmail:
   744  		return "", ErrNoEmail
   745  	case tok != nil && tok.Email != internal.UnknownEmail:
   746  		return tok.Email, nil
   747  	}
   748  
   749  	// There's no token cached yet (and thus email is not known). First try to ask
   750  	// the provider for email only. Most providers can return it without doing any
   751  	// RPCs or heavy calls. If this is not supported, resort to a heavier code
   752  	// paths that actually refreshes the token and grabs its email along the way.
   753  	a.lock.RLock()
   754  	email := a.authToken.provider.Email()
   755  	a.lock.RUnlock()
   756  	switch {
   757  	case email == internal.NoEmail:
   758  		return "", ErrNoEmail
   759  	case email != "":
   760  		return email, nil
   761  	}
   762  
   763  	// The provider doesn't know the email. We need a forceful token refresh to
   764  	// grab it (or discover it is NoEmail). This is relatively rare. It happens
   765  	// only when using UserAuth TokenProvider and there's no cached token at all
   766  	// or it is in old format that don't have email field.
   767  	//
   768  	// Pass -1 as lifetime to force trigger the refresh right now.
   769  	tok, err = a.refreshToken(tok, -1)
   770  	switch {
   771  	case err == ErrLoginRequired:
   772  		return "", err
   773  	case err != nil:
   774  		return "", errors.Annotate(err, "failed to refresh auth token").Err()
   775  	case tok.Email == internal.NoEmail:
   776  		return "", ErrNoEmail
   777  	case tok.Email == internal.UnknownEmail: // this must not happen, but let's be cautious
   778  		return "", errors.Reason("internal error when fetching the email, see logs").Err()
   779  	default:
   780  		return tok.Email, nil
   781  	}
   782  }
   783  
   784  // CheckLoginRequired decides whether an interactive login is required.
   785  //
   786  // It examines the token cache and the configured authentication method to
   787  // figure out whether we can attempt to grab an access token without involving
   788  // the user interaction.
   789  //
   790  // Note: it does not check that the cached refresh token is still valid (i.e.
   791  // not revoked). A revoked token will result in ErrLoginRequired error on a
   792  // first attempt to use it.
   793  //
   794  // Returns:
   795  //   - nil if we have a valid cached token or can mint one on the fly.
   796  //   - ErrLoginRequired if we have no cached token and need to bother the user.
   797  //   - ErrInsufficientAccess if the configured auth method can't mint the token
   798  //     we require (e.g. when using GCE method and the instance doesn't have all
   799  //     requested OAuth scopes).
   800  //   - Generic error on other unexpected errors.
   801  func (a *Authenticator) CheckLoginRequired() error {
   802  	a.lock.Lock()
   803  	defer a.lock.Unlock()
   804  
   805  	if err := a.ensureInitialized(); err != nil {
   806  		return err
   807  	}
   808  
   809  	// No cached base token and the token provider requires interaction with the
   810  	// user: need to login. Only non-interactive token providers are allowed to
   811  	// mint tokens on the fly, see refreshToken.
   812  	if a.baseToken.token == nil && a.baseToken.provider.RequiresInteraction() {
   813  		return ErrLoginRequired
   814  	}
   815  
   816  	return nil
   817  }
   818  
   819  // Login perform an interaction with the user to get a long term refresh token
   820  // and cache it.
   821  //
   822  // Blocks for user input, can use stdin. It overwrites currently cached
   823  // credentials, if any.
   824  //
   825  // When used with non-interactive token providers (e.g. based on service
   826  // accounts), just clears the cached access token, so next the next
   827  // authenticated call gets a fresh one.
   828  func (a *Authenticator) Login() error {
   829  	a.lock.Lock()
   830  	defer a.lock.Unlock()
   831  
   832  	err := a.ensureInitialized()
   833  	if err != nil {
   834  		return err
   835  	}
   836  
   837  	// Remove any cached tokens to trigger full relogin.
   838  	if err := a.purgeCredentialsCacheLocked(); err != nil {
   839  		return err
   840  	}
   841  
   842  	if !a.baseToken.provider.RequiresInteraction() {
   843  		return nil // can mint the token on the fly, no need for login
   844  	}
   845  
   846  	// Create an initial base token. This may require interaction with a user. Do
   847  	// not do retries here, since Login is called when the user is looking, let
   848  	// the user do the retries (since if MintToken() interacts with the user,
   849  	// retrying it automatically will be extra confusing).
   850  	a.baseToken.token, err = a.baseToken.provider.MintToken(a.ctx, nil)
   851  	if err != nil {
   852  		return err
   853  	}
   854  
   855  	// Store the initial token in the cache. Don't abort if it fails, the token
   856  	// is still usable from the memory.
   857  	if err := a.baseToken.putToCache(a.ctx); err != nil {
   858  		logging.Warningf(a.ctx, "Failed to write token to cache: %s", err)
   859  	}
   860  
   861  	return nil
   862  }
   863  
   864  // PurgeCredentialsCache removes cached tokens.
   865  //
   866  // Does not revoke them!
   867  func (a *Authenticator) PurgeCredentialsCache() error {
   868  	a.lock.Lock()
   869  	defer a.lock.Unlock()
   870  	if err := a.ensureInitialized(); err != nil {
   871  		return err
   872  	}
   873  	return a.purgeCredentialsCacheLocked()
   874  }
   875  
   876  func (a *Authenticator) purgeCredentialsCacheLocked() error {
   877  	// No need to purge twice if baseToken == authToken, which is the case in
   878  	// non-actor mode.
   879  	var merr errors.MultiError
   880  	if a.baseToken != a.authToken {
   881  		merr = errors.NewMultiError(
   882  			a.baseToken.purgeToken(a.ctx),
   883  			a.authToken.purgeToken(a.ctx))
   884  	} else {
   885  		merr = errors.NewMultiError(a.baseToken.purgeToken(a.ctx))
   886  	}
   887  
   888  	switch total, first := merr.Summary(); {
   889  	case total == 0:
   890  		return nil
   891  	case total == 1:
   892  		return first
   893  	default:
   894  		return merr
   895  	}
   896  }
   897  
   898  ////////////////////////////////////////////////////////////////////////////////
   899  // credentials.PerRPCCredentials implementation.
   900  
   901  type perRPCCreds struct {
   902  	a *Authenticator
   903  }
   904  
   905  func (creds perRPCCreds) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
   906  	if len(uri) == 0 {
   907  		panic("perRPCCreds: no URI given")
   908  	}
   909  	if creds.a == nil {
   910  		return nil, nil
   911  	}
   912  	tok, err := creds.a.GetAccessToken(creds.a.opts.MinTokenLifetime)
   913  	if err != nil {
   914  		return nil, err
   915  	}
   916  	return map[string]string{
   917  		"Authorization": tok.Type() + " " + tok.AccessToken,
   918  	}, nil
   919  }
   920  
   921  func (creds perRPCCreds) RequireTransportSecurity() bool { return true }
   922  
   923  ////////////////////////////////////////////////////////////////////////////////
   924  // oauth2.TokenSource implementation.
   925  
   926  type tokenSource struct {
   927  	a *Authenticator
   928  }
   929  
   930  // Token is part of oauth2.TokenSource interface.
   931  func (s tokenSource) Token() (*oauth2.Token, error) {
   932  	return s.a.GetAccessToken(s.a.opts.MinTokenLifetime)
   933  }
   934  
   935  ////////////////////////////////////////////////////////////////////////////////
   936  // Authenticator private methods.
   937  
   938  // actingMode returns possible ways the authenticator can "act" as an account.
   939  type actingMode int
   940  
   941  const (
   942  	actingModeNone actingMode = 0
   943  	actingModeIAM  actingMode = 1
   944  	actingModeLUCI actingMode = 2
   945  )
   946  
   947  // actingMode returns an acting mode based on Options.
   948  func (a *Authenticator) actingMode() actingMode {
   949  	switch {
   950  	case a.opts.ActAsServiceAccount == "":
   951  		return actingModeNone
   952  	case a.opts.ActViaLUCIRealm != "":
   953  		return actingModeLUCI
   954  	default:
   955  		return actingModeIAM
   956  	}
   957  }
   958  
   959  // checkInitialized is (true, <err>) if initialization happened (successfully or
   960  // not) of (false, nil) if not.
   961  func (a *Authenticator) checkInitialized() (bool, error) {
   962  	if a.err != nil || a.baseToken != nil {
   963  		return true, a.err
   964  	}
   965  	return false, nil
   966  }
   967  
   968  // ensureInitialized instantiates TokenProvider and reads token from cache.
   969  //
   970  // It is supposed to be called under the lock.
   971  func (a *Authenticator) ensureInitialized() error {
   972  	// Already initialized (successfully or not)?
   973  	if initialized, err := a.checkInitialized(); initialized {
   974  		return err
   975  	}
   976  
   977  	// ActViaLUCIRealm makes sense only with ActAsServiceAccount.
   978  	if a.opts.ActViaLUCIRealm != "" && a.opts.ActAsServiceAccount == "" {
   979  		a.err = ErrBadOptions
   980  		return a.err
   981  	}
   982  
   983  	// SelectBestMethod may do heavy calls like talking to GCE metadata server,
   984  	// call it lazily here rather than in NewAuthenticator.
   985  	if a.opts.Method == AutoSelectMethod {
   986  		a.opts.Method = SelectBestMethod(a.ctx, *a.opts)
   987  	}
   988  
   989  	// In Actor mode, switch the base token to have scopes required to call
   990  	// the API used to generate target auth tokens. In non-actor mode, the base
   991  	// token is also the target auth token, so scope it to whatever scopes were
   992  	// requested via Options.
   993  	var scopes []string
   994  	var useIDTokens bool
   995  	switch a.actingMode() {
   996  	case actingModeNone:
   997  		scopes = a.opts.Scopes
   998  		useIDTokens = a.opts.UseIDTokens
   999  		// To get ID tokens when using end-user credentials we need userinfo scope.
  1000  		if useIDTokens && a.opts.Method == UserCredentialsMethod {
  1001  			if !slices.Contains(scopes, OAuthScopeEmail) {
  1002  				scopes = normalizeScopes(append(scopes, OAuthScopeEmail))
  1003  			}
  1004  		}
  1005  	case actingModeIAM:
  1006  		scopes = []string{OAuthScopeIAM}
  1007  		useIDTokens = false // IAM uses OAuth tokens
  1008  	case actingModeLUCI:
  1009  		scopes = []string{OAuthScopeEmail}
  1010  		useIDTokens = false // LUCI currently uses OAuth tokens
  1011  	default:
  1012  		panic("impossible")
  1013  	}
  1014  	a.baseToken = &tokenWithProvider{}
  1015  	a.baseToken.provider, a.err = makeBaseTokenProvider(a.ctx, a.opts, scopes, useIDTokens)
  1016  	if a.err != nil {
  1017  		return a.err // note: this can be ErrInsufficientAccess
  1018  	}
  1019  
  1020  	// In non-actor mode, the token we must check in 'CheckLoginRequired' is the
  1021  	// same as returned by 'GetAccessToken'. In actor mode, they are different.
  1022  	// See comments for 'baseToken' and 'authToken'.
  1023  	switch a.actingMode() {
  1024  	case actingModeNone:
  1025  		a.authToken = a.baseToken
  1026  	case actingModeIAM:
  1027  		a.authToken = &tokenWithProvider{}
  1028  		a.authToken.provider, a.err = makeIAMTokenProvider(a.ctx, a.opts)
  1029  	case actingModeLUCI:
  1030  		a.authToken = &tokenWithProvider{}
  1031  		a.authToken.provider, a.err = makeLUCITokenProvider(a.ctx, a.opts)
  1032  	default:
  1033  		panic("impossible")
  1034  	}
  1035  	if a.err != nil {
  1036  		return a.err
  1037  	}
  1038  
  1039  	// Initialize the token cache. Use the disk cache only if SecretsDir is given
  1040  	// and any of the providers is not "lightweight" (so it makes sense to
  1041  	// actually hit the disk, rather then call the provider each time new token is
  1042  	// needed).
  1043  	if a.opts.testingCache != nil {
  1044  		a.baseToken.cache = a.opts.testingCache
  1045  		a.authToken.cache = a.opts.testingCache
  1046  	} else {
  1047  		cache := internal.ProcTokenCache
  1048  		if !a.baseToken.provider.Lightweight() || !a.authToken.provider.Lightweight() {
  1049  			if a.opts.SecretsDir != "" {
  1050  				cache = &internal.DiskTokenCache{
  1051  					Context:    a.ctx,
  1052  					SecretsDir: a.opts.SecretsDir,
  1053  				}
  1054  			} else {
  1055  				logging.Warningf(a.ctx, "Disabling auth disk token cache. Not configured.")
  1056  			}
  1057  		}
  1058  		// Use the disk cache only for non-lightweight providers to avoid
  1059  		// unnecessarily leaks of tokens to the disk.
  1060  		if a.baseToken.provider.Lightweight() {
  1061  			a.baseToken.cache = internal.ProcTokenCache
  1062  		} else {
  1063  			a.baseToken.cache = cache
  1064  		}
  1065  		if a.authToken.provider.Lightweight() {
  1066  			a.authToken.cache = internal.ProcTokenCache
  1067  		} else {
  1068  			a.authToken.cache = cache
  1069  		}
  1070  	}
  1071  
  1072  	// Interactive providers need to know whether there's a cached token (to ask
  1073  	// to run interactive login if there's none). Non-interactive providers do not
  1074  	// care about state of the cache that much (they know how to update it
  1075  	// themselves). So examine the cache here only when using interactive
  1076  	// provider. Non interactive providers will do it lazily on a first
  1077  	// refreshToken(...) call.
  1078  	if a.baseToken.provider.RequiresInteraction() {
  1079  		// Broken token cache is not a fatal error. So just log it and forget, a new
  1080  		// token will be minted in Login.
  1081  		if err := a.baseToken.fetchFromCache(a.ctx); err != nil {
  1082  			logging.Warningf(a.ctx, "Failed to read auth token from cache: %s", err)
  1083  		}
  1084  	}
  1085  
  1086  	// Note: a.authToken.provider is either equal to a.baseToken.provider (if not
  1087  	// using actor mode), or (when using actor mode) it doesn't require
  1088  	// interaction (because all "acting" providers are non-interactive). So don't
  1089  	// bother fetching 'authToken' from the cache. It will be fetched lazily on
  1090  	// the first use.
  1091  
  1092  	return nil
  1093  }
  1094  
  1095  // doLoginIfRequired optionally performs an interactive login.
  1096  //
  1097  // This is the main place where LoginMode handling is performed. Used by various
  1098  // factories (Transport, PerRPCCredentials, TokenSource, ...).
  1099  //
  1100  // If requiresAuth is false, we respect OptionalLogin mode. If true - we treat
  1101  // OptionalLogin mode as SilentLogin: some authentication mechanisms (like
  1102  // oauth2.TokenSource) require valid tokens no matter what. The corresponding
  1103  // factories set requiresAuth to true.
  1104  //
  1105  // Returns:
  1106  //
  1107  //	(true, nil) if successfully initialized the authenticator with some token.
  1108  //	(false, nil) to disable authentication (for OptionalLogin mode).
  1109  //	(false, err) on errors.
  1110  func (a *Authenticator) doLoginIfRequired(requiresAuth bool) (useAuth bool, err error) {
  1111  	err = a.CheckLoginRequired() // also initializes guts for effectiveLoginMode()
  1112  	effectiveMode := a.effectiveLoginMode()
  1113  	if requiresAuth && effectiveMode == OptionalLogin {
  1114  		effectiveMode = SilentLogin
  1115  	}
  1116  	switch {
  1117  	case err == nil:
  1118  		return true, nil // have a valid cached base token
  1119  	case err == ErrInsufficientAccess && effectiveMode == OptionalLogin:
  1120  		return false, nil // have the base token, but it doesn't have enough scopes
  1121  	case err != ErrLoginRequired:
  1122  		return false, err // some error we can't handle (we handle only ErrLoginRequired)
  1123  	case effectiveMode == SilentLogin:
  1124  		return false, ErrLoginRequired // can't run Login in SilentLogin mode
  1125  	case effectiveMode == OptionalLogin:
  1126  		return false, nil // we can skip auth in OptionalLogin if we have no token
  1127  	case effectiveMode != InteractiveLogin:
  1128  		return false, errors.Reason("invalid mode argument: %s", effectiveMode).Err()
  1129  	}
  1130  	if err := a.Login(); err != nil {
  1131  		return false, err
  1132  	}
  1133  	return true, nil
  1134  }
  1135  
  1136  // effectiveLoginMode returns a login mode to use, considering what kind of a
  1137  // token provider is being used.
  1138  //
  1139  // See comments for OptionalLogin for more info. The gist of it: we treat
  1140  // OptionalLogin as SilentLogin when using a service account private key.
  1141  func (a *Authenticator) effectiveLoginMode() (lm LoginMode) {
  1142  	// a.opts.Method is modified under a lock, need to grab the lock to avoid a
  1143  	// race. Note that a.loginMode is immutable and can be read outside the
  1144  	// lock. We skip the locking if we know for sure that the return value will be
  1145  	// same as a.loginMode (which is the case for a.loginMode != OptionalLogin).
  1146  	lm = a.loginMode
  1147  	if lm == OptionalLogin {
  1148  		a.lock.RLock()
  1149  		if a.opts.Method == ServiceAccountMethod {
  1150  			lm = SilentLogin
  1151  		}
  1152  		a.lock.RUnlock()
  1153  	}
  1154  	return
  1155  }
  1156  
  1157  // currentToken returns last known authentication token (or nil).
  1158  //
  1159  // If the token is not loaded yet, will attempt to load it from the on-disk
  1160  // cache. Returns nil if it's not there.
  1161  //
  1162  // It lock a.lock inside. It MUST NOT be called when a.lock is held. It will
  1163  // lazily call 'ensureInitialized' if necessary, returning its error.
  1164  func (a *Authenticator) currentToken() (tok *internal.Token, err error) {
  1165  	a.lock.RLock()
  1166  	initialized, err := a.checkInitialized()
  1167  	if initialized && err == nil {
  1168  		tok = a.authToken.token
  1169  	}
  1170  	a.lock.RUnlock()
  1171  	if err != nil {
  1172  		return
  1173  	}
  1174  
  1175  	if !initialized || tok == nil {
  1176  		a.lock.Lock()
  1177  		defer a.lock.Unlock()
  1178  
  1179  		if !initialized {
  1180  			if err = a.ensureInitialized(); err != nil {
  1181  				return
  1182  			}
  1183  			tok = a.authToken.token
  1184  		}
  1185  
  1186  		if tok == nil {
  1187  			// Reading the token from cache is best effort. A broken cache is treated
  1188  			// like a cache miss.
  1189  			if cacheErr := a.authToken.fetchFromCache(a.ctx); cacheErr != nil {
  1190  				logging.Warningf(a.ctx, "Failed to read auth token from cache: %s", cacheErr)
  1191  			}
  1192  			tok = a.authToken.token
  1193  		}
  1194  	}
  1195  
  1196  	return
  1197  }
  1198  
  1199  // refreshToken compares current auth token to 'prev' and launches token refresh
  1200  // procedure if they still match.
  1201  //
  1202  // Returns a refreshed token (if a refresh procedure happened) or the current
  1203  // token, if it's already different from 'prev'. Acts as "Compare-And-Swap"
  1204  // where "Swap" is a token refresh procedure.
  1205  //
  1206  // If the token can't be refreshed (e.g. the base token or the credentials were
  1207  // revoked), sets the current auth token to nil and returns an error.
  1208  func (a *Authenticator) refreshToken(prev *internal.Token, lifetime time.Duration) (*internal.Token, error) {
  1209  	return a.authToken.compareAndRefresh(a.ctx, compareAndRefreshOp{
  1210  		lock:     &a.lock,
  1211  		prev:     prev,
  1212  		lifetime: lifetime,
  1213  		refreshCb: func(ctx context.Context, prev *internal.Token) (*internal.Token, error) {
  1214  			// In Actor mode, need to make sure we have a sufficiently fresh base
  1215  			// token first, since it's needed to call "acting" API to get a new auth
  1216  			// token for the target service account. 1 min should be more than enough
  1217  			// to make an RPC.
  1218  			var base *internal.Token
  1219  			if a.actingMode() != actingModeNone {
  1220  				var err error
  1221  				if base, err = a.getBaseTokenLocked(ctx, time.Minute); err != nil {
  1222  					return nil, err
  1223  				}
  1224  			}
  1225  			return a.authToken.renewToken(ctx, prev, base)
  1226  		},
  1227  	})
  1228  }
  1229  
  1230  // getBaseTokenLocked is used to get an actor token when running in actor mode.
  1231  //
  1232  // It is called with a.lock locked.
  1233  func (a *Authenticator) getBaseTokenLocked(ctx context.Context, lifetime time.Duration) (*internal.Token, error) {
  1234  	// Already have a good token?
  1235  	if !internal.TokenExpiresInRnd(ctx, a.baseToken.token, lifetime) {
  1236  		return a.baseToken.token, nil
  1237  	}
  1238  
  1239  	// Need to make one.
  1240  	return a.baseToken.compareAndRefresh(ctx, compareAndRefreshOp{
  1241  		lock:     nil, // already holding the lock
  1242  		prev:     a.baseToken.token,
  1243  		lifetime: lifetime,
  1244  		refreshCb: func(ctx context.Context, prev *internal.Token) (*internal.Token, error) {
  1245  			return a.baseToken.renewToken(ctx, prev, nil)
  1246  		},
  1247  	})
  1248  }
  1249  
  1250  ////////////////////////////////////////////////////////////////////////////////
  1251  // Transport implementation.
  1252  
  1253  // authTokenInjector injects an authentication token into request headers.
  1254  //
  1255  // Used as a callback for NewModifyingTransport.
  1256  func (a *Authenticator) authTokenInjector(req *http.Request) error {
  1257  	switch tok, err := a.GetAccessToken(a.opts.MinTokenLifetime); {
  1258  	case err == ErrLoginRequired && a.effectiveLoginMode() == OptionalLogin:
  1259  		return nil // skip auth, no need for modifications
  1260  	case err != nil:
  1261  		return err
  1262  	default:
  1263  		tok.SetAuthHeader(req)
  1264  		return nil
  1265  	}
  1266  }
  1267  
  1268  ////////////////////////////////////////////////////////////////////////////////
  1269  // tokenWithProvider implementation.
  1270  
  1271  // tokenWithProvider wraps a token with provider that can update it and a cache
  1272  // that stores it.
  1273  type tokenWithProvider struct {
  1274  	token    *internal.Token        // in-memory cache of the token
  1275  	provider internal.TokenProvider // knows how to generate 'token'
  1276  	cache    internal.TokenCache    // persistent cache for the token
  1277  }
  1278  
  1279  // fetchFromCache updates 't.token' by reading it from the cache.
  1280  func (t *tokenWithProvider) fetchFromCache(ctx context.Context) error {
  1281  	key, err := t.provider.CacheKey(ctx)
  1282  	if err != nil {
  1283  		return err
  1284  	}
  1285  	tok, err := t.cache.GetToken(key)
  1286  	if err != nil {
  1287  		return err
  1288  	}
  1289  	t.token = tok
  1290  	return nil
  1291  }
  1292  
  1293  // putToCache puts 't.token' value into the cache.
  1294  func (t *tokenWithProvider) putToCache(ctx context.Context) error {
  1295  	key, err := t.provider.CacheKey(ctx)
  1296  	if err != nil {
  1297  		return err
  1298  	}
  1299  	return t.cache.PutToken(key, t.token)
  1300  }
  1301  
  1302  // purgeToken removes the token from both on-disk cache and memory.
  1303  func (t *tokenWithProvider) purgeToken(ctx context.Context) error {
  1304  	t.token = nil
  1305  	key, err := t.provider.CacheKey(ctx)
  1306  	if err != nil {
  1307  		return err
  1308  	}
  1309  	return t.cache.DeleteToken(key)
  1310  }
  1311  
  1312  // compareAndRefreshOp is parameters for 'compareAndRefresh' call.
  1313  type compareAndRefreshOp struct {
  1314  	lock      sync.Locker     // optional lock to grab when comparing and refreshing
  1315  	prev      *internal.Token // previously known token (the one we are refreshing)
  1316  	lifetime  time.Duration   // minimum acceptable token lifetime or <0 to force a refresh
  1317  	refreshCb func(ctx context.Context, existing *internal.Token) (*internal.Token, error)
  1318  }
  1319  
  1320  // compareAndRefresh compares currently stored token to 'prev' and calls the
  1321  // given callback (under the lock, if not nil) to refresh it if they are still
  1322  // equal.
  1323  //
  1324  // Returns a refreshed token (if a refresh procedure happened) or the current
  1325  // token, if it's already different from 'prev'. Acts as "Compare-And-Swap"
  1326  // where "Swap" is a token refresh callback.
  1327  //
  1328  // If the callback returns an error (meaning the token can't be refreshed), sets
  1329  // the token to nil and returns the error.
  1330  func (t *tokenWithProvider) compareAndRefresh(ctx context.Context, params compareAndRefreshOp) (*internal.Token, error) {
  1331  	cacheKey, err := t.provider.CacheKey(ctx)
  1332  	if err != nil {
  1333  		// An error here is truly fatal. It is something like "can't read service
  1334  		// account JSON from disk". There's no way to refresh a token without it.
  1335  		return nil, err
  1336  	}
  1337  
  1338  	// To give a context to "Minting a new token" messages and similar below.
  1339  	ctx = logging.SetFields(ctx, logging.Fields{
  1340  		"key":    cacheKey.Key,
  1341  		"scopes": strings.Join(cacheKey.Scopes, " "),
  1342  	})
  1343  
  1344  	// Check that the token still need a refresh and do it (under the lock).
  1345  	tok, cacheIt, err := func() (*internal.Token, bool, error) {
  1346  		if params.lock != nil {
  1347  			params.lock.Lock()
  1348  			defer params.lock.Unlock()
  1349  		}
  1350  
  1351  		// Some other goroutine already updated the token, just return the new one.
  1352  		if t.token != nil && !internal.EqualTokens(t.token, params.prev) {
  1353  			return t.token, false, nil
  1354  		}
  1355  
  1356  		// Rescan the cache. Maybe some other process updated the token. This branch
  1357  		// is also responsible for lazy-loading of tokens from cache for
  1358  		// non-interactive providers, see ensureInitialized().
  1359  		if cached, _ := t.cache.GetToken(cacheKey); cached != nil {
  1360  			t.token = cached
  1361  			if !internal.EqualTokens(cached, params.prev) && params.lifetime > 0 && !internal.TokenExpiresIn(ctx, cached, params.lifetime) {
  1362  				return cached, false, nil
  1363  			}
  1364  		}
  1365  
  1366  		// No one updated the token yet. It should be us. Mint a new token or
  1367  		// refresh the existing one.
  1368  		start := clock.Now(ctx)
  1369  		newTok, err := params.refreshCb(ctx, t.token)
  1370  		if err != nil {
  1371  			t.token = nil
  1372  			return nil, false, err
  1373  		}
  1374  		now := clock.Now(ctx)
  1375  		logging.Debugf(
  1376  			ctx, "The token refreshed in %s, expires in %s",
  1377  			now.Sub(start), newTok.Expiry.Round(0).Sub(now))
  1378  		t.token = newTok
  1379  		return newTok, true, nil
  1380  	}()
  1381  
  1382  	if err == internal.ErrBadRefreshToken || err == internal.ErrBadCredentials {
  1383  		// Do not keep the broken token in the cache. It is unusable. Do this
  1384  		// outside the lock to avoid blocking other callers. Note that t.token is
  1385  		// already nil.
  1386  		if err := t.cache.DeleteToken(cacheKey); err != nil {
  1387  			logging.Warningf(ctx, "Failed to remove broken token from the cache: %s", err)
  1388  		}
  1389  		// A bad refresh token can be fixed by interactive login, so adjust the
  1390  		// error accordingly in this case.
  1391  		if err == internal.ErrBadRefreshToken {
  1392  			err = ErrLoginRequired
  1393  		}
  1394  	}
  1395  
  1396  	if err != nil {
  1397  		return nil, err
  1398  	}
  1399  
  1400  	// Update the cache outside the lock, no need for callers to wait for this.
  1401  	// Do not die if failed, the token is still usable from the memory.
  1402  	if cacheIt && tok != nil {
  1403  		if err := t.cache.PutToken(cacheKey, tok); err != nil {
  1404  			logging.Warningf(ctx, "Failed to write refreshed token to the cache: %s", err)
  1405  		}
  1406  	}
  1407  
  1408  	return tok, nil
  1409  }
  1410  
  1411  // renewToken is called to mint a new token or update existing one.
  1412  //
  1413  // It is called from non-interactive 'refreshToken' method, and thus it can't
  1414  // use interactive login flow.
  1415  func (t *tokenWithProvider) renewToken(ctx context.Context, prev, base *internal.Token) (*internal.Token, error) {
  1416  	if prev == nil {
  1417  		if t.provider.RequiresInteraction() {
  1418  			return nil, ErrLoginRequired
  1419  		}
  1420  		logging.Debugf(ctx, "Minting a new token")
  1421  		tok, err := t.mintTokenWithRetries(ctx, base)
  1422  		if err != nil {
  1423  			logging.Warningf(ctx, "Failed to mint a token: %s", err)
  1424  			return nil, err
  1425  		}
  1426  		return tok, nil
  1427  	}
  1428  
  1429  	logging.Debugf(ctx, "Refreshing the token")
  1430  	tok, err := t.refreshTokenWithRetries(ctx, prev, base)
  1431  	if err != nil {
  1432  		logging.Warningf(ctx, "Failed to refresh the token: %s", err)
  1433  		return nil, err
  1434  	}
  1435  	return tok, nil
  1436  }
  1437  
  1438  // retryParams defines retry strategy for handling transient errors when minting
  1439  // or refreshing tokens.
  1440  func retryParams() retry.Iterator {
  1441  	return &retry.ExponentialBackoff{
  1442  		Limited: retry.Limited{
  1443  			Delay:    10 * time.Millisecond,
  1444  			Retries:  50,
  1445  			MaxTotal: 2 * time.Minute,
  1446  		},
  1447  		Multiplier: 2,
  1448  	}
  1449  }
  1450  
  1451  // mintTokenWithRetries calls provider's MintToken() retrying on transient
  1452  // errors a bunch of times. Called only for non-interactive providers.
  1453  func (t *tokenWithProvider) mintTokenWithRetries(ctx context.Context, base *internal.Token) (tok *internal.Token, err error) {
  1454  	err = retry.Retry(ctx, transient.Only(retryParams), func() error {
  1455  		tok, err = t.provider.MintToken(ctx, base)
  1456  		return err
  1457  	}, retry.LogCallback(ctx, "token-mint"))
  1458  	return
  1459  }
  1460  
  1461  // refreshTokenWithRetries calls providers' RefreshToken(...) retrying on
  1462  // transient errors a bunch of times.
  1463  func (t *tokenWithProvider) refreshTokenWithRetries(ctx context.Context, prev, base *internal.Token) (tok *internal.Token, err error) {
  1464  	err = retry.Retry(ctx, transient.Only(retryParams), func() error {
  1465  		tok, err = t.provider.RefreshToken(ctx, prev, base)
  1466  		return err
  1467  	}, retry.LogCallback(ctx, "token-refresh"))
  1468  	return
  1469  }
  1470  
  1471  ////////////////////////////////////////////////////////////////////////////////
  1472  // Utility functions.
  1473  
  1474  // normalizeScopes sorts the list of scopes and removes dups.
  1475  //
  1476  // Doesn't modify the original slice.
  1477  func normalizeScopes(s []string) []string {
  1478  	for i := 1; i < len(s); i++ {
  1479  		if s[i] <= s[i-1] { // not sorted or has dups
  1480  			sorted := stringset.NewFromSlice(s...)
  1481  			return sorted.ToSortedSlice()
  1482  		}
  1483  	}
  1484  	return s // already sorted and dedupped
  1485  }
  1486  
  1487  // prepPhonyIDTokenScope checks `useIDTokens`.
  1488  //
  1489  // If it is true, requires the audience to be set and replaces scopes with
  1490  // a phony "audience:<value>" scope to be used as a cache key (and ignored by
  1491  // the providers, since they don't use OAuth2 scopes when minting ID tokens).
  1492  // See also comment for Scopes in internal.CacheKey.
  1493  //
  1494  // If `useIDTokens` is false, clears `audience`.
  1495  //
  1496  // As a result, the audience is set if and only if `useIDTokens` is true.
  1497  func prepPhonyIDTokenScope(useIDTokens bool, scopes []string, audience string) (scopesOut []string, audienceOut string, err error) {
  1498  	if useIDTokens {
  1499  		if audience == "" {
  1500  			return nil, "", ErrAudienceRequired
  1501  		}
  1502  		return []string{"audience:" + audience}, audience, nil
  1503  	}
  1504  	return scopes, "", nil
  1505  }
  1506  
  1507  // makeBaseTokenProvider creates TokenProvider implementation based on options.
  1508  //
  1509  // opts.Scopes and opts.UseIDTokens are ignored, `scopes` and `useIDTokens` are
  1510  // used instead. This is used in actor mode to supply parameters necessary to
  1511  // use an "acting" API: they generally do not match what's in `opts`.
  1512  //
  1513  // Called by ensureInitialized.
  1514  func makeBaseTokenProvider(ctx context.Context, opts *Options, scopes []string, useIDTokens bool) (internal.TokenProvider, error) {
  1515  	if opts.testingBaseTokenProvider != nil {
  1516  		return opts.testingBaseTokenProvider, nil
  1517  	}
  1518  
  1519  	// Only UserCredentialsMethod can generate ID tokens and access tokens at
  1520  	// the same time. All other methods can do only ID tokens or only access
  1521  	// tokens. prepPhonyIDTokenScope checks/mutates the parameters accordingly,
  1522  	// see its doc.
  1523  	audience := opts.Audience
  1524  	if opts.Method != UserCredentialsMethod {
  1525  		var err error
  1526  		scopes, audience, err = prepPhonyIDTokenScope(useIDTokens, scopes, audience)
  1527  		if err != nil {
  1528  			return nil, err
  1529  		}
  1530  	}
  1531  
  1532  	switch opts.Method {
  1533  	case UserCredentialsMethod:
  1534  		if opts.ClientID == "" || opts.ClientSecret == "" {
  1535  			return nil, errors.Reason("OAuth client is not configured, can't use interactive login").Err()
  1536  		}
  1537  		// Note: both LoginSessionTokenProvider and UserAuthTokenProvider support
  1538  		// ID tokens and OAuth access tokens at the same time. They ignore audience
  1539  		// (it always matches ClientID).
  1540  		if opts.LoginSessionsHost != "" {
  1541  			if internal.NewLoginSessionTokenProvider == nil {
  1542  				return nil, errors.New("support for interactive login flow is not compiled into this binary")
  1543  			}
  1544  			return internal.NewLoginSessionTokenProvider(
  1545  				ctx,
  1546  				opts.LoginSessionsHost,
  1547  				opts.ClientID,
  1548  				opts.ClientSecret,
  1549  				scopes,
  1550  				opts.Transport)
  1551  		}
  1552  		return internal.NewUserAuthTokenProvider(
  1553  			ctx,
  1554  			opts.ClientID,
  1555  			opts.ClientSecret,
  1556  			scopes)
  1557  	case ServiceAccountMethod:
  1558  		serviceAccountPath := ""
  1559  		if len(opts.ServiceAccountJSON) == 0 {
  1560  			serviceAccountPath = opts.ServiceAccountJSONPath
  1561  		}
  1562  		return internal.NewServiceAccountTokenProvider(
  1563  			ctx,
  1564  			opts.ServiceAccountJSON,
  1565  			serviceAccountPath,
  1566  			scopes,
  1567  			audience)
  1568  	case GCEMetadataMethod:
  1569  		return internal.NewGCETokenProvider(
  1570  			ctx,
  1571  			opts.GCEAccountName,
  1572  			scopes,
  1573  			audience)
  1574  	case LUCIContextMethod:
  1575  		return internal.NewLUCIContextTokenProvider(
  1576  			ctx,
  1577  			scopes,
  1578  			audience,
  1579  			opts.Transport)
  1580  	default:
  1581  		return nil, errors.Reason("unrecognized authentication method: %s", opts.Method).Err()
  1582  	}
  1583  }
  1584  
  1585  // makeIAMTokenProvider creates TokenProvider to use in actingModeIAM mode.
  1586  //
  1587  // Called by ensureInitialized.
  1588  func makeIAMTokenProvider(ctx context.Context, opts *Options) (internal.TokenProvider, error) {
  1589  	if opts.testingIAMTokenProvider != nil {
  1590  		return opts.testingIAMTokenProvider, nil
  1591  	}
  1592  	scopes, audience, err := prepPhonyIDTokenScope(opts.UseIDTokens, opts.Scopes, opts.Audience)
  1593  	if err != nil {
  1594  		return nil, err
  1595  	}
  1596  	return internal.NewIAMTokenProvider(
  1597  		ctx,
  1598  		opts.ActAsServiceAccount,
  1599  		scopes,
  1600  		audience,
  1601  		opts.Transport)
  1602  }
  1603  
  1604  // makeLUCITokenProvider creates TokenProvider to use in actingModeLUCI mode.
  1605  //
  1606  // Called by ensureInitialized.
  1607  func makeLUCITokenProvider(ctx context.Context, opts *Options) (internal.TokenProvider, error) {
  1608  	if opts.TokenServerHost == "" {
  1609  		return nil, ErrBadOptions
  1610  	}
  1611  	if internal.NewLUCITSTokenProvider == nil {
  1612  		return nil, errors.New("support for impersonation through LUCI is not compiled into this binary")
  1613  	}
  1614  	scopes, audience, err := prepPhonyIDTokenScope(opts.UseIDTokens, opts.Scopes, opts.Audience)
  1615  	if err != nil {
  1616  		return nil, err
  1617  	}
  1618  	return internal.NewLUCITSTokenProvider(
  1619  		ctx,
  1620  		opts.TokenServerHost,
  1621  		opts.ActAsServiceAccount,
  1622  		opts.ActViaLUCIRealm,
  1623  		scopes,
  1624  		audience,
  1625  		opts.Transport)
  1626  }