auth
auth/oauth2_test.go
Enable CSRF protection, add expiration to sessions. Sessions gain a CSRF token, which is passed as a parameter to the login page. The login page now checks for that CSRF token, and logs a CSRF attempt if the token does not match. I also added an expiration to sessions, so they don't last forever. Sessions should be pretty short--we just need to stay logged in for long enough to approve the OAuth request. Everything after that should be cookie based. Finally, I added a configuration parameter to control whether the session cookie should be set to Secure, requiring the use of HTTPS. For production use, this flag is a requirement, but it makes testing extremely difficult, so we need a way to disable it.
1.1 --- a/oauth2_test.go Sat Jan 24 10:34:33 2015 -0500 1.2 +++ b/oauth2_test.go Wed Jan 28 07:27:32 2015 -0500 1.3 @@ -71,6 +71,8 @@ 1.4 ID: "testsession", 1.5 Active: true, 1.6 ProfileID: profile.ID, 1.7 + CSRFToken: "nosurf", 1.8 + Expires: time.Now().Add(time.Hour), 1.9 } 1.10 err = testContext.CreateSession(session) 1.11 if err != nil { 1.12 @@ -117,6 +119,7 @@ 1.13 w = httptest.NewRecorder() 1.14 data := url.Values{} 1.15 data.Set("grant", "approved") 1.16 + data.Set("csrftoken", session.CSRFToken) 1.17 body := bytes.NewBufferString(data.Encode()) 1.18 req.Body = ioutil.NopCloser(body) 1.19 GetAuthorizationCodeHandler(w, req, testContext) 1.20 @@ -129,6 +132,7 @@ 1.21 t.Fatalf(`Being redirected to a non-URL "%s" threw error "%s" for "%s"\n`, redirectedTo, err, req.URL.String()) 1.22 } 1.23 t.Log("Redirected to", redirectedTo) 1.24 + t.Log("Body", w.Body.String()) 1.25 if red.Query().Get("code") == "" { 1.26 t.Fatalf(`Expected code param in redirect URL to be set, but it wasn't for %s`, req.URL.String()) 1.27 } 1.28 @@ -173,8 +177,10 @@ 1.29 t.Fatal("Can't store client:", err) 1.30 } 1.31 session := Session{ 1.32 - ID: "testsession", 1.33 - Active: true, 1.34 + ID: "testsession", 1.35 + Active: true, 1.36 + CSRFToken: "nosurf", 1.37 + Expires: time.Now().Add(time.Hour), 1.38 } 1.39 err = testContext.CreateSession(session) 1.40 if err != nil { 1.41 @@ -246,8 +252,10 @@ 1.42 t.Fatal("Can't store client:", err) 1.43 } 1.44 session := Session{ 1.45 - ID: "testsession", 1.46 - Active: true, 1.47 + ID: "testsession", 1.48 + Active: true, 1.49 + CSRFToken: "nosurf", 1.50 + Expires: time.Now().Add(time.Hour), 1.51 } 1.52 err = testContext.CreateSession(session) 1.53 if err != nil { 1.54 @@ -362,8 +370,10 @@ 1.55 t.Fatal("Can't store endpoint:", err) 1.56 } 1.57 session := Session{ 1.58 - ID: "testsession", 1.59 - Active: true, 1.60 + ID: "testsession", 1.61 + Active: true, 1.62 + CSRFToken: "nosurf", 1.63 + Expires: time.Now().Add(time.Hour), 1.64 } 1.65 err = testContext.CreateSession(session) 1.66 if err != nil { 1.67 @@ -465,8 +475,10 @@ 1.68 t.Fatal("Can't store endpoint:", err) 1.69 } 1.70 session := Session{ 1.71 - ID: "testsession", 1.72 - Active: true, 1.73 + ID: "testsession", 1.74 + Active: true, 1.75 + CSRFToken: "nosurf", 1.76 + Expires: time.Now().Add(time.Hour), 1.77 } 1.78 err = testContext.CreateSession(session) 1.79 if err != nil { 1.80 @@ -489,8 +501,10 @@ 1.81 params.Set("state", "my super secure state string") 1.82 data := url.Values{} 1.83 data.Set("grant", "denied") 1.84 + data.Set("csrftoken", session.CSRFToken) 1.85 req.URL.RawQuery = params.Encode() 1.86 req.Body = ioutil.NopCloser(bytes.NewBufferString(data.Encode())) 1.87 + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") 1.88 req.Method = "POST" 1.89 w := httptest.NewRecorder() 1.90 GetAuthorizationCodeHandler(w, req, testContext) 1.91 @@ -566,8 +580,10 @@ 1.92 t.Errorf("Expected ErrNoSession, got %s", err) 1.93 } 1.94 session = Session{ 1.95 - ID: "testsession", 1.96 - Active: true, 1.97 + ID: "testsession", 1.98 + Active: true, 1.99 + CSRFToken: "nosurf", 1.100 + Expires: time.Now().Add(time.Hour), 1.101 } 1.102 err = testContext.CreateSession(session) 1.103 if err != nil {