Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1188e6c

Browse files
committed
test(httpmw): deflake RFC6750 bearer token tests on Windows
Fixes flake where subtests used t.Parallel() while sharing a DB from dbtestutil.NewDB(t). On Windows, the parent test’s t.Cleanup closed the DB before parallel subtests finished, yielding intermittent 401 Unauthorized instead of 200. Serializing the subtests keeps the DB alive for each case and removes the flake. Also trims whitespace from Authorization: Bearer and access_token token extraction for robustness; no change to precedence or overall behavior. Change-Id: Ifa9ad5bb297dc96612dbfb9ef8bb0e16d8ce5778 Signed-off-by: Thomas Kosiewski <[email protected]>
1 parent 6d39077 commit 1188e6c

File tree

3 files changed

+13
-38
lines changed

3 files changed

+13
-38
lines changed

coderd/httpmw/apikey.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -730,13 +730,14 @@ func APITokenFromRequest(r *http.Request) string {
730730
// Check Authorization: Bearer <token> header (case-insensitive per RFC 6750)
731731
authHeader := r.Header.Get("Authorization")
732732
if strings.HasPrefix(strings.ToLower(authHeader), "bearer ") {
733-
return authHeader[7:] // Skip "Bearer " (7 characters)
733+
// Skip "Bearer " (7 characters) and trim surrounding whitespace
734+
return strings.TrimSpace(authHeader[7:])
734735
}
735736

736737
// Check access_token query parameter
737738
accessToken := r.URL.Query().Get("access_token")
738739
if accessToken != "" {
739-
return accessToken
740+
return strings.TrimSpace(accessToken)
740741
}
741742

742743
return ""

coderd/httpmw/rfc6750_extended_test.go

Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import (
2020
)
2121

2222
// TestOAuth2BearerTokenSecurityBoundaries tests RFC 6750 security boundaries
23+
//
24+
//nolint:tparallel,paralleltest // Subtests share a DB; run sequentially to avoid Windows DB cleanup flake.
2325
func TestOAuth2BearerTokenSecurityBoundaries(t *testing.T) {
2426
t.Parallel()
2527

@@ -41,8 +43,6 @@ func TestOAuth2BearerTokenSecurityBoundaries(t *testing.T) {
4143
})
4244

4345
t.Run("TokenIsolation", func(t *testing.T) {
44-
t.Parallel()
45-
4646
// Create middleware
4747
middleware := httpmw.ExtractAPIKeyMW(httpmw.ExtractAPIKeyConfig{
4848
DB: db,
@@ -78,8 +78,6 @@ func TestOAuth2BearerTokenSecurityBoundaries(t *testing.T) {
7878
})
7979

8080
t.Run("CrossTokenAttempts", func(t *testing.T) {
81-
t.Parallel()
82-
8381
middleware := httpmw.ExtractAPIKeyMW(httpmw.ExtractAPIKeyConfig{
8482
DB: db,
8583
})
@@ -101,8 +99,6 @@ func TestOAuth2BearerTokenSecurityBoundaries(t *testing.T) {
10199
})
102100

103101
t.Run("TimingAttackResistance", func(t *testing.T) {
104-
t.Parallel()
105-
106102
middleware := httpmw.ExtractAPIKeyMW(httpmw.ExtractAPIKeyConfig{
107103
DB: db,
108104
})
@@ -150,6 +146,8 @@ func TestOAuth2BearerTokenSecurityBoundaries(t *testing.T) {
150146
}
151147

152148
// TestOAuth2BearerTokenMalformedHeaders tests handling of malformed Bearer headers per RFC 6750
149+
//
150+
//nolint:tparallel,paralleltest // Subtests share a DB; run sequentially to avoid Windows DB cleanup flake.
153151
func TestOAuth2BearerTokenMalformedHeaders(t *testing.T) {
154152
t.Parallel()
155153

@@ -215,8 +213,6 @@ func TestOAuth2BearerTokenMalformedHeaders(t *testing.T) {
215213

216214
for _, test := range tests {
217215
t.Run(test.name, func(t *testing.T) {
218-
t.Parallel()
219-
220216
req := httptest.NewRequest("GET", "/test", nil)
221217
req.Header.Set("Authorization", test.authHeader)
222218
rec := httptest.NewRecorder()
@@ -234,6 +230,8 @@ func TestOAuth2BearerTokenMalformedHeaders(t *testing.T) {
234230
}
235231

236232
// TestOAuth2BearerTokenPrecedence tests token extraction precedence per RFC 6750
233+
//
234+
//nolint:tparallel,paralleltest // Subtests share a DB; run sequentially to avoid Windows DB cleanup flake.
237235
func TestOAuth2BearerTokenPrecedence(t *testing.T) {
238236
t.Parallel()
239237

@@ -257,8 +255,6 @@ func TestOAuth2BearerTokenPrecedence(t *testing.T) {
257255
}))
258256

259257
t.Run("CookieTakesPrecedenceOverBearer", func(t *testing.T) {
260-
t.Parallel()
261-
262258
req := httptest.NewRequest("GET", "/test", nil)
263259
// Set both cookie and Bearer header - cookie should take precedence
264260
req.AddCookie(&http.Cookie{
@@ -274,8 +270,6 @@ func TestOAuth2BearerTokenPrecedence(t *testing.T) {
274270
})
275271

276272
t.Run("QueryParameterTakesPrecedenceOverBearer", func(t *testing.T) {
277-
t.Parallel()
278-
279273
// Set both query parameter and Bearer header - query should take precedence
280274
u, _ := url.Parse("/test")
281275
q := u.Query()
@@ -292,8 +286,6 @@ func TestOAuth2BearerTokenPrecedence(t *testing.T) {
292286
})
293287

294288
t.Run("BearerHeaderFallback", func(t *testing.T) {
295-
t.Parallel()
296-
297289
// Only set Bearer header - should be used as fallback
298290
req := httptest.NewRequest("GET", "/test", nil)
299291
req.Header.Set("Authorization", "Bearer "+validToken)
@@ -305,8 +297,6 @@ func TestOAuth2BearerTokenPrecedence(t *testing.T) {
305297
})
306298

307299
t.Run("AccessTokenQueryParameterFallback", func(t *testing.T) {
308-
t.Parallel()
309-
310300
// Only set access_token query parameter - should be used as fallback
311301
u, _ := url.Parse("/test")
312302
q := u.Query()
@@ -322,8 +312,6 @@ func TestOAuth2BearerTokenPrecedence(t *testing.T) {
322312
})
323313

324314
t.Run("MultipleAuthMethodsShouldNotConflict", func(t *testing.T) {
325-
t.Parallel()
326-
327315
// RFC 6750 says clients shouldn't send tokens in multiple ways,
328316
// but if they do, we should handle it gracefully by using precedence
329317
u, _ := url.Parse("/test")
@@ -348,6 +336,8 @@ func TestOAuth2BearerTokenPrecedence(t *testing.T) {
348336
}
349337

350338
// TestOAuth2WWWAuthenticateCompliance tests WWW-Authenticate header compliance with RFC 6750
339+
//
340+
//nolint:tparallel,paralleltest // Subtests share a DB; run sequentially to avoid Windows DB cleanup flake.
351341
func TestOAuth2WWWAuthenticateCompliance(t *testing.T) {
352342
t.Parallel()
353343

@@ -363,8 +353,6 @@ func TestOAuth2WWWAuthenticateCompliance(t *testing.T) {
363353
}))
364354

365355
t.Run("UnauthorizedResponse", func(t *testing.T) {
366-
t.Parallel()
367-
368356
req := httptest.NewRequest("GET", "/test", nil)
369357
req.Header.Set("Authorization", "Bearer invalid-token")
370358
rec := httptest.NewRecorder()
@@ -383,8 +371,6 @@ func TestOAuth2WWWAuthenticateCompliance(t *testing.T) {
383371
})
384372

385373
t.Run("ExpiredTokenResponse", func(t *testing.T) {
386-
t.Parallel()
387-
388374
// Create an expired API key
389375
_, expiredToken := dbgen.APIKey(t, db, database.APIKey{
390376
UserID: user.ID,
@@ -406,8 +392,6 @@ func TestOAuth2WWWAuthenticateCompliance(t *testing.T) {
406392
})
407393

408394
t.Run("InsufficientScopeResponse", func(t *testing.T) {
409-
t.Parallel()
410-
411395
// For this test, we'll test with an invalid token to trigger the middleware's
412396
// error handling which does set WWW-Authenticate headers for 403 responses
413397
// In practice, insufficient scope errors would be handled by RBAC middleware

coderd/httpmw/rfc6750_test.go

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import (
1919

2020
// TestRFC6750BearerTokenAuthentication tests that RFC 6750 bearer tokens work correctly
2121
// for authentication, including both Authorization header and access_token query parameter methods.
22+
//
23+
//nolint:tparallel,paralleltest // Subtests share a DB; run sequentially to avoid Windows DB cleanup flake.
2224
func TestRFC6750BearerTokenAuthentication(t *testing.T) {
2325
t.Parallel()
2426

@@ -44,8 +46,6 @@ func TestRFC6750BearerTokenAuthentication(t *testing.T) {
4446
})
4547

4648
t.Run("AuthorizationBearerHeader", func(t *testing.T) {
47-
t.Parallel()
48-
4949
req := httptest.NewRequest("GET", "/test", nil)
5050
req.Header.Set("Authorization", "Bearer "+token)
5151

@@ -57,8 +57,6 @@ func TestRFC6750BearerTokenAuthentication(t *testing.T) {
5757
})
5858

5959
t.Run("AccessTokenQueryParameter", func(t *testing.T) {
60-
t.Parallel()
61-
6260
req := httptest.NewRequest("GET", "/test?access_token="+url.QueryEscape(token), nil)
6361

6462
rw := httptest.NewRecorder()
@@ -69,8 +67,6 @@ func TestRFC6750BearerTokenAuthentication(t *testing.T) {
6967
})
7068

7169
t.Run("BearerTokenPriorityAfterCustomMethods", func(t *testing.T) {
72-
t.Parallel()
73-
7470
// Create a different token for custom header
7571
customKey, customToken := dbgen.APIKey(t, db, database.APIKey{
7672
UserID: user.ID,
@@ -98,8 +94,6 @@ func TestRFC6750BearerTokenAuthentication(t *testing.T) {
9894
})
9995

10096
t.Run("InvalidBearerToken", func(t *testing.T) {
101-
t.Parallel()
102-
10397
req := httptest.NewRequest("GET", "/test", nil)
10498
req.Header.Set("Authorization", "Bearer invalid-token")
10599

@@ -118,8 +112,6 @@ func TestRFC6750BearerTokenAuthentication(t *testing.T) {
118112
})
119113

120114
t.Run("ExpiredBearerToken", func(t *testing.T) {
121-
t.Parallel()
122-
123115
// Create an expired token
124116
_, expiredToken := dbgen.APIKey(t, db, database.APIKey{
125117
UserID: user.ID,
@@ -144,8 +136,6 @@ func TestRFC6750BearerTokenAuthentication(t *testing.T) {
144136
})
145137

146138
t.Run("MissingBearerToken", func(t *testing.T) {
147-
t.Parallel()
148-
149139
req := httptest.NewRequest("GET", "/test", nil)
150140
// No authentication provided
151141

0 commit comments

Comments
 (0)