Skip to content

Commit 10c2817

Browse files
authored
chore: swagger docs omit brower based credentials, rely on swagger auth (coder#13742)
* 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 cd069fa commit 10c2817

File tree

2 files changed

+40
-1
lines changed

2 files changed

+40
-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: 15 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,20 @@ 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+
http.Error(w,
30+
fmt.Sprintf("CSRF error encountered. Authentication via %q cookie and %q header detected, but the values do not match. "+
31+
"To resolve this issue ensure the values used in both match, or only use one of the authentication methods. "+
32+
"You can also try clearing your cookies if this error persists.",
33+
codersdk.SessionTokenCookie, codersdk.SessionTokenHeader),
34+
http.StatusBadRequest)
35+
return
36+
}
37+
2338
http.Error(w, "Something is wrong with your CSRF token. Please refresh the page. If this error persists, try clearing your cookies.", http.StatusBadRequest)
2439
}))
2540

0 commit comments

Comments
 (0)