Version: v0.0.0-...-4a11b79 Latest Latest

This package is not in the latest version of its module.

Go to latest
Published: Sep 2, 2020 License: Apache-2.0 Imports: 18 Imported by: 0


LUCI Auth libraries

This package contains auth related elements for LUCI programs.

  • This package - Implements a wrapper around which is aware of how LUCI programs handle OAuth2 credentials.
  • client - Command-line flags and programs to interact with luci/auth from clients of LUCI systems.
  • identity - Defines common LUCI identity types and concepts. Used by clients and servers.
  • integration - Libraries for exporting LUCI authentication tokens (OAuth2) to other systems (like gsutil or gcloud).



Package auth implements a wrapper around

Its main improvement is the on-disk cache for OAuth tokens, which is especially important for 3-legged interactive OAuth flows: its usage eliminates annoying login prompts each time a program is used (because the refresh token can now be reused). The cache also allows to reduce unnecessary token refresh calls when sharing a service account between processes.

The package also implements some best practices regarding interactive login flows in CLI programs. It makes it easy to implement a login process as a separate interactive step that happens before the main program loop.

The antipattern it tries to prevent is "launch an interactive login flow whenever program hits 'Not Authorized' response from the server". This usually results in a very confusing behavior, when login prompts pop up unexpectedly at random time, random places and from multiple goroutines at once, unexpectedly consuming unintended stdin input.



