Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(40)

Side by Side Diff: common/auth/auth.go

Issue 2956543003: auth: Fix very unlikely race condition (sort of) when refreshing token. (Closed)
Patch Set: Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 The LUCI Authors. All rights reserved. 1 // Copyright 2015 The LUCI Authors. All rights reserved.
2 // Use of this source code is governed under the Apache License, Version 2.0 2 // Use of this source code is governed under the Apache License, Version 2.0
3 // that can be found in the LICENSE file. 3 // that can be found in the LICENSE file.
4 4
5 // Package auth implements a wrapper around golang.org/x/oauth2. 5 // Package auth implements a wrapper around golang.org/x/oauth2.
6 // 6 //
7 // Its main improvement is the on-disk cache for OAuth tokens, which is 7 // Its main improvement is the on-disk cache for OAuth tokens, which is
8 // especially important for 3-legged interactive OAuth flows: its usage 8 // especially important for 3-legged interactive OAuth flows: its usage
9 // eliminates annoying login prompts each time a program is used (because the 9 // eliminates annoying login prompts each time a program is used (because the
10 // refresh token can now be reused). The cache also allows to reduce unnecessary 10 // refresh token can now be reused). The cache also allows to reduce unnecessary
(...skipping 543 matching lines...) Expand 10 before | Expand all | Expand 10 after
554 return nil, err 554 return nil, err
555 case useAuth: 555 case useAuth:
556 return perRPCCreds{a}, nil // token-injecting PerRPCCredentials 556 return perRPCCreds{a}, nil // token-injecting PerRPCCredentials
557 default: 557 default:
558 return perRPCCreds{}, nil // noop PerRPCCredentials 558 return perRPCCreds{}, nil // noop PerRPCCredentials
559 } 559 }
560 } 560 }
561 561
562 // GetAccessToken returns a valid access token with specified minimum lifetime. 562 // GetAccessToken returns a valid access token with specified minimum lifetime.
563 // 563 //
564 //
565 // Does not interact with the user. May return ErrLoginRequired. 564 // Does not interact with the user. May return ErrLoginRequired.
566 func (a *Authenticator) GetAccessToken(lifetime time.Duration) (*oauth2.Token, e rror) { 565 func (a *Authenticator) GetAccessToken(lifetime time.Duration) (*oauth2.Token, e rror) {
567 tok, err := a.currentToken() 566 tok, err := a.currentToken()
568 if err != nil { 567 if err != nil {
569 return nil, err 568 return nil, err
570 } 569 }
571 if tok == nil || internal.TokenExpiresInRnd(a.ctx, tok, lifetime) { 570 if tok == nil || internal.TokenExpiresInRnd(a.ctx, tok, lifetime) {
571 // Give 5 sec extra to make sure callers definitely receive a to ken that
572 // has at least 'lifetime' seconds of life left. Without this ma rgin, we
573 // can get into an unlucky situation where the token is valid he re, but
574 // no longer valid (has fewer than 'lifetime' life left) up the stack, due
575 // to the passage of time.
572 var err error 576 var err error
573 » » tok, err = a.refreshToken(tok, lifetime) 577 » » tok, err = a.refreshToken(tok, lifetime+5*time.Second)
iannucci 2017/06/23 00:06:18 would it make sense to tie this to the context dea
Vadim Sh. 2017/06/23 00:11:59 I don't think if will provide any value. By existi
574 if err != nil { 578 if err != nil {
575 return nil, err 579 return nil, err
576 } 580 }
577 // Note: no randomization here. It is a sanity check that verifi es 581 // Note: no randomization here. It is a sanity check that verifi es
578 // refreshToken did its job. 582 // refreshToken did its job.
579 if internal.TokenExpiresIn(a.ctx, tok, lifetime) { 583 if internal.TokenExpiresIn(a.ctx, tok, lifetime) {
580 return nil, fmt.Errorf("auth: failed to refresh the toke n") 584 return nil, fmt.Errorf("auth: failed to refresh the toke n")
581 } 585 }
582 } 586 }
583 return tok, nil 587 return tok, nil
(...skipping 407 matching lines...) Expand 10 before | Expand all | Expand 10 after
991 }) 995 })
992 } 996 }
993 997
994 //////////////////////////////////////////////////////////////////////////////// 998 ////////////////////////////////////////////////////////////////////////////////
995 // Transport implementation. 999 // Transport implementation.
996 1000
997 // authTokenInjector injects an authentication token into request headers. 1001 // authTokenInjector injects an authentication token into request headers.
998 // 1002 //
999 // Used as a callback for NewModifyingTransport. 1003 // Used as a callback for NewModifyingTransport.
1000 func (a *Authenticator) authTokenInjector(req *http.Request) error { 1004 func (a *Authenticator) authTokenInjector(req *http.Request) error {
1001 » // Grab a currently known token or fail if 'ensureInitialized' failed. 1005 » switch tok, err := a.GetAccessToken(minAcceptedLifetime); {
1002 » tok, err := a.currentToken() 1006 » case err == ErrLoginRequired && a.effectiveLoginMode() == OptionalLogin:
1003 » if err != nil { 1007 » » return nil // skip auth, no need for modifications
1008 » case err != nil:
1004 return err 1009 return err
1010 default:
1011 tok.SetAuthHeader(req)
1012 return nil
1005 } 1013 }
1006
1007 // Attempt to refresh the token, if required. Revert to non-authed call if
1008 // token can't be refreshed and running in OptionalLogin mode.
1009 if tok == nil || internal.TokenExpiresInRnd(a.ctx, tok, minAcceptedLifet ime) {
1010 var err error
1011 tok, err = a.refreshToken(tok, minAcceptedLifetime)
1012 switch {
1013 case err == ErrLoginRequired && a.effectiveLoginMode() == Option alLogin:
1014 return nil // skip auth, no need for modifications
1015 case err != nil:
1016 return err
1017 // Note: no randomization here. It is a sanity check that verifi es
1018 // refreshToken did its job.
1019 case internal.TokenExpiresIn(a.ctx, tok, minAcceptedLifetime):
1020 return fmt.Errorf("auth: failed to refresh the token")
1021 }
1022 }
1023 tok.SetAuthHeader(req)
1024 return nil
1025 } 1014 }
1026 1015
1027 //////////////////////////////////////////////////////////////////////////////// 1016 ////////////////////////////////////////////////////////////////////////////////
1028 // tokenWithProvider implementation. 1017 // tokenWithProvider implementation.
1029 1018
1030 // tokenWithProvider wraps a token with provider that can update it and a cache 1019 // tokenWithProvider wraps a token with provider that can update it and a cache
1031 // that stores it. 1020 // that stores it.
1032 type tokenWithProvider struct { 1021 type tokenWithProvider struct {
1033 token *oauth2.Token // in-memory cache of the token 1022 token *oauth2.Token // in-memory cache of the token
1034 provider internal.TokenProvider // knows how to generate 'token' 1023 provider internal.TokenProvider // knows how to generate 'token'
(...skipping 238 matching lines...) Expand 10 before | Expand all | Expand 10 after
1273 func makeIAMTokenProvider(ctx context.Context, opts *Options) (internal.TokenPro vider, error) { 1262 func makeIAMTokenProvider(ctx context.Context, opts *Options) (internal.TokenPro vider, error) {
1274 if opts.testingIAMTokenProvider != nil { 1263 if opts.testingIAMTokenProvider != nil {
1275 return opts.testingIAMTokenProvider, nil 1264 return opts.testingIAMTokenProvider, nil
1276 } 1265 }
1277 return internal.NewIAMTokenProvider( 1266 return internal.NewIAMTokenProvider(
1278 ctx, 1267 ctx,
1279 opts.ActAsServiceAccount, 1268 opts.ActAsServiceAccount,
1280 opts.Scopes, 1269 opts.Scopes,
1281 opts.Transport) 1270 opts.Transport)
1282 } 1271 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698