Skip to content

Commit dbabe66

Browse files
authored
oauth-manager: fix evaluation of should refresh function (#280)
* oauth-manager: fix evaluation of should refresh function
1 parent 8b33861 commit dbabe66

8 files changed

Lines changed: 88 additions & 29 deletions

File tree

coap-gateway/schema/device/status/status.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,5 @@ func (s Status) IsOnline() bool {
3636
if s.ValidUntil <= 0 {
3737
return s.State == State_Online
3838
}
39-
return time.Now().Before(time.Unix(s.ValidUntil, 0))
39+
return time.Now().UnixNano() < time.Unix(s.ValidUntil, 0).UnixNano()
4040
}

coap-gateway/service/auth.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111
)
1212

1313
func isExpired(e time.Time) bool {
14-
return !e.IsZero() && time.Now().After(e)
14+
return !e.IsZero() && e.UnixNano() < time.Now().UnixNano()
1515
}
1616

1717
func NewAuthInterceptor() kitNetCoap.Interceptor {

coap-gateway/service/client.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@ func (a *authCtx) GetDeviceId() string {
5252
return ""
5353
}
5454

55+
func (a *authCtx) GetAccessToken() string {
56+
if a != nil {
57+
return a.AccessToken
58+
}
59+
return ""
60+
}
61+
5562
const pendingDeviceSubscriptionToken = "pending"
5663

5764
//Client a setup of connection
@@ -115,7 +122,7 @@ func (client *Client) cancelResourceSubscription(token string, wantWait bool) (b
115122
}
116123

117124
func (client *Client) observeResource(ctx context.Context, deviceID, href string, observable, allowDuplicit bool) (err error) {
118-
log.Debugf("coap-gw: client.observeResource /%v%v ins %v: observe resource", deviceID, href)
125+
log.Debugf("coap-gw: observing resource /%v%v", deviceID, href)
119126
instanceID := getInstanceID(href)
120127
client.observedResourcesLock.Lock()
121128
defer client.observedResourcesLock.Unlock()

coap-gateway/service/clientDeleteHandler_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,9 @@ func Test_clientDeleteHandler(t *testing.T) {
7979
ctx, cancel := context.WithTimeout(context.Background(), TestExchangeTimeout)
8080
defer cancel()
8181
req, err := tcp.NewDeleteRequest(ctx, tt.args.path)
82+
require.NoError(t, err)
8283
resp, err := co.Do(req)
83-
assert.NoError(t, err)
84+
require.NoError(t, err)
8485
assert.Equal(t, tt.wantsCode.String(), resp.Code().String())
8586
if tt.wantsContent != nil {
8687
require.NotEmpty(t, resp.Body())

coap-gateway/service/devicesStatusUpdater.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ func (u *devicesStatusUpdater) updateOnlineStatus(client *Client, validUntil tim
6666
return time.Time{}, fmt.Errorf("cannot get service token: %w", err)
6767
}
6868
ctx := kitNetGrpc.CtxWithUserID(kitNetGrpc.CtxWithToken(client.Context(), serviceToken.AccessToken), authCtx.GetUserID())
69-
if authCtx.Expire.Before(validUntil) {
69+
if authCtx.Expire.UnixNano() < validUntil.UnixNano() {
7070
validUntil = authCtx.Expire
7171
}
7272

@@ -85,7 +85,7 @@ func (u *devicesStatusUpdater) getDevicesToUpdate(now time.Time) []*deviceExpire
8585
case <-d.client.Context().Done():
8686
delete(u.devices, key)
8787
default:
88-
if now.Add(u.deviceStatusValidity / 2).After(d.expires) {
88+
if d.expires.UnixNano() < now.Add(u.deviceStatusValidity/2).UnixNano() {
8989
res = append(res, d)
9090
}
9191
}

coap-gateway/service/signUp.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,9 @@ func signOffHandler(s mux.ResponseWriter, req *mux.Message, client *Client) {
153153
if deviceID == "" {
154154
deviceID = authCurrentCtx.GetDeviceId()
155155
}
156+
if accessToken == "" {
157+
accessToken = authCurrentCtx.GetAccessToken()
158+
}
156159

157160
err := validateSignOff(deviceID, accessToken)
158161
if err != nil {

coap-gateway/service/signUp_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,32 @@ func TestSignOffHandler(t *testing.T) {
7474
t.Run(test.name, tf)
7575
}
7676
}
77+
78+
func TestSignOffWithSignInHandler(t *testing.T) {
79+
shutdown := setUp(t)
80+
defer shutdown()
81+
82+
co := testCoapDial(t, testCfg.GW_HOST)
83+
if co == nil {
84+
return
85+
}
86+
defer co.Close()
87+
88+
signUpEl := testEl{"signUp", input{coapCodes.POST, `{"di": "` + CertIdentity + `", "accesstoken":"` + oauthTest.DeviceAccessToken + `", "authprovider": "` + oauthTest.NewTestProvider().GetProviderName() + `"}`, nil}, output{coapCodes.Changed, TestCoapSignUpResponse{RefreshToken: "refresh-token", UserID: AuthorizationUserId}, nil}}
89+
testPostHandler(t, uri.SignUp, signUpEl, co)
90+
91+
signInEl := testEl{"signIn", input{coapCodes.POST, `{"di": "` + CertIdentity + `", "uid":"` + AuthorizationUserId + `", "accesstoken":"` + oauthTest.DeviceAccessToken + `", "login": true }`, nil}, output{coapCodes.Changed, TestCoapSignInResponse{}, nil}}
92+
testPostHandler(t, uri.SignIn, signInEl, co)
93+
94+
tbl := []testEl{
95+
{"Deleted", input{coapCodes.DELETE, `{}`, nil}, output{coapCodes.Deleted, nil, nil}},
96+
}
97+
98+
for _, test := range tbl {
99+
tf := func(t *testing.T) {
100+
// delete record for signUp
101+
testPostHandler(t, uri.SignUp, test, co)
102+
}
103+
t.Run(test.name, tf)
104+
}
105+
}

pkg/security/oauth/manager/manager.go

Lines changed: 42 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,16 @@ import (
1515

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

3030
// NewManagerFromConfiguration creates a new oauth manager which refreshing token.
@@ -40,18 +40,19 @@ func NewManagerFromConfiguration(config Config, tlsCfg *tls.Config) (*Manager, e
4040
Transport: t,
4141
Timeout: config.RequestTimeout,
4242
}
43-
token, startRefreshToken, err := getToken(cfg, httpClient, config.RequestTimeout)
43+
token, nextTokenRenewalTime, err := getToken(cfg, httpClient, config.RequestTimeout)
4444
if err != nil {
4545
return nil, err
4646
}
47+
log.Infof("client credential token is refreshed, the next refresh token occurs after %v", nextTokenRenewalTime)
4748

4849
mgr := &Manager{
49-
config: cfg,
50-
token: token,
51-
startRefreshToken: startRefreshToken,
52-
requestTimeout: config.RequestTimeout,
53-
httpClient: httpClient,
54-
tickFrequency: config.TickFrequency,
50+
config: cfg,
51+
token: token,
52+
nextTokenRenewalTime: nextTokenRenewalTime,
53+
requestTimeout: config.RequestTimeout,
54+
httpClient: httpClient,
55+
tickFrequency: config.TickFrequency,
5556

5657
done: make(chan struct{}),
5758
}
@@ -78,7 +79,23 @@ func (a *Manager) Close() {
7879
}
7980

8081
func (a *Manager) shouldRefresh() bool {
81-
return time.Now().After(a.startRefreshToken)
82+
/*
83+
We cannot use time.Now().After(a.nextTokenRenewalTime ) because
84+
golang using monotonic clock for comparision.
85+
86+
So if we have 2 times:
87+
// update time on PC to future eg: `date MMDDHHMM`
88+
t1 := time.Now() eg (2021-06-15T12:00:00)
89+
// return back time on PC: `date MMDDHHMM`
90+
t2 := time.Now() eg (2021-06-01T12:00:00)
91+
and then you call t2.After(t1) - it's return true :)
92+
93+
more info: https://github.com/golang/go/blob/master/src/time/time.go
94+
95+
the issue can occurs when pc hibernates.
96+
*/
97+
98+
return time.Now().UnixNano() > a.nextTokenRenewalTime.UnixNano()
8299
}
83100

84101
func getToken(cfg clientcredentials.Config, httpClient *http.Client, requestTimeout time.Duration) (*oauth2.Token, time.Time, error) {
@@ -88,24 +105,26 @@ func getToken(cfg clientcredentials.Config, httpClient *http.Client, requestTime
88105
ctx = context.WithValue(ctx, oauth2.HTTPClient, httpClient)
89106

90107
token, err := cfg.Token(ctx)
91-
var startRefreshToken time.Time
108+
var nextTokenRenewalTime time.Time
92109
if err == nil {
93110
now := time.Now()
94-
startRefreshToken = now.Add(token.Expiry.Sub(now) * 2 / 3)
111+
nextTokenRenewalTime = now.Add(token.Expiry.Sub(now) * 2 / 3)
95112
}
96-
return token, startRefreshToken, err
113+
return token, nextTokenRenewalTime, err
97114
}
98115

99116
func (a *Manager) refreshToken() {
100-
token, startRefreshToken, err := getToken(a.config, a.httpClient, a.requestTimeout)
117+
token, nextTokenRenewalTime, err := getToken(a.config, a.httpClient, a.requestTimeout)
101118
if err != nil {
102119
log.Errorf("cannot refresh token: %v", err)
120+
} else {
121+
log.Infof("client credential token is refreshed, the next refresh token occurs after %v", nextTokenRenewalTime)
103122
}
104123
a.mutex.Lock()
105124
defer a.mutex.Unlock()
106125
a.token = token
107126
a.tokenErr = err
108-
a.startRefreshToken = startRefreshToken
127+
a.nextTokenRenewalTime = nextTokenRenewalTime
109128
}
110129

111130
func (a *Manager) watchToken() {

0 commit comments

Comments
 (0)