TestOAuth2WWWAuthenticateCompliance tests WWW-Authenticate header compliance with RFC 6750 nolint:tparallel,paralleltest // Subtests share a DB; run sequentially to avoid Windows DB cleanup flake.
(t *testing.T)
| 339 | // |
| 340 | //nolint:tparallel,paralleltest // Subtests share a DB; run sequentially to avoid Windows DB cleanup flake. |
| 341 | func TestOAuth2WWWAuthenticateCompliance(t *testing.T) { |
| 342 | t.Parallel() |
| 343 | |
| 344 | db, _ := dbtestutil.NewDB(t) |
| 345 | user := dbgen.User(t, db, database.User{}) |
| 346 | |
| 347 | middleware := httpmw.ExtractAPIKeyMW(httpmw.ExtractAPIKeyConfig{ |
| 348 | DB: db, |
| 349 | }) |
| 350 | |
| 351 | handler := middleware(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { |
| 352 | rw.WriteHeader(http.StatusOK) |
| 353 | })) |
| 354 | |
| 355 | t.Run("UnauthorizedResponse", func(t *testing.T) { |
| 356 | req := httptest.NewRequest("GET", "/test", nil) |
| 357 | req.Header.Set("Authorization", "Bearer invalid-token") |
| 358 | rec := httptest.NewRecorder() |
| 359 | handler.ServeHTTP(rec, req) |
| 360 | |
| 361 | require.Equal(t, http.StatusUnauthorized, rec.Code) |
| 362 | |
| 363 | wwwAuth := rec.Header().Get("WWW-Authenticate") |
| 364 | require.NotEmpty(t, wwwAuth) |
| 365 | |
| 366 | // RFC 6750 requires specific format: Bearer realm="realm" |
| 367 | require.Contains(t, wwwAuth, "Bearer") |
| 368 | require.Contains(t, wwwAuth, "realm=\"coder\"") |
| 369 | require.Contains(t, wwwAuth, "error=\"invalid_token\"") |
| 370 | require.Contains(t, wwwAuth, "error_description=") |
| 371 | }) |
| 372 | |
| 373 | t.Run("ExpiredTokenResponse", func(t *testing.T) { |
| 374 | // Create an expired API key |
| 375 | _, expiredToken := dbgen.APIKey(t, db, database.APIKey{ |
| 376 | UserID: user.ID, |
| 377 | ExpiresAt: dbtime.Now().Add(-time.Hour), // Expired 1 hour ago |
| 378 | }) |
| 379 | |
| 380 | req := httptest.NewRequest("GET", "/test", nil) |
| 381 | req.Header.Set("Authorization", "Bearer "+expiredToken) |
| 382 | rec := httptest.NewRecorder() |
| 383 | handler.ServeHTTP(rec, req) |
| 384 | |
| 385 | require.Equal(t, http.StatusUnauthorized, rec.Code) |
| 386 | |
| 387 | wwwAuth := rec.Header().Get("WWW-Authenticate") |
| 388 | require.Contains(t, wwwAuth, "Bearer") |
| 389 | require.Contains(t, wwwAuth, "realm=\"coder\"") |
| 390 | require.Contains(t, wwwAuth, "error=\"invalid_token\"") |
| 391 | require.Contains(t, wwwAuth, "error_description=\"The access token has expired\"") |
| 392 | }) |
| 393 | |
| 394 | t.Run("InsufficientScopeResponse", func(t *testing.T) { |
| 395 | // For this test, we'll test with an invalid token to trigger the middleware's |
| 396 | // error handling which does set WWW-Authenticate headers for 403 responses |
| 397 | // In practice, insufficient scope errors would be handled by RBAC middleware |
| 398 | // that comes after authentication, but we can simulate a 403 from the auth middleware |
nothing calls this directly
no test coverage detected