Skip to content

Commit 97ac613

Browse files
committed
chore: swagger docs omit brower based credentials, rely on swagger auth
Swagger has an "Authorize" button which should be the only authentication being used in the api requests
1 parent 6c94dd4 commit 97ac613

File tree

2 files changed

+42
-1
lines changed

2 files changed

+42
-1
lines changed

coderd/coderd.go

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,31 @@ import (
8787
var globalHTTPSwaggerHandler http.HandlerFunc
8888

8989
func init() {
90-
globalHTTPSwaggerHandler = httpSwagger.Handler(httpSwagger.URL("/swagger/doc.json"))
90+
globalHTTPSwaggerHandler = httpSwagger.Handler(
91+
httpSwagger.URL("/swagger/doc.json"),
92+
// The swagger UI has an "Authorize" button that will input the
93+
// credentials into the Coder-Session-Token header. This bypasses
94+
// CSRF checks **if** there is no cookie auth also present.
95+
// (If the cookie matches, then it's ok too)
96+
//
97+
// Because swagger is hosted on the same domain, we have the cookie
98+
// auth and the header auth competing. This can cause CSRF errors,
99+
// and can be confusing what authentication is being used.
100+
//
101+
// So remove authenticating via a cookie, and rely on the authorization
102+
// header passed in.
103+
httpSwagger.UIConfig(map[string]string{
104+
// Pulled from https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/
105+
// 'withCredentials' should disable fetch sending browser credentials, but
106+
// for whatever reason it does not.
107+
// So this `requestInterceptor` ensures browser credentials are
108+
// omitted from all requests.
109+
"requestInterceptor": `(a => {
110+
a.credentials = "omit";
111+
return a;
112+
})`,
113+
"withCredentials": "false",
114+
}))
91115
}
92116

93117
var expDERPOnce = sync.Once{}

coderd/httpmw/csrf.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package httpmw
22

33
import (
4+
"fmt"
45
"net/http"
56
"regexp"
67
"strings"
@@ -20,6 +21,22 @@ func CSRF(secureCookie bool) func(next http.Handler) http.Handler {
2021
mw := nosurf.New(next)
2122
mw.SetBaseCookie(http.Cookie{Path: "/", HttpOnly: true, SameSite: http.SameSiteLaxMode, Secure: secureCookie})
2223
mw.SetFailureHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
24+
sessCookie, err := r.Cookie(codersdk.SessionTokenCookie)
25+
if err == nil && r.Header.Get(codersdk.SessionTokenHeader) != sessCookie.Value {
26+
// If a user is using header authentication and cookie auth, but the values
27+
// do not match, the cookie value takes priority.
28+
// At the very least, return a more helpful error to the user.
29+
// This happens frequently on the hosted swagger docs. There
30+
// is no easy way to disable cookies from
31+
http.Error(w,
32+
fmt.Sprintf("CSRF error encountered. Authentication via %q cookie and %q header detected, but the values do not match. "+
33+
"To resolve this issue ensure the values used in both match, or only use one of the authentication methods. "+
34+
"You can also try clearing your cookies if this error persists.",
35+
codersdk.SessionTokenCookie, codersdk.SessionTokenHeader),
36+
http.StatusBadRequest)
37+
return
38+
}
39+
2340
http.Error(w, "Something is wrong with your CSRF token. Please refresh the page. If this error persists, try clearing your cookies.", http.StatusBadRequest)
2441
}))
2542

0 commit comments

Comments
 (0)