How to fix 'http: named cookie not present' in golang?

10,625

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.

Share:
10,625
Abe Brandsma
Author by

Abe Brandsma

Updated on June 06, 2022

Comments

  • Abe Brandsma
    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
    Abe Brandsma almost 5 years
    Thank 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.