How to fix 'http: named cookie not present' in golang?
Remember to set the Path of the cookie:
c := &http.Cookie{Name: name, Value: encode(value), Path: "/"}
http.SetCookie(w, c)
if you don't, the Path will be the path of the current request, and the cookie will therefore ONLY be available from this exact path.
Abe Brandsma
Updated on June 06, 2022Comments
-
Abe Brandsma almost 2 years
I'm building a small dinner/plan management application (with the use of microservices) for a couple of people I know. The intention is that each person can login to their own account and can then authenticate to other services using a bearer token (JWT).
This bearer token is stored in a cookie. However, I cannot find this cookie after it has been set and I try to retrieve it again.
Which eventually results in the error
http: named cookie not present
Why is the response body of the request empty? Why aren't there any cookies sent with my GET request? How can I go about fixing this?
I've searched around the net for a bit and tried the following things
Net/http cookie: The implementation that seems the most simple, and also the one that I'm showing here. It seems like this trivial example should work.
Cookiejar implementation: I tried to use the cookiejar implementation to set and retrieve the cookies from both the browser and postman, however it resulted in the same outcome. The cookiejar implementation I used is described in https://golang.org/pkg/net/http/cookiejar/?m=all#New
Setting to specific URL and extra GET request: I tried to place the cookies on a different specific URL within my domain. At some point it seemed like the cookies could only be retrieved from a certain specific absolute URL, this was not the case.
httputil DumpRequestOut: I found that the utility package of net/http had a function called DumpRequestOut, this function might have been able to extract the body from the request, but this was also empty.
Setting cookie 'secure' flag to false: I found a suggestion that the secure flag makes the cookies impossible to read. Unfortunately changing the secure flag had no effect.
Postman clearly shows that the cookies do exist. My browser (firefox) also shows that the cookies exist, but they have been given quite an abstract name. The Postman requests can be found at https://www.getpostman.com/collections/fccea5d5dc22e7107664
If I try to retrieve the cookies using the "net/http" package from golang, the response body comes up empty.
I set the session tokens and redirect the client directly after I've authenticated the user/password combination.
// SetTokenAndRedirect sets an access token to the cookies func SetTokenAndRedirect(w http.ResponseWriter, r *http.Request, db *mgo.Session, u *user.User, redirectURL string) *handler.AppError { // Generate a unique ID for the session token. tokenID := uuid.Must(uuid.NewV4()).String() //set the expiration time (found in config.config.go) expirationTime := time.Now().Add(config.ExpireTime) // Set the cookie with the JWT http.SetCookie(w, &http.Cookie{ Name: config.AccessTokenName, Value: createToken(u.UserID, expirationTime, tokenID, r.Header.Get("User-Agent")), Expires: expirationTime, HttpOnly: true, Secure: false, }) // Redirects user to provided redirect URL if redirectURL == "" { return handler.AppErrorf(417, nil, "No redirect URL has been provided") } http.Redirect(w, r, redirectURL, 200) return nil }
I try to verify the incoming request and JWT token as follows.
// All handlers will have this adapted serveHTTP function func (fn AppHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if err := Authorize(w, r); err != nil { http.Error(w, fmt.Sprintf("Not Authorized: %v", err), http.StatusUnauthorized) return } if e := fn(w, r); e != nil { // e is *appError, not os.Error. log.Printf("Handler error: status code: %d, message: %s, underlying err: %#v", e.Code, e.Message, e.Error) http.Error(w, e.Message, e.Code) } }
// Claims defines what will be stored in a JWT access token type Claims struct { ProgramVersion string `json:"programVersion"` UserAgent string `json:"userAgent"` jwt.StandardClaims } // Authorize checks if the jwt token is valid func Authorize(w http.ResponseWriter, r *http.Request) error { c, err := r.Cookie("access_token") if err != nil { if err == http.ErrNoCookie { // The program returns this error return err } return err } tokenString := c.Value claim := &Claims{} tkn, err := jwt.ParseWithClaims(tokenString, claim, func(tkn *jwt.Token) (interface{}, error) { return config.JwtSigningSecret, nil }) if !tkn.Valid { return err } if err != nil { if err == jwt.ErrSignatureInvalid { return err } return err } // JWT token is valid return nil }
The request is structured as follows when setting the cookie
// Pretty printed version Host: localhost:8080 content-type: application/json user-agent: PostmanRuntime/7.11.0 cache-control: no-cache accept-encoding: gzip, deflate content-length: 68 connection: keep-alive accept: */* postman-token: 36268859-a342-4630-9fb4-c286f76d868b cookie: access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwcm9ncmFtVmVyc2lvbiI6IjEuMC4wIiwidXNlckFnZW50IjoiUG9zdG1hblJ1bnRpbWUvNy4xMS4wIiwiZXhwIjoxNTU2MjA0MTg3LCJqdGkiOiJlZDlmMThhZi01NTAwLTQ0YTEtYmRkZi02M2E4YWVhM2M0ZDEiLCJpYXQiOjE1NTYyMDM1ODcsImlzcyI6ImdrLmp3dC5wcm9maWxlU2VydmljZS5hIn0.bssnjTZ8woKwIncdz_EOwYbCtt9t6V-7PmLxfq7GVyo
// Raw Version &{POST /auth/users/login?redirect=/ HTTP/1.1 1 1 map[Cache-Control:[no-cache] Postman-Token:[d33a093e-c7ab-4eba-8c1e-914e85a0d289] Cookie:[access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwcm9ncmFtVmVyc2lvbiI6IjEuMC4wIiwidXNlckFnZW50IjoiUG9zdG1hblJ1bnRpbWUvNy4xMS4wIiwiZXhwIjoxNTU2MjA0NDU4LCJqdGkiOiIzOTk1MmI1NS0yOWQzLTQ4NGQtODhhNC1iMDlhYmI1OWEyNzgiLCJpYXQiOjE1NTYyMDM4NTgsImlzcyI6ImdrLmp3dC5wcm9maWxlU2VydmljZS5hIn0.DFA7KBET3C2q1A9N1hXGMT0QbabHgaVcDBpAYpBdbi8] Accept-Encoding:[gzip, deflate] Connection:[keep-alive] Content-Type:[application/json] User-Agent:[PostmanRuntime/7.11.0] Accept:[*/*] Content-Length:[68]] 0xc0001ba140 <nil> 68 [] false localhost:8080 map[redirect:[/]] map[] <nil> map[] [::1]:36584 /auth/users/login?redirect=/ <nil> <nil> <nil> 0xc00016a2a0}
The request is structured as follows when getting the cookie
// Pretty printed version Host: localhost:8080 cache-control: no-cache postman-token: 20f7584f-b59d-46d8-b50f-7040d9d40062 accept-encoding: gzip, deflate connection: keep-alive user-agent: PostmanRuntime/7.11.0 accept: */*
// Raw version 2019/04/25 12:22:56 &{GET /path/provide HTTP/1.1 1 1 map[User-Agent:[PostmanRuntime/7.11.0] Accept:[*/*] Cache-Control:[no-cache] Postman-Token:[b79a73a3-3e08-48a4-b350-6bde4ac38d23] Accept-Encoding:[gzip, deflate] Connection:[keep-alive]] {} <nil> 0 [] false localhost:8080 map[] map[] <nil> map[] [::1]:35884 /path/provide <nil> <nil> <nil> 0xc000138240}
The response is structured as follows when setting the cooke
response Headers: map[Location:[/] Set-Cookie:[access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwcm9ncmFtVmVyc2lvbiI6IjEuMC4wIiwidXNlckFnZW50IjoiR28taHR0cC1jbGllbnQvMS4xIiwiZXhwIjoxNTU2MjI4ODIyLCJqdGkiOiJlY2Q2NWRkZi1jZjViLTQ4N2YtYTNkYy00NmM3N2IyMmUzMWUiLCJpYXQiOjE1NTYyMjgyMjIsImlzcyI6ImdrLmp3dC5wcm9maWxlU2VydmljZS5hIn0.0sOvEzQS2gczjWSmtVSD_u0qMV2L7M4hKF1KUM08-bQ; Expires=Thu, 25 Apr 2019 21:47:02 GMT; HttpOnly] Date:[Thu, 25 Apr 2019 21:37:02 GMT] Content-Length:[0]]
I expect that the Authorize function will return nil. Also,if I add the following piece of code I expect that there are some cookies present.
for _, cookie := range r.Cookies() { fmt.Fprint(w, cookie.Name) }
However, the Authorize function returns the error in the title and the printf does not print out any cookies.
-
Abe Brandsma almost 5 yearsThank you for taking the time to reply to my question. I'm sorry that I caused some confusion there. I am in fact searching for "access_token" and setting "access_token", I had temporarily changed it for my own sanity check. The discussion you send does help though, I might be writing something to the response before setting the cookies.