Yesterday, I was working on the end-to-end tests for my example web application, (specifically this PR). But I ran into an issue.
- In the web browser I was able to successfully login and access a protected page.
- Using Go code and a
http.Client
I got a successful response but the session cookie was not set.
The session cookie
After a successful login, my app sets a session cookie. This cookie has two important attributes:
HttpOnly
: Meaning the cookie is inaccessible via Javascript.Secure
: Meaning the cookie should only be sent over an encrypted request via HTTPS.
You can find more information about these attributes on MDN.
Dealing with localhost
Most (all?) webbrowsers ignore the HTTPS requirement when the cookie is sent to a localhost
hostname. This is to make local development easier I assume.
However, the Go net/http/cookiejar
package doesn’t make this exception, it requires HTTPS even for localhost
hostnames. Secure
cookies are (silently) not included in such requests.
There is an open proposal to change this behavior,
Demo
package main
import (
"fmt"
"log"
"net/http"
"net/http/cookiejar"
"time"
"golang.org/x/net/publicsuffix"
)
func main() {
s := &http.Server{
Addr: ":9999",
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Printf("cookies in request: %v\n", r.Cookies())
// return a secure cookie
http.SetCookie(w, &http.Cookie{
Name: "secureCookie",
Value: "hello world!",
Secure: true,
})
}),
}
go func() {
err := s.ListenAndServe()
if err != nil {
log.Fatalf("failed to listen and serve: %v", err)
}
}()
// Create a HTTP client with a cookiejar.
jar, err := cookiejar.New(&cookiejar.Options{
PublicSuffixList: publicsuffix.List,
})
if err != nil {
log.Fatalf("failed to create cookiejar: %v", err)
}
client := &http.Client{
Timeout: time.Second,
Jar: jar,
}
// Do two requests.
for i := 0; i < 2; i++ {
res, err := client.Get("http://localhost:9999")
if err != nil {
log.Fatalf("failed to do request: %v", err)
}
defer res.Body.Close()
fmt.Printf("cookies in response: %v\n", res.Cookies())
}
}
If you run the example you will see that the cookies are always returned in the response, but never included in the requests.
If you change the Secure
attribute to false
(or remove it), you will see that the cookies are included in the second request.
My solution
In my app I added an option to disable Secure
cookies for my end-to-end tests.
Get my free newsletter every second week
Used by 500+ developers to boost their Go skills.
"I'll share tips, interesting links and new content. You'll also get a brief guide to time for developers for free."