Skip to content

Commit 408ae28

Browse files
committed
security/oauth/manager: introduce ticks to check expiration of tokens
it fixes issues when PC/VirtualPC was hibernated for time so refreshing the token can occurs long time after token expired.
1 parent 558c3bd commit 408ae28

2 files changed

Lines changed: 52 additions & 33 deletions

File tree

security/oauth/manager/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ type Config struct {
1818
Endpoint Endpoint `envconfig:"ENDPOINT" env:"ENDPOINT"`
1919
Audience string `envconfig:"AUDIENCE" env:"AUDIENCE"`
2020
RequestTimeout time.Duration `envconfig:"REQUEST_TIMEOUT" env:"REQUEST_TIMEOUT" default:"10s"`
21+
TickFrequency time.Duration `envconfig:"TICK_FREQUENCY" env:"TICK_FREQUENCY" long:"tick-frequency" description:"how frequently we should check whether our token needs renewal" default:"15s"`
2122
}
2223

2324
// ToClientCrendtials converts to clientcredentials.Config

security/oauth/manager/manager.go

Lines changed: 51 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -10,36 +10,52 @@ import (
1010
"golang.org/x/oauth2/clientcredentials"
1111

1212
"github.com/plgd-dev/kit/log"
13-
"github.com/plgd-dev/kit/net/http/transport"
1413
"golang.org/x/oauth2"
1514
)
1615

1716
// Manager holds certificates from filesystem watched for changes
1817
type Manager struct {
19-
mutex sync.Mutex
20-
config clientcredentials.Config
21-
tlsCfg *tls.Config
22-
requestTimeout time.Duration
23-
token *oauth2.Token
24-
tokenErr error
25-
doneWg sync.WaitGroup
26-
done chan struct{}
18+
mutex sync.Mutex
19+
config clientcredentials.Config
20+
tlsCfg *tls.Config
21+
requestTimeout time.Duration
22+
tickFrequency time.Duration
23+
startRefreshToken time.Time
24+
token *oauth2.Token
25+
httpClient *http.Client
26+
tokenErr error
27+
doneWg sync.WaitGroup
28+
done chan struct{}
2729
}
2830

2931
// NewManagerFromConfiguration creates a new oauth manager which refreshing token.
3032
func NewManagerFromConfiguration(config Config, tlsCfg *tls.Config) (*Manager, error) {
3133
cfg := config.ToClientCrendtials()
32-
token, err := getToken(cfg, tlsCfg, config.RequestTimeout)
34+
t := http.DefaultTransport.(*http.Transport).Clone()
35+
t.MaxIdleConns = 1
36+
t.MaxConnsPerHost = 1
37+
t.MaxIdleConnsPerHost = 1
38+
t.IdleConnTimeout = time.Second * 30
39+
t.TLSClientConfig = tlsCfg
40+
httpClient := &http.Client{
41+
Transport: t,
42+
Timeout: config.RequestTimeout,
43+
}
44+
token, startRefreshToken, err := getToken(cfg, httpClient, config.RequestTimeout)
3345
if err != nil {
3446
return nil, err
3547
}
36-
mgr := &Manager{
37-
config: cfg,
38-
token: token,
39-
tlsCfg: tlsCfg,
4048

41-
requestTimeout: config.RequestTimeout,
42-
done: make(chan struct{}),
49+
mgr := &Manager{
50+
config: cfg,
51+
token: token,
52+
tlsCfg: tlsCfg,
53+
startRefreshToken: startRefreshToken,
54+
requestTimeout: config.RequestTimeout,
55+
httpClient: httpClient,
56+
tickFrequency: config.TickFrequency,
57+
58+
done: make(chan struct{}),
4359
}
4460
mgr.doneWg.Add(1)
4561

@@ -63,48 +79,50 @@ func (a *Manager) Close() {
6379
}
6480
}
6581

66-
func (a *Manager) nextRenewal() time.Duration {
67-
t, _ := a.GetToken(context.Background())
68-
now := time.Now()
69-
lifetime := t.Expiry.Sub(now) * 2 / 3
70-
if lifetime < a.requestTimeout {
71-
lifetime = a.requestTimeout
72-
}
73-
return lifetime
82+
func (a *Manager) wantToRefresh() bool {
83+
return time.Now().After(a.startRefreshToken)
7484
}
7585

76-
func getToken(cfg clientcredentials.Config, tlsCfg *tls.Config, requestTimeout time.Duration) (*oauth2.Token, error) {
86+
func getToken(cfg clientcredentials.Config, httpClient *http.Client, requestTimeout time.Duration) (*oauth2.Token, time.Time, error) {
7787
ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
7888
defer cancel()
7989

80-
t := transport.NewDefaultTransport()
81-
t.TLSClientConfig = tlsCfg
82-
httpClient := &http.Client{Transport: t}
8390
ctx = context.WithValue(ctx, oauth2.HTTPClient, httpClient)
8491

85-
return cfg.Token(ctx)
92+
token, err := cfg.Token(ctx)
93+
var startRefreshToken time.Time
94+
if err == nil {
95+
now := time.Now()
96+
startRefreshToken = now.Add(token.Expiry.Sub(now) * 2 / 3)
97+
}
98+
return token, startRefreshToken, err
8699
}
87100

88101
func (a *Manager) refreshToken() {
89-
token, err := getToken(a.config, a.tlsCfg, a.requestTimeout)
102+
token, startRefreshToken, err := getToken(a.config, a.httpClient, a.requestTimeout)
90103
if err != nil {
91104
log.Errorf("cannot refresh token: %v", err)
92105
}
93106
a.mutex.Lock()
94107
defer a.mutex.Unlock()
95108
a.token = token
96109
a.tokenErr = err
110+
a.startRefreshToken = startRefreshToken
97111
}
98112

99113
func (a *Manager) watchToken() {
100114
defer a.doneWg.Done()
115+
t := time.NewTicker(a.tickFrequency)
116+
defer t.Stop()
117+
101118
for {
102-
nextRenewal := a.nextRenewal()
103119
select {
104120
case <-a.done:
105121
return
106-
case <-time.After(nextRenewal):
107-
a.refreshToken()
122+
case <-t.C:
123+
if a.wantToRefresh() {
124+
a.refreshToken()
125+
}
108126
}
109127
}
110128
}

0 commit comments

Comments
 (0)