A cookie is a small piece of data a site gives the browser with an answer. The browser keeps it and sends it back, on its own, with every later request to that site. That's how a website stays logged in without adding an Authorization header to each request.
In Go, http.SetCookie(w, &http.Cookie{…}) adds a cookie to the answer, and r.Cookie("name") reads one from the request. r.Cookie also gives an error, which is not nil when the request has no such cookie. For example, two routes could remember a shopper's favorite color:
app.HandleFunc("POST /color", func(w http.ResponseWriter, r *http.Request) {
var data map[string]string
json.NewDecoder(r.Body).Decode(&data)
http.SetCookie(w, &http.Cookie{Name: "color", Value: data["color"], Path: "/"})
writeJSON(w, 200, map[string]string{"color": data["color"]})
})
app.HandleFunc("GET /color", func(w http.ResponseWriter, r *http.Request) {
color := "blue"
cookie, err := r.Cookie("color")
if err == nil {
color = cookie.Value
}
writeJSON(w, 200, map[string]string{"color": color})
})
In this lesson, /login also sends the token in a cookie named "session", with HttpOnly: true so the page's scripts can't read it. currentUser uses that cookie when a request brings no Authorization header. When the header is there, it still wins. A made-up cookie value gets nowhere: no row in the tokens table has it. The tests use one client that keeps its cookies, like a browser:
GET /me (no header, no cookie yet) → 401 {"error": "Login required"}
POST /login {"email": "ana@example.com", "password": "secret123"} → 200 {"token": "…"}
GET /me (no header) → 200 {"email": "ana@example.com", "id": 2, "role": "customer"}
GET /welcome (no header) → 200 Welcome back, ana@example.com!
POST /products {"name": "Desk", "price": 120} (no header) → 403 {"error": "Admins only"}
POST /login {"email": "admin@example.com", "password": "admin-pass-123"} → 200 {"token": "…"}
POST /products {"name": "Desk", "price": 120} (no header) → 201 {"id": 9, "name": "Desk", "price": 120}
GET /me (Bearer Ana's token) → 200 {"email": "ana@example.com", "id": 2, "role": "customer"}
GET /me (no header) → 200 {"email": "admin@example.com", "id": 1, "role": "admin"}
Your task: at login, send the cookie "session" holding the token with http.SetCookie (with Path: "/" and HttpOnly: true), and let currentUser take the token from that cookie when the header gives none.
Assignment
Follow the Coding Tutorial to keep users logged in with a session cookie.
Hint
Without the header, strings.ReplaceAll(header, "Bearer ", "") gives the empty text "": that's when to use the cookie. Check err == nil too: with no cookie, cookie is nil, and reading cookie.Value would crash. Go refuses variables that are declared and never used, so the app only compiles again once cookie.Value is used.
Free plan: 30 tutor messages. PRO is unlimited.
