diff --git a/CHANGELOG.md b/CHANGELOG.md index d94b0ce4..09321bfb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased ### Fixed +- HTTP cookies not stored in `irmaclient` when received from a `Set-Cookie` header - Invalid hostname specified in MX record bypasses e-mail address revalidation - Background revocation tasks not stopped when closing an `irmaclient` diff --git a/irmago_test.go b/irmago_test.go index 535edb84..7af3e1cf 100644 --- a/irmago_test.go +++ b/irmago_test.go @@ -167,6 +167,27 @@ func TestRetryHTTPRequest(t *testing.T) { require.Equal(t, "42\n", string(bts)) } +func TestHTTPTransportCookieJar(t *testing.T) { + mux := http.NewServeMux() + mux.HandleFunc("/setcookie", func(w http.ResponseWriter, r *http.Request) { + http.SetCookie(w, &http.Cookie{Name: "testcookie", Value: "42", Domain: "localhost"}) + w.WriteHeader(http.StatusNoContent) + }) + mux.HandleFunc("/checkcookie", func(w http.ResponseWriter, r *http.Request) { + c, err := r.Cookie("testcookie") + require.NoError(t, err) + require.Equal(t, "42", c.Value) + w.WriteHeader(http.StatusNoContent) + }) + server := &http.Server{Addr: "localhost:48682", Handler: mux} + go server.ListenAndServe() + defer server.Close() + + transport := NewHTTPTransport("http://localhost:48682", false) + require.NoError(t, transport.Get("/setcookie", nil)) + require.NoError(t, transport.Get("/checkcookie", nil)) +} + func TestInvalidIrmaConfigurationRestoreFromRemote(t *testing.T) { test.StartSchemeManagerHttpServer() defer test.StopSchemeManagerHttpServer() diff --git a/transport.go b/transport.go index 9fc5bf34..6fdb2ba0 100644 --- a/transport.go +++ b/transport.go @@ -10,6 +10,7 @@ import ( "log" "net" "net/http" + "net/http/cookiejar" "net/url" "strings" "time" @@ -104,6 +105,13 @@ func NewHTTPTransport(serverURL string, forceHTTPS bool) *HTTPTransport { }, } + // Create cookie jar to store cookies in + cookieJar, err := cookiejar.New(&cookiejar.Options{PublicSuffixList: httpPublicSuffixList{}}) + if err != nil { + Logger.Warnf("failed to create cookie jar: %s", err.Error()) + cookieJar = nil + } + client := &retryablehttp.Client{ Logger: transportlogger, RetryWaitMin: 100 * time.Millisecond, @@ -120,6 +128,7 @@ func NewHTTPTransport(serverURL string, forceHTTPS bool) *HTTPTransport { HTTPClient: &http.Client{ Timeout: time.Second * 5, Transport: innerTransport, + Jar: cookieJar, }, } @@ -343,3 +352,15 @@ func (transport *HTTPTransport) Get(url string, result interface{}) error { func (transport *HTTPTransport) Delete() error { return transport.jsonRequest("", http.MethodDelete, nil, nil) } + +// httpPublicSuffixList implements the PublicSuffixList interface for use in cookiejar. +// It is used to prevent cookies from being sent to other domains and subdomains as the host. +type httpPublicSuffixList struct{} + +func (p httpPublicSuffixList) PublicSuffix(domain string) string { + return domain +} + +func (p httpPublicSuffixList) String() string { + return "github.com/privacybydesign/irmago/httpPublicSuffixList-v1" +}