View Source
const (
	OAuthScopeEmail = ""
	OAuthScopeIAM   = ""

Some known Google API OAuth scopes.

View Source
const (
	// GCEServiceAccount is special value that can be passed instead of path to
	// a service account credentials file to indicate that GCE VM credentials should
	// be used instead of a real credentials file.
	GCEServiceAccount = ":gce"


View Source
var (
	// ErrLoginRequired is returned by Transport() in case long term credentials
	// are not cached and the user must go through interactive login.
	ErrLoginRequired = errors.New("interactive login is required")

	// ErrInsufficientAccess is returned by Login() or Transport() if an access
	// token can't be minted for given OAuth scopes. For example if a GCE instance
	// wasn't granted access to requested scopes when it was created.
	ErrInsufficientAccess = internal.ErrInsufficientAccess

	// ErrBadCredentials is returned by authenticating RoundTripper if service
	// account key used to generate access tokens is revoked, malformed or can not
	// be read from disk.
	ErrBadCredentials = internal.ErrBadCredentials

	// ErrNoEmail is returned by GetEmail() if the cached credentials are not
	// associated with some particular email. This may happen, for example, when
	// using a refresh token that doesn't have '' scope.
	ErrNoEmail = errors.New("the token is not associated with an email")

	// ErrBadOptions is returned by Login() or Transport() if Options passed
	// to authenticator indicate incompatible features. This likely indicates
	// a programming error.
	ErrBadOptions = errors.New("bad authenticator options")


func AllowsArbitraryScopes

func AllowsArbitraryScopes(ctx context.Context, opts Options) bool

AllowsArbitraryScopes returns true if given authenticator options allow generating tokens for arbitrary set of scopes.

For example, using a private key to sign assertions allows to mint tokens for any set of scopes (since there's no restriction on what scopes we can put into JWT to be signed).

On other hand, using e.g GCE metadata server restricts us to use only scopes assigned to GCE instance when it was created.

func NewModifyingTransport

func NewModifyingTransport(base http.RoundTripper, modifier func(*http.Request) error) http.RoundTripper

NewModifyingTransport returns a transport that can modify headers of http.Request's that pass through it (e.g. by appending authentication information).

Go docs explicitly prohibit this kind of behavior, but everyone cheat and implement it anyway. E.g.

Works only with Go >=1.5 transports (that use Request.Cancel instead of deprecated Transport.CancelRequest). For same reason doesn't implement CancelRequest itself.

TODO(vadimsh): Move it elsewhere. It has no direct relation to auth.

func SetMonitoringInstrumentation

func SetMonitoringInstrumentation(cb func(context.Context, http.RoundTripper, string) http.RoundTripper)

SetMonitoringInstrumentation sets a global callback used by Authenticator to add monitoring instrumentation to a transport.

This is used by tsmon library in init(). We have to resort to callbacks to break module dependency cycle (tsmon is using auth lib already).


type Authenticator

type Authenticator struct {
	// contains filtered or unexported fields

Authenticator is a factory for http.RoundTripper objects that know how to use cached OAuth credentials and how to send monitoring metrics (if tsmon package was imported).

Authenticator also knows how to run interactive login flow, if required.

func NewAuthenticator

func NewAuthenticator(ctx context.Context, loginMode LoginMode, opts Options) *Authenticator

NewAuthenticator returns a new instance of Authenticator given its options.

The authenticator is essentially a factory for http.RoundTripper that knows how to use OAuth2 tokens. It is bound to the given context: uses its logger, clock, transport and deadline.

func (*Authenticator) CheckLoginRequired

func (a *Authenticator) CheckLoginRequired() error

CheckLoginRequired decides whether an interactive login is required.

It examines the token cache and the configured authentication method to figure out whether we can attempt to grab an access token without involving the user interaction.

Note: it does not check that the cached refresh token is still valid (i.e. not revoked). A revoked token will result in ErrLoginRequired error on a first attempt to use it.


  • nil if we have a valid cached token or can mint one on the fly.
  • ErrLoginRequired if we have no cached token and need to bother the user.
  • ErrInsufficientAccess if the configured auth method can't mint the token we require (e.g when using GCE method and the instance doesn't have all requested OAuth scopes).
  • Generic error on other unexpected errors.

func (*Authenticator) Client

func (a *Authenticator) Client() (*http.Client, error)

Client optionally performs a login and returns http.Client.

It uses transport returned by Transport(). See documentation for LoginMode for more details.

func (*Authenticator) GetAccessToken

func (a *Authenticator) GetAccessToken(lifetime time.Duration) (*oauth2.Token, error)

GetAccessToken returns a valid access token with specified minimum lifetime.

Does not interact with the user. May return ErrLoginRequired.

func (*Authenticator) GetEmail

func (a *Authenticator) GetEmail() (string, error)

GetEmail returns an email associated with the credentials.

In most cases this is a fast call that hits the cache. In some rare cases it may do an RPC to the Token Info endpoint to grab an email associated with the token.

Returns ErrNoEmail if the email is not available. This may happen, for example, when using a refresh token that doesn't have '' scope. Callers must expect this error to show up and should prepare a fallback.

Returns an error if the email can't be fetched due to some other transient or fatal error. In particular, returns ErrLoginRequired if interactive login is required to get the token in the first place.

func (*Authenticator) Login

func (a *Authenticator) Login() error

Login perform an interaction with the user to get a long term refresh token and cache it.

Blocks for user input, can use stdin. It overwrites currently cached credentials, if any.

When used with non-interactive token providers (e.g. based on service accounts), just clears the cached access token, so next the next authenticated call gets a fresh one.

func (*Authenticator) PerRPCCredentials

func (a *Authenticator) PerRPCCredentials() (credentials.PerRPCCredentials, error)

PerRPCCredentials optionally performs a login and returns PerRPCCredentials.

It can be used to authenticate outbound gPRC RPC's.

Has same logic as Transport(), in particular supports OptionalLogin mode. See Transport() for more details.

func (*Authenticator) PurgeCredentialsCache

func (a *Authenticator) PurgeCredentialsCache() error

PurgeCredentialsCache removes cached tokens.

Does not revoke them!

func (*Authenticator) TokenSource

func (a *Authenticator) TokenSource() (oauth2.TokenSource, error)

TokenSource optionally performs a login and returns oauth2.TokenSource.

Can be used for interoperability with libraries that use

It doesn't support 'OptionalLogin' mode, since oauth2.TokenSource must return some token. Otherwise its logic is similar to Transport(). In particular it may return ErrLoginRequired if interactive login is required, but the authenticator is in the silent mode. See LoginMode enum for more details.

func (*Authenticator) Transport

func (a *Authenticator) Transport() (http.RoundTripper, error)

Transport optionally performs a login and returns http.RoundTripper.

It is a high level wrapper around CheckLoginRequired() and Login() calls. See documentation for LoginMode for more details.

type LoginMode

type LoginMode string

LoginMode is used as enum in NewAuthenticator function.

const (
	// InteractiveLogin indicates to Authenticator that it is okay to run an
	// interactive login flow (via Login()) in Transport(), Client() or other
	// factories if there's no cached token.
	// This is typically used with UserCredentialsMethod to generate an OAuth
	// refresh token and put it in the token cache at the start of the program,
	// when grabbing a transport.
	// Has no effect when used with service account credentials.
	InteractiveLogin LoginMode = "InteractiveLogin"

	// SilentLogin indicates to Authenticator that it must return a transport that
	// implements authentication, but it is NOT OK to run interactive login flow
	// to make it.
	// Transport() and other factories will fail with ErrLoginRequired error if
	// there's no cached token or one can't be generated on the fly in
	// non-interactive mode. This may happen when using UserCredentialsMethod.
	// It is always OK to use SilentLogin mode with service accounts credentials
	// (ServiceAccountMethod mode), since no user interaction is necessary to
	// generate an access token in this case.
	SilentLogin LoginMode = "SilentLogin"

	// OptionalLogin indicates to Authenticator that it should return a transport
	// that implements authentication, but it is OK to return non-authenticating
	// transport if there are no valid cached credentials.
	// An interactive login flow will never be invoked. An unauthenticated client
	// will be returned if no credentials are present.
	// Can be used when making calls to backends that allow anonymous access. This
	// is especially useful with UserCredentialsMethod: a user may start using
	// the service right away (in anonymous mode), and later login (using Login()
	// method or any other way of initializing credentials cache) to get more
	// permissions.
	// When used with ServiceAccountMethod it is identical to SilentLogin, since
	// it makes no sense to ignore invalid service account credentials when the
	// caller is explicitly asking the authenticator to use them.
	// Has the original meaning when used with GCEMetadataMethod: it instructs to
	// skip authentication if the token returned by GCE metadata service doesn't
	// have all requested scopes.
	OptionalLogin LoginMode = "OptionalLogin"

type Method

type Method string

Method defines a method to use to obtain OAuth access token.

const (
	// AutoSelectMethod can be used to allow the library to pick a method most
	// appropriate for given set of options and the current execution environment.
	// For example, passing ServiceAccountJSONPath or ServiceAccountJSON makes
	// Authenticator to pick ServiceAccountMethod.
	// See SelectBestMethod function for details.
	AutoSelectMethod Method = ""

	// UserCredentialsMethod is used for interactive OAuth 3-legged login flow.
	// Using this method requires specifying an OAuth client by passing ClientID
	// and ClientSecret in Options when calling NewAuthenticator.
	// Additionally, SilentLogin and OptionalLogin (i.e. non-interactive) login
	// modes rely on a presence of a refresh token in the token cache, thus using
	// these modes with UserCredentialsMethod also requires configured token
	// cache (see SecretsDir field of Options).
	UserCredentialsMethod Method = "UserCredentialsMethod"

	// ServiceAccountMethod is used to authenticate as a service account using
	// a private key.
	// Callers of NewAuthenticator must pass either a path to a JSON file with
	// service account key (as produced by Google Cloud Console) or a body of this
	// JSON file. See ServiceAccountJSONPath and ServiceAccountJSON fields in
	// Options.
	// Using ServiceAccountJSONPath has an advantage: Authenticator always loads
	// the private key from the file before refreshing the token, it allows to
	// replace the key while the process is running.
	ServiceAccountMethod Method = "ServiceAccountMethod"

	// GCEMetadataMethod is used on Compute Engine to use tokens provided by
	// Metadata server. See
	GCEMetadataMethod Method = "GCEMetadataMethod"

	// LUCIContextMethod is used by LUCI-aware applications to fetch tokens though
	// a local auth server (discoverable via "local_auth" key in LUCI_CONTEXT).
	// This method is similar in spirit to GCEMetadataMethod: it uses some local
	// HTTP server as a provider of OAuth access tokens, which gives an ambient
	// authentication context to apps that use it.
	// There are some big differences:
	//  1. LUCIContextMethod supports minting tokens for multiple different set
	//     of scopes, unlike GCE metadata server that always gives a token with
	//     preconfigured scopes (set when the GCE instance was created).
	//  2. LUCIContextMethod is not GCE-specific. It doesn't use magic link-local
	//     IP address. It can run on any machine.
	//  3. The access to the local auth server is controlled by file system
	//     permissions of LUCI_CONTEXT file (there's a secret in this file).
	//  4. There can be many local auth servers running at once (on different
	//     ports). Useful for bringing up sub-contexts, in particular in
	//     combination with ActAsServiceAccount ("sudo" mode) or for tests.
	// See auth/integration/localauth package for the implementation of the server
	// side of the protocol.
	LUCIContextMethod Method = "LUCIContextMethod"

Supported authentication methods.

func SelectBestMethod

func SelectBestMethod(ctx context.Context, opts Options) Method

SelectBestMethod returns a most appropriate authentication method for the given set of options and the current execution environment.

Invoked by Authenticator if AutoSelectMethod is passed as Method in Options. It picks the first applicable method in this order:

  • ServiceAccountMethod (if the service account private key is configured).
  • LUCIContextMethod (if running inside LUCI_CONTEXT with an auth server).
  • GCEMetadataMethod (if running on GCE and GCEAllowAsDefault is true).
  • UserCredentialsMethod (if no other method applies).

Beware: it may do relatively heavy calls on first usage (to detect GCE environment). Fast after that.

type Options

type Options struct {
	// Transport is underlying round tripper to use for requests.
	// Default: http.DefaultTransport.
	Transport http.RoundTripper

	// Method defines how to grab OAuth2 tokens.
	// Default: AutoSelectMethod.
	Method Method

	// Scopes is a list of OAuth scopes to request.
	// Default: [OAuthScopeEmail].
	Scopes []string

	// ActAsServiceAccount is used to act as a specified service account email.
	// When this option is set, there are two identities involved:
	//  1. A service account identity specified by `ActAsServiceAccount`.
	//  2. An identity conveyed by the authenticator options (via cached refresh
	//     token, or via `ServiceAccountJSON`, or other similar ways), i.e. the
	//     identity asserted by the authenticator in case `ActAsServiceAccount` is
	//     not set. It is referred to below as the Actor identity.
	// The resulting authenticator will produce access tokens for service account
	// `ActAsServiceAccount`, using the Actor identity to generate them via some
	// "acting" API.
	// If `ActViaLUCIRealm` is not set, the "acting" API is Google Cloud IAM.
	// The Actor credentials will internally be used to generate access tokens
	// with IAM scope (see `OAuthScopeIAM`). These tokens will then be used to
	// call `generateAccessToken` Cloud IAM RPC to obtain the final tokens that
	// belong to the service account `ActAsServiceAccount`. This requires the
	// Actor to have "iam.serviceAccounts.getAccessToken" Cloud IAM permission,
	// which is usually granted via "Service Account Token Creator" IAM role.
	// If `ActViaLUCIRealm` is set, the "acting" API is the LUCI Token Server.
	// The Actor credentials will internally be used to generate access tokens
	// with just email scope (see `OAuthScopeEmail`). These tokens will then be
	// used to call `MintServiceAccountToken` RPC. This requires the following
	// LUCI permissions in the realm specified by `ActViaLUCIRealm`:
	//  1. The Actor needs "luci.serviceAccounts.mintToken" permission.
	//  2. The target service account needs "luci.serviceAccounts.existInRealm"
	//     permission.
	//  3. The LUCI project the realm belongs to must be authorized to use the
	//     target service account (currently via project_owned_accounts.cfg global
	//     config file).
	// Regardless of what "acting" API is used, `Scopes` parameter specifies what
	// OAuth scopes to request for the final access token belonging to
	// `ActAsServiceAccount`.
	// Default: none.
	ActAsServiceAccount string

	// ActViaLUCIRealm is a LUCI Realm to use to authorize access to the service
	// account when "acting" as it through a LUCI Token Server.
	// See `ActAsServiceAccount` for a detailed explanation.
	// Should have form "<project>:<realm>" (e.g. "chromium:ci"). It instructs
	// the Token Server to lookup acting permissions in a realm named "<realm>",
	// defined in `realms.cfg` project config file in a LUCI project named
	// "<project>".
	// Using this option requires `TokenServerHost` to be set.
	// Default: none.
	ActViaLUCIRealm string

	// TokenServerHost is a hostname of a LUCI Token Server to use when acting.
	// Used only when `ActAsServiceAccount` and `ActViaLUCIRealm` are set.
	// Default: none.
	TokenServerHost string

	// ClientID is OAuth client ID to use with UserCredentialsMethod.
	// See
	// (in particular everything related to "Desktop apps").
	// Together with Scopes forms a cache key in the token cache, which in
	// practical terms means there can be only one concurrently "logged in" user
	// per [ClientID, Scopes] combination. So if multiple binaries use exact same
	// ClientID and Scopes, they'll share credentials cache (a login in one app
	// makes the user logged in in the other app too).
	// If you don't want to share login information between tools, use separate
	// ClientID or SecretsDir values.
	// If not set, UserCredentialsMethod auth method will not work.
	// Default: none.
	ClientID string

	// ClientSecret is OAuth client secret to use with UserCredentialsMethod.
	// Default: none.
	ClientSecret string

	// ServiceAccountJSONPath is a path to a JSON blob with a private key to use.
	// Can also be set to GCEServiceAccount (':gce') to indicate that the GCE VM
	// service account should be used instead. Useful in CLI interfaces. This
	// works only if Method is set to AutoSelectMethod (which is the default for
	// most CLI apps). If GCEServiceAccount is used on non-GCE machine,
	// authenticator methods return ErrBadCredentials.
	// Used only with ServiceAccountMethod.
	ServiceAccountJSONPath string

	// ServiceAccountJSON is a body of JSON key file to use.
	// Overrides ServiceAccountJSONPath if given.
	ServiceAccountJSON []byte

	// GCEAccountName is an account name to query to fetch token for from metadata
	// server when GCEMetadataMethod is used.
	// If given account wasn't granted required set of scopes during instance
	// creation time, Transport() call fails with ErrInsufficientAccess.
	// Default: "default" account.
	GCEAccountName string

	// GCEAllowAsDefault indicates whether it is OK to pick GCE authentication
	// method as default if no other methods apply.
	// Effective only when running on GCE and Method is set to AutoSelectMethod.
	// In theory using GCE metadata server for authentication when it is
	// available looks attractive. In practice, especially if running in a
	// heterogeneous fleet with a mix of GCE and non-GCE machines, automatically
	// enabling GCE-based authentication is very surprising when it happens.
	// Default: false (don't "sniff" GCE environment).
	GCEAllowAsDefault bool

	// SecretsDir can be used to set the path to a directory where tokens
	// are cached.
	// If not set, tokens will be cached only in the process memory. For refresh
	// tokens it means the user would have to go through the login process each
	// time process is started. For service account tokens it means there'll be
	// HTTP round trip to OAuth backend to generate access token each time the
	// process is started.
	SecretsDir string

	// DisableMonitoring can be used to disable the monitoring instrumentation.
	// The transport produced by this authenticator sends tsmon metrics IFF:
	//  1. DisableMonitoring is false (default).
	//  2. The context passed to 'NewAuthenticator' has monitoring initialized.
	DisableMonitoring bool

	// MonitorAs is used for 'client' field of monitoring metrics.
	// The default is 'luci-go'.
	MonitorAs string

	// MinTokenLifetime defines a minimally acceptable lifetime of access tokens
	// generated internally by authenticating http.RoundTripper, TokenSource and
	// PerRPCCredentials.
	// Not used when GetAccessToken is called directly (it accepts this parameter
	// as an argument).
	// The default is 2 min. There's rarely a need to change it and using smaller
	// values may be dangerous (e.g. if the request gets stuck somewhere or the
	// token is cached incorrectly it may expire before it is checked).
	MinTokenLifetime time.Duration
	// contains filtered or unexported fields

Options are used by NewAuthenticator call.


Path Synopsis
Package authctx allows to run subprocesses in an environment with ambient auth.
Package authctx allows to run subprocesses in an environment with ambient auth.
Package authcli implements authentication related flags parsing and CLI subcommands.
Package authcli implements authentication related flags parsing and CLI subcommands.
Command luci-auth can be used to interact with OAuth2 token cache on disk.
Command luci-auth can be used to interact with OAuth2 token cache on disk.
Command realms-dump can dump realm configs served by an Auth Service.
Command realms-dump can dump realm configs served by an Auth Service.
Package identity defines Identity type and related types and constants.
Package identity defines Identity type and related types and constants.
Package authtest implements authentication related test helpers.
Package authtest implements authentication related test helpers.
Package devshell implements Devshell protocol for locally getting auth token.
Package devshell implements Devshell protocol for locally getting auth token.
Package firebase implements an auth server that allows firebase-tools to use an exposed OAuth2 TokenSource for auth.
Package firebase implements an auth server that allows firebase-tools to use an exposed OAuth2 TokenSource for auth.
Package gcemeta implements a subset of GCE metadata server protocol.
Package gcemeta implements a subset of GCE metadata server protocol.
Package gsutil implements a hacky shim that makes gsutil use LUCI local auth.
Package gsutil implements a hacky shim that makes gsutil use LUCI local auth.
Package localauth implements localhost HTTP server that hands out tokens to local LUCI-aware processes.
Package localauth implements localhost HTTP server that hands out tokens to local LUCI-aware processes.
Package internal contains code used internally by auth/integration.
Package internal contains code used internally by auth/integration.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL