Documentation ¶
Overview ¶
Package internal contains code used internally by auth/integration.
Index ¶
- Constants
- Variables
- func EqualCacheKeys(a, b *CacheKey) bool
- func EqualTokens(a, b *Token) bool
- func TokenExpiresIn(ctx context.Context, t *Token, lifetime time.Duration) bool
- func TokenExpiresInRnd(ctx context.Context, t *Token, lifetime time.Duration) bool
- type CacheKey
- type DiskTokenCache
- type MemoryTokenCache
- type Token
- type TokenCache
- type TokenProvider
- func NewGCETokenProvider(ctx context.Context, account string, scopes []string) (TokenProvider, error)
- func NewIAMTokenProvider(ctx context.Context, actAs string, scopes []string, ...) (TokenProvider, error)
- func NewLUCIContextTokenProvider(ctx context.Context, scopes []string, transport http.RoundTripper) (TokenProvider, error)
- func NewServiceAccountTokenProvider(ctx context.Context, jsonKey []byte, path string, scopes []string) (TokenProvider, error)
- func NewUserAuthTokenProvider(ctx context.Context, clientID, clientSecret string, scopes []string) (TokenProvider, error)
Constants ¶
const ( // NoEmail indicates an OAuth2 token is not associated with an email. // // See Token below. We need this special value to distinguish "an email can not // possibly be fetched ever" from "the cached token doesn't have an email yet" // cases. NoEmail = "-" // UnknownEmail indicates an OAuth2 token may potentially be associated with // an email, but we haven't tried to fetch the email yet. UnknownEmail = "" )
const ( // GCAccessTokenMaxAge defines when to remove unused access tokens from the // disk cache. // // We define "an access token" as an instance of oauth2.Token with // RefreshToken set to "". // // If an access token expired older than GCAccessTokenMaxAge ago, it will be // evicted from the cache (it is essentially garbage now anyway). GCAccessTokenMaxAge = 2 * time.Hour // GCRefreshTokenMaxAge defines when to remove unused refresh tokens from the // disk cache. // // We define "a refresh token" as an instance of oauth2.Token with // RefreshToken not set to "". // // Refresh tokens don't expire, but they get neglected and forgotten by users, // staying on their disks forever. We remove such tokens if they haven't been // used for more than two weeks. // // It essentially logs out the user on inactivity. We don't actively revoke // evicted tokens though, since it's possible the user has copied the token // and uses it elsewhere (it happens). Such token can always be revoked from // Google Accounts page manually, if required. GCRefreshTokenMaxAge = 14 * 24 * time.Hour // CacheFilename is a name of the file with all cached tokens. CacheFilename = "creds.json" )
Variables ¶
var ( // ErrInsufficientAccess is returned by MintToken() if 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 = errors.New("can't get access token for the given account and scopes") // ErrBadRefreshToken is returned by RefreshToken if refresh token was revoked // or otherwise invalid. It means MintToken must be used to get a new refresh // token. ErrBadRefreshToken = errors.New("refresh_token is not valid") // ErrBadCredentials is returned by MintToken or RefreshToken if provided // offline credentials (like service account key) are invalid. ErrBadCredentials = errors.New("invalid service account credentials") )
Functions ¶
func EqualCacheKeys ¶
EqualCacheKeys returns true if keys are equal.
func EqualTokens ¶
EqualTokens returns true if tokens are equal.
'nil' token corresponds to an empty access token.
func TokenExpiresIn ¶
TokenExpiresIn returns True if the token is not valid or expires within given duration.
The function returns True in any of the following conditions:
- The token is not valid.
- The token expires before now+lifetime.
In all other cases it returns False.
func TokenExpiresInRnd ¶
TokenExpiresInRnd is like TokenExpiresIn, except it slightly randomizes the token expiration time.
If the function returns False, the token expires past now+lifetime. In other words, it is totally safe to use the token until now+lifetime. The inverse of this statement is not correct though: if the function returns True, it doesn't necessarily imply the token will expire before now+lifetime.
The function returns True in any of the following conditions:
- The token is not valid.
- The token expires before now+lifetime.
- The token expiration time is between (now+lifetime, now+lifetime+rnd), where rnd is a uniformly distributed random number between 0 and expiryRandInterval sec (which is set to 30 sec).
This is useful for processes that use multiple service account keys at around the same time. Without randomization, access tokens for such keys expire at the same time (strictly 1h after process startup, where 1h is the default token lifetime). This causes unnecessary contention on the token cache file.
Types ¶
type CacheKey ¶
type CacheKey struct { // Key identifies an auth method being used to get the token and its // parameters. // // Its exact form is not important, since it is used only for string matching // when searching for a token inside the cache. // // The following forms are being used currently: // * user/<client_id> when using UserCredentialsMethod with some ClientID. // * service_account/<email>/<key_id> when using ServiceAccountMethod. // * gce/<account> when using GCEMetadataMethod. // * iam/<account> when using actor mode with ActAsServiceAccount != "". // * luci_ctx/<digest> when using LUCIContextMethod. Key string `json:"key"` // Scopes is the list of requested OAuth scopes. Scopes []string `json:"scopes"` }
CacheKey identifies a slot in the token cache to store the token in.
type DiskTokenCache ¶
DiskTokenCache implements TokenCache on top of a file.
It uses single file to store all tokens. If multiple processes try to write to it at the same time, only one process wins (so some updates may be lost).
TODO(vadimsh): Either use file locking or split the cache into multiple files to avoid concurrency issues.
TODO(vadimsh): Once this implementation settles and is deployed everywhere, add a cleanup step that removes <cache_dir>/*.tok left from the previous version of this code.
func (*DiskTokenCache) DeleteToken ¶
func (c *DiskTokenCache) DeleteToken(key *CacheKey) error
DeleteToken removes the token from cache.
type MemoryTokenCache ¶
type MemoryTokenCache struct {
// contains filtered or unexported fields
}
MemoryTokenCache implements TokenCache on top of in-process memory.
func (*MemoryTokenCache) DeleteToken ¶
func (c *MemoryTokenCache) DeleteToken(key *CacheKey) error
DeleteToken removes the token from cache.
type Token ¶
type Token struct { oauth2.Token Email string // an email or NoEmail or empty string (aka UnknownEmail) }
Token is an oauth2.Token with an email that corresponds to it.
Email may be an empty string, in which case we assume the email hasn't been fetched yet. It can also be a special NoEmail string, which means the token is not associated with an email (happens for tokens without 'userinfo.email' scope).
type TokenCache ¶
type TokenCache interface { // GetToken reads the token from cache. // // Returns (nil, nil) if requested token is not in the cache. GetToken(key *CacheKey) (*Token, error) // PutToken writes the token to cache. PutToken(key *CacheKey, tok *Token) error // DeleteToken removes the token from cache. DeleteToken(key *CacheKey) error }
TokenCache stores access and refresh tokens to avoid requesting them all the time.
var ( // ProcTokenCache is shared in-process cache to use if disk cache is disabled. ProcTokenCache TokenCache = &MemoryTokenCache{} )
type TokenProvider ¶
type TokenProvider interface { // RequiresInteraction is true if provider may start user interaction // in MintToken. RequiresInteraction() bool // Lightweight is true if MintToken is very cheap to call. // // In this case the token is not being cached on disk (only in memory), since // it's easy to get a new one each time the process starts. // // By avoiding the disk cache, we reduce the chance of a leak. Lightweight() bool // Email is email associated with tokens produced by the provider, if known. // // May return UnknownEmail, which means the provider doesn't know the email // in advance and RefreshToken must be used to get the token and the email. // This happens, for example, for interactive providers before user has // logged in. // // It can also be NoEmail which means the email is not available, even if // caller is using RefreshToken. Email() string // CacheKey identifies a slot in the token cache to store the token in. // // Note: CacheKey MAY change during lifetime of a TokenProvider. It happens, // for example, for ServiceAccount token provider if the underlying service // account key is replaced while the process is still running. CacheKey(ctx context.Context) (*CacheKey, error) // MintToken launches authentication flow (possibly interactive) and returns // a new refreshable token (or error). It must never return (nil, nil). // // In actor mode 'base' is an IAM-scoped sufficiently fresh oauth token. It's // nil otherwise. Used by IAM-based token provider. MintToken(ctx context.Context, base *Token) (*Token, error) // RefreshToken takes existing token (probably expired, but not necessarily) // and returns a new refreshed token. It should never do any user interaction. // If a user interaction is required, a error should be returned instead. // // In actor mode 'base' is an IAM-scoped sufficiently fresh oauth token. It's // nil otherwise. Used by IAM-based token provider. RefreshToken(ctx context.Context, prev, base *Token) (*Token, error) }
TokenProvider knows how to mint new tokens or refresh existing ones.
func NewGCETokenProvider ¶
func NewGCETokenProvider(ctx context.Context, account string, scopes []string) (TokenProvider, error)
NewGCETokenProvider returns TokenProvider that knows how to use GCE metadata server.
func NewIAMTokenProvider ¶
func NewIAMTokenProvider(ctx context.Context, actAs string, scopes []string, transport http.RoundTripper) (TokenProvider, error)
NewIAMTokenProvider returns TokenProvider that uses generateAccessToken IAM API to grab tokens belonging to some service account.
func NewLUCIContextTokenProvider ¶
func NewLUCIContextTokenProvider(ctx context.Context, scopes []string, transport http.RoundTripper) (TokenProvider, error)
NewLUCIContextTokenProvider returns TokenProvider that knows how to use a local auth server to mint tokens.
It requires LUCI_CONTEXT["local_auth"] to be present in the 'ctx'. It's a description of how to locate and contact the local auth server.
See auth/integration/localauth package for the implementation of the server.
func NewServiceAccountTokenProvider ¶
func NewServiceAccountTokenProvider(ctx context.Context, jsonKey []byte, path string, scopes []string) (TokenProvider, error)
NewServiceAccountTokenProvider returns TokenProvider that uses service account private key (on disk or in memory) to make access tokens.
func NewUserAuthTokenProvider ¶
func NewUserAuthTokenProvider(ctx context.Context, clientID, clientSecret string, scopes []string) (TokenProvider, error)
NewUserAuthTokenProvider returns TokenProvider that can perform 3-legged OAuth flow involving interaction with a user.