Documentation ¶
Overview ¶
Package internal contains code used internally by auth/integration.
Index ¶
- Constants
- Variables
- func EnableVirtualTerminal() (supported bool, done func())
- func EqualTokens(a, b *Token) bool
- func IsDumbTerminal() 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 IDTokenClaims
- type MemoryTokenCache
- type Token
- type TokenCache
- type TokenProvider
- func NewGCETokenProvider(ctx context.Context, account string, attachScopes bool, scopes []string, ...) (TokenProvider, error)
- func NewIAMTokenProvider(ctx context.Context, actAs string, scopes []string, audience string, ...) (TokenProvider, error)
- func NewLUCIContextTokenProvider(ctx context.Context, scopes []string, audience string, ...) (TokenProvider, error)
- func NewServiceAccountTokenProvider(ctx context.Context, jsonKey []byte, path 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 = "" // NoIDToken indicates it was impossible to obtain an ID token, e.g. no // "openid" scope in the refresh token or the provider doesn't support ID // tokens at all. NoIDToken = "-" // NoAccessToken indicates the access token was not returned by the provider. // // This can happen with providers that support only ID tokens. NoAccessToken = "-" )
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 )
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 or unavailable service account credentials") )
var ( // NewLUCITSTokenProvider returns TokenProvider that uses a LUCI Token Server // to grab tokens belonging to some service account. // // Implemented in luci_ts.go. NewLUCITSTokenProvider func(ctx context.Context, host, actAs, realm string, scopes []string, audience string, transport http.RoundTripper) (TokenProvider, error) // NewLoginSessionTokenProvider returns TokenProvider that can perform // a user-interacting login flow that involves a LoginSessions service. NewLoginSessionTokenProvider func(ctx context.Context, loginSessionsHost, clientID, clientSecret string, scopes []string, transport http.RoundTripper) (TokenProvider, error) )
Functions ¶
func EnableVirtualTerminal ¶
func EnableVirtualTerminal() (supported bool, done func())
EnableVirtualTerminal is a dummy function on unix systems that always returns supported=true.
func EqualTokens ¶
EqualTokens returns true if tokens are equal.
'nil' token corresponds to an empty access token.
func IsDumbTerminal ¶
func IsDumbTerminal() bool
IsDumbTerminal determines if the current terminal supports CursorUp and CursorDown control characters.
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 IAM actor mode. // * luci_ts/<account>/<host>/<realm> when using Token Server actor mode. // * luci_ctx/<digest> when using LUCIContextMethod. Key string `json:"key"` // Scopes is the list of requested OAuth scopes or an ID token audience. // // The token audience is indicated by a fake scope that looks like // "audience:<value>". Cache keys are used only for map indexing, their exact // content doesn't matter. Adding a separate field (like `Audience`) to the // key causes complication with older binaries that read the token cache and // don't know about the new field, so we abuse `Scopes` field instead. Scopes []string `json:"scopes,omitempty"` }
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 IDTokenClaims ¶
type IDTokenClaims struct { Aud string `json:"aud"` Email string `json:"email"` EmailVerified bool `json:"email_verified"` Exp int64 `json:"exp"` Iss string `json:"iss"` Nonce string `json:"nonce"` Sub string `json:"sub"` }
IDTokenClaims contains a *subset* of ID token claims we are interested in.
func ParseIDTokenClaims ¶
func ParseIDTokenClaims(tok string) (*IDTokenClaims, error)
ParseIDTokenClaims extracts claims of the ID token.
It doesn't validate the signature nor the validity of the claims.
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 IDToken string // an ID token derived directly from the access token or NoIDToken Email string // an email or NoEmail or empty string (aka UnknownEmail) }
Token is an oauth2.Token with an email and ID token that correspond 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, attachScopes bool, scopes []string, audience 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, audience 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, audience 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, audience string) (TokenProvider, error)
NewServiceAccountTokenProvider returns TokenProvider that uses service account private key (on disk or in memory) to make access tokens.