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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With