@@ -213,6 +213,26 @@ func ExtractAPIKey(rw http.ResponseWriter, r *http.Request, cfg ExtractAPIKeyCon
213
213
return nil, nil, false
214
214
}
215
215
216
+ // Add WWW-Authenticate header for 401/403 responses (RFC 6750)
217
+ if code == http.StatusUnauthorized || code == http.StatusForbidden {
218
+ // Basic Bearer challenge with realm
219
+ wwwAuth := `Bearer realm="coder"`
220
+
221
+ // Add error details based on the type of error
222
+ switch {
223
+ case strings.Contains(response.Message, "invalid") || strings.Contains(response.Detail, "invalid"):
224
+ wwwAuth = `Bearer realm="coder", error="invalid_token", error_description="The access token is invalid"`
225
+ case strings.Contains(response.Message, "expired") || strings.Contains(response.Detail, "expired"):
226
+ wwwAuth = `Bearer realm="coder", error="invalid_token", error_description="The access token has expired"`
227
+ case strings.Contains(response.Message, "audience") || strings.Contains(response.Message, "mismatch"):
228
+ wwwAuth = `Bearer realm="coder", error="invalid_token", error_description="The access token audience does not match this resource"`
229
+ case code == http.StatusForbidden:
230
+ wwwAuth = `Bearer realm="coder", error="insufficient_scope", error_description="The request requires higher privileges than provided by the access token"`
231
+ }
232
+
233
+ rw.Header().Set("WWW-Authenticate", wwwAuth)
234
+ }
235
+
216
236
httpapi.Write(ctx, rw, code, response)
217
237
return nil, nil, false
218
238
}
@@ -607,9 +627,14 @@ func UserRBACSubject(ctx context.Context, db database.Store, userID uuid.UUID, s
607
627
// 1: The cookie
608
628
// 2. The coder_session_token query parameter
609
629
// 3. The custom auth header
630
+ // 4. RFC 6750 Authorization: Bearer header
631
+ // 5. RFC 6750 access_token query parameter
610
632
//
611
633
// API tokens for apps are read from workspaceapps/cookies.go.
612
634
func APITokenFromRequest(r *http.Request) string {
635
+ // Prioritize existing Coder custom authentication methods first
636
+ // to maintain backward compatibility and existing behavior
637
+
613
638
cookie, err := r.Cookie(codersdk.SessionTokenCookie)
614
639
if err == nil && cookie.Value != "" {
615
640
return cookie.Value
@@ -625,7 +650,18 @@ func APITokenFromRequest(r *http.Request) string {
625
650
return headerValue
626
651
}
627
652
628
- // TODO(ThomasK33): Implement RFC 6750
653
+ // RFC 6750 Bearer Token support (added as fallback methods)
654
+ // Check Authorization: Bearer <token> header
655
+ authHeader := r.Header.Get("Authorization")
656
+ if strings.HasPrefix(authHeader, "Bearer ") {
657
+ return strings.TrimPrefix(authHeader, "Bearer ")
658
+ }
659
+
660
+ // Check access_token query parameter
661
+ accessToken := r.URL.Query().Get("access_token")
662
+ if accessToken != "" {
663
+ return accessToken
664
+ }
629
665
630
666
return ""
631
667
}
0 commit comments