luci: go.chromium.org/luci/auth Index | Files | Directories

package auth

import "go.chromium.org/luci/auth"

Package auth implements a wrapper around golang.org/x/oauth2.

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.

Index

Package Files

auth.go transport.go

Constants

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"
)
const (
    OAuthScopeEmail = "https://www.googleapis.com/auth/userinfo.email"
)

Known Google API OAuth scopes.

Variables

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 access_token
    // can't be minted for given OAuth scopes. For example if 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 'userinfo.email' scope.
    ErrNoEmail = errors.New("the token is not associated with an email")
)

func AllowsArbitraryScopes Uses

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 Uses

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. https://github.com/golang/oauth2.

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 Uses

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 Uses

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 Uses

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 Uses

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.

Returns:

* 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 Uses

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 Uses

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 Uses

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 'userinfo.email' 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 Uses

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 Uses

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 Uses

func (a *Authenticator) PurgeCredentialsCache() error

PurgeCredentialsCache removes cached tokens.

Does not revoke them!

func (*Authenticator) TokenSource Uses

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 golang.org/x/oauth2.

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 Uses

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 Uses

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 Uses

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 ServiceAcountJSON 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 https://cloud.google.com/compute/docs/authentication
    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 ActAsServiceAcccount ("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 Uses

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 Uses

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.
    //
    // This uses signBlob Cloud IAM API and "iam.serviceAccountActor" role.
    //
    // When this option is set, there are two identities involved:
    //  1. A service account identity directly 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. This identity must have "iam.serviceAccountActor" role in
    //     the `ActAsServiceAccount` IAM resource. It is referred to below as
    //     Actor identity.
    //
    // The resulting authenticator will produce access tokens for service account
    // `ActAsServiceAccount`, using Actor identity to generate them via Cloud IAM
    // API.
    //
    // `Scopes` parameter specifies what OAuth scopes to request for access tokens
    // belonging to `ActAsServiceAccount`.
    //
    // The Actor credentials will be internally used to generate access token with
    // IAM scope ("https://www.googleapis.com/auth/iam"). It means Login() action
    // sets up a refresh token with IAM scope (not `Scopes`), and the user will
    // be presented with a consent screen for IAM scope.
    //
    // More info at https://cloud.google.com/iam/docs/service-accounts
    //
    // Default: none.
    ActAsServiceAccount string

    // ClientID is OAuth client ID to use with UserCredentialsMethod.
    //
    // See https://developers.google.com/identity/protocols/OAuth2InstalledApp
    // (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
    // contains filtered or unexported fields
}

Options are used by NewAuthenticator call.

Directories

PathSynopsis
client/authcliPackage authcli implements authentication related flags parsing and CLI subcommands.
client/cmd/luci-authCommand luci-auth can be used to interact with OAuth2 token cache on disk.
client/luci_auth
identityPackage identity defines Identity type and related types and constants.
integration/authtestPackage authtest implements authentication related test helpers.
integration/devshellPackage devshell implements Devshell protocol for locally getting auth token.
integration/gsutilPackage gsutil implements a hacky shim that makes gsutil use LUCI local auth.
integration/internal/localsrvPackage localsrv provides helpers for running local TCP servers.
integration/localauthPackage localauth implements localhost HTTP server that hands out tokens to local LUCI-aware processes.
integration/localauth/rpcs
internalPackage internal contains code used internally by auth/integration.

Package auth imports 19 packages (graph) and is imported by 57 packages. Updated 2018-08-21. Refresh now. Tools for package owners.