Start learning free

LESSON 96 OF 173 · BACKEND: USERS AND SECURITY

CORS

Practice

Your browser runs the JavaScript of every page you open. So browsers follow a rule: a page may read answers only from its own site, unless the other site allows it. Without that rule, a page on evil.example.com could call the shop's API from your browser and read your data. The way a site says which other sites may read its answers is CORS (Cross-Origin Resource Sharing).

The browser names the page's site in the request header Origin. When that site is allowed, the API answers with the header Access-Control-Allow-Origin, and the browser lets the page read the answer. Every answer needs the rule, errors too, so it goes in a middleware: a function that wraps the whole mux, gets every request first, sets headers, then passes the request on with next.ServeHTTP. This one adds another header to every answer, and newApp returns addVersion(app) instead of app:

func addVersion(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("X-Shop-Version", "2")
        next.ServeHTTP(w, r)
    })
}

Before a request that sends a token or a JSON body, or uses PUT, the browser first asks with an OPTIONS request, a preflight, and reads which headers and methods the shop's site may send. The shop has no OPTIONS routes, so Go's mux would answer 405 Method Not Allowed, and the browser would take that as a no. So the middleware answers OPTIONS itself: it sets the headers and returns without calling next, which Go sends as an empty 200. CORS is a rule the browser follows, not a lock: the shop still answers other sites, only without permission.

GET /products/1       Origin: https://shop.example.com  →  200, Access-Control-Allow-Origin: https://shop.example.com
GET /products/1       Origin: https://evil.example.com  →  200, no Access-Control-Allow-Origin
GET /products/99      Origin: https://shop.example.com  →  404, Access-Control-Allow-Origin: https://shop.example.com
OPTIONS /me           Origin: https://shop.example.com  →  200, Access-Control-Allow-Headers: Authorization, Content-Type
OPTIONS /me/password  Origin: https://shop.example.com  →  200, Access-Control-Allow-Methods: GET, POST, PUT
OPTIONS /me           Origin: https://evil.example.com  →  200, no CORS headers

Your task: keep the shop's website in a constant shopSite, and write a middleware addCORSHeaders that newApp wraps around the mux. It adds Access-Control-Allow-Origin, Access-Control-Allow-Headers and Access-Control-Allow-Methods only when the request's Origin is shopSite, and answers OPTIONS requests itself with an empty 200.


Assignment
Follow the Coding Tutorial to add the shopSite constant and the addCORSHeaders middleware, and wrap the shop in it.


Hint
Read the page's site with r.Header.Get("Origin"), and set answer headers with w.Header().Set(name, value) before next.ServeHTTP runs the route. For an OPTIONS request, return before next.ServeHTTP.


STEP 1 OF 11
    Ctrl + Enter to check