Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit5f8826f

Browse files
Emyrkkylecarbs
authored andcommitted
feat: Longer lived api keys for cli (#1935)
* feat: Longer lived api keys for cli* feat: Refresh tokens based on their lifetime set in the db* test: Add unit test for refreshing
1 parenta6b8569 commit5f8826f

File tree

10 files changed

+147
-8
lines changed

10 files changed

+147
-8
lines changed

‎coderd/database/databasefake/databasefake.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1128,9 +1128,14 @@ func (q *fakeQuerier) InsertAPIKey(_ context.Context, arg database.InsertAPIKeyP
11281128
q.mutex.Lock()
11291129
deferq.mutex.Unlock()
11301130

1131+
ifarg.LifetimeSeconds==0 {
1132+
arg.LifetimeSeconds=86400
1133+
}
1134+
11311135
//nolint:gosimple
11321136
key:= database.APIKey{
11331137
ID:arg.ID,
1138+
LifetimeSeconds:arg.LifetimeSeconds,
11341139
HashedSecret:arg.HashedSecret,
11351140
UserID:arg.UserID,
11361141
ExpiresAt:arg.ExpiresAt,

‎coderd/database/dump.sql

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ALTERTABLE api_keys DROP COLUMN lifetime_seconds;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- Default lifetime is 24hours.
2+
ALTERTABLE api_keys ADD COLUMN lifetime_secondsbigint default86400NOT NULL;

‎coderd/database/models.go

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/database/queries.sql.go

Lines changed: 13 additions & 2 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/database/queries/apikeys.sql

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ LIMIT
1212
INSERT INTO
1313
api_keys (
1414
id,
15+
lifetime_seconds,
1516
hashed_secret,
1617
user_id,
1718
last_used,
@@ -25,7 +26,13 @@ INSERT INTO
2526
oauth_expiry
2627
)
2728
VALUES
28-
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12) RETURNING*;
29+
(@id,
30+
-- If the lifetime is set to 0, default to 24hrs
31+
CASE @lifetime_seconds::bigint
32+
WHEN0 THEN86400
33+
ELSE @lifetime_seconds::bigint
34+
END
35+
, @hashed_secret, @user_id, @last_used, @expires_at, @created_at, @updated_at, @login_type, @oauth_access_token, @oauth_refresh_token, @oauth_id_token, @oauth_expiry) RETURNING*;
2936

3037
-- name: UpdateAPIKeyByID :exec
3138
UPDATE

‎coderd/httpmw/apikey.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs) func(http.Handler) h
166166
}
167167
// Only update the ExpiresAt once an hour to prevent database spam.
168168
// We extend the ExpiresAt to reduce re-authentication.
169-
apiKeyLifetime:=24*time.Hour
169+
apiKeyLifetime:=time.Duration(key.LifetimeSeconds)*time.Second
170170
ifkey.ExpiresAt.Sub(now)<=apiKeyLifetime-time.Hour {
171171
key.ExpiresAt=now.Add(apiKeyLifetime)
172172
changed=true

‎coderd/users.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -660,9 +660,14 @@ func (api *API) postAPIKey(rw http.ResponseWriter, r *http.Request) {
660660
return
661661
}
662662

663+
lifeTime:=time.Hour*24*7
663664
sessionToken,created:=api.createAPIKey(rw,r, database.InsertAPIKeyParams{
664665
UserID:user.ID,
665666
LoginType:database.LoginTypePassword,
667+
// All api generated keys will last 1 week. Browser login tokens have
668+
// a shorter life.
669+
ExpiresAt:database.Now().Add(lifeTime),
670+
LifetimeSeconds:int64(lifeTime.Seconds()),
666671
})
667672
if!created {
668673
return
@@ -723,10 +728,21 @@ func (api *API) createAPIKey(rw http.ResponseWriter, r *http.Request, params dat
723728
}
724729
hashed:=sha256.Sum256([]byte(keySecret))
725730

731+
// Default expires at to now+lifetime, or just 24hrs if not set
732+
ifparams.ExpiresAt.IsZero() {
733+
ifparams.LifetimeSeconds!=0 {
734+
params.ExpiresAt=database.Now().Add(time.Duration(params.LifetimeSeconds)*time.Second)
735+
}else {
736+
params.ExpiresAt=database.Now().Add(24*time.Hour)
737+
}
738+
}
739+
726740
_,err=api.Database.InsertAPIKey(r.Context(), database.InsertAPIKeyParams{
727-
ID:keyID,
728-
UserID:params.UserID,
729-
ExpiresAt:database.Now().Add(24*time.Hour),
741+
ID:keyID,
742+
UserID:params.UserID,
743+
LifetimeSeconds:params.LifetimeSeconds,
744+
// Make sure in UTC time for common time zone
745+
ExpiresAt:params.ExpiresAt.UTC(),
730746
CreatedAt:database.Now(),
731747
UpdatedAt:database.Now(),
732748
HashedSecret:hashed[:],

‎coderd/users_test.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@ import (
88
"sort"
99
"strings"
1010
"testing"
11+
"time"
1112

1213
"github.com/google/uuid"
1314
"github.com/stretchr/testify/require"
1415

1516
"github.com/coder/coder/coderd/coderdtest"
17+
"github.com/coder/coder/coderd/database"
1618
"github.com/coder/coder/coderd/database/databasefake"
1719
"github.com/coder/coder/coderd/httpmw"
1820
"github.com/coder/coder/coderd/rbac"
@@ -130,6 +132,99 @@ func TestPostLogin(t *testing.T) {
130132
})
131133
require.NoError(t,err)
132134
})
135+
136+
t.Run("Lifetime&Expire",func(t*testing.T) {
137+
t.Parallel()
138+
var (
139+
ctx=context.Background()
140+
)
141+
client,api:=coderdtest.NewWithAPI(t,nil)
142+
admin:=coderdtest.CreateFirstUser(t,client)
143+
144+
split:=strings.Split(client.SessionToken,"-")
145+
loginKey,err:=api.Database.GetAPIKeyByID(ctx,split[0])
146+
require.NoError(t,err,"fetch login key")
147+
require.Equal(t,int64(86400),loginKey.LifetimeSeconds,"default should be 86400")
148+
149+
// Generated tokens have a longer life
150+
token,err:=client.CreateAPIKey(ctx,admin.UserID.String())
151+
require.NoError(t,err,"make new api key")
152+
split=strings.Split(token.Key,"-")
153+
apiKey,err:=api.Database.GetAPIKeyByID(ctx,split[0])
154+
require.NoError(t,err,"fetch api key")
155+
156+
require.True(t,apiKey.ExpiresAt.After(time.Now().Add(time.Hour*24*6)),"api key lasts more than 6 days")
157+
require.True(t,apiKey.ExpiresAt.After(loginKey.ExpiresAt.Add(time.Hour)),"api key should be longer expires")
158+
require.Greater(t,apiKey.LifetimeSeconds,loginKey.LifetimeSeconds,"api key should have longer lifetime")
159+
})
160+
161+
t.Run("APIKeyExtend",func(t*testing.T) {
162+
t.Parallel()
163+
var (
164+
ctx=context.Background()
165+
)
166+
client,api:=coderdtest.NewWithAPI(t,nil)
167+
admin:=coderdtest.CreateFirstUser(t,client)
168+
169+
token,err:=client.CreateAPIKey(ctx,admin.UserID.String())
170+
require.NoError(t,err,"make new api key")
171+
client.SessionToken=token.Key
172+
split:=strings.Split(token.Key,"-")
173+
174+
apiKey,err:=api.Database.GetAPIKeyByID(ctx,split[0])
175+
require.NoError(t,err,"fetch api key")
176+
177+
err=api.Database.UpdateAPIKeyByID(ctx, database.UpdateAPIKeyByIDParams{
178+
ID:apiKey.ID,
179+
LastUsed:apiKey.LastUsed,
180+
// This should cause a refresh
181+
ExpiresAt:apiKey.ExpiresAt.Add(time.Hour*-2),
182+
OAuthAccessToken:apiKey.OAuthAccessToken,
183+
OAuthRefreshToken:apiKey.OAuthRefreshToken,
184+
OAuthExpiry:apiKey.OAuthExpiry,
185+
})
186+
require.NoError(t,err,"update api key")
187+
188+
_,err=client.User(ctx,codersdk.Me)
189+
require.NoError(t,err,"fetch user")
190+
191+
apiKey,err=api.Database.GetAPIKeyByID(ctx,split[0])
192+
require.NoError(t,err,"fetch refreshed api key")
193+
// 1 minute tolerance
194+
require.True(t,apiKey.ExpiresAt.After(time.Now().Add(time.Hour*24*7).Add(time.Minute*-1)),"api key lasts 7 days")
195+
})
196+
197+
t.Run("LoginKeyExtend",func(t*testing.T) {
198+
t.Parallel()
199+
var (
200+
ctx=context.Background()
201+
)
202+
client,api:=coderdtest.NewWithAPI(t,nil)
203+
_=coderdtest.CreateFirstUser(t,client)
204+
split:=strings.Split(client.SessionToken,"-")
205+
206+
apiKey,err:=api.Database.GetAPIKeyByID(ctx,split[0])
207+
require.NoError(t,err,"fetch login key")
208+
209+
err=api.Database.UpdateAPIKeyByID(ctx, database.UpdateAPIKeyByIDParams{
210+
ID:apiKey.ID,
211+
LastUsed:apiKey.LastUsed,
212+
// This should cause a refresh
213+
ExpiresAt:apiKey.ExpiresAt.Add(time.Hour*-2),
214+
OAuthAccessToken:apiKey.OAuthAccessToken,
215+
OAuthRefreshToken:apiKey.OAuthRefreshToken,
216+
OAuthExpiry:apiKey.OAuthExpiry,
217+
})
218+
require.NoError(t,err,"update login key")
219+
220+
_,err=client.User(ctx,codersdk.Me)
221+
require.NoError(t,err,"fetch user")
222+
223+
apiKey,err=api.Database.GetAPIKeyByID(ctx,split[0])
224+
require.NoError(t,err,"fetch refreshed login key")
225+
// 1 minute tolerance
226+
require.True(t,apiKey.ExpiresAt.After(time.Now().Add(time.Hour*24).Add(time.Minute*-1)),"login key lasts 24 hrs")
227+
})
133228
}
134229

135230
funcTestPostLogout(t*testing.T) {

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp