Start learning free

LESSON 90 OF 173 · BACKEND: USERS AND SECURITY

A loginRequired Wrapper

Practice

Every handler for logged-in users starts the same way: find the user from the token, and answer 401 when there is none. Copying those lines into every handler works until the day you forget them on one route, and that route is open to everybody. A wrapper writes the check once, and wrapping a handler puts it in front of any route.

A wrapper is a function that receives a handler and gives back another handler to use in its place. Here is a small one that has nothing to do with logins: it prints a route's name before running it.

func logCalls(name string, next http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        fmt.Println("calling", name)
        next(w, r)
    }
}

app.HandleFunc("GET /hello", logCalls("hello", hello))

A login wrapper has one more job: hand the user it found to the handler. The given type userHandler is a handler that takes the user as a third parameter: func(w http.ResponseWriter, r *http.Request, user User). loginRequired turns a userHandler into a plain handler the mux accepts. Values from the path stay in r, so one wrapper fits every route.

Ana (id 1) signs up and logs in first; <token> is the token that login answered:

POST /register  {"email": "ana@example.com", "password": "secret123"}  →  201  {"email": "ana@example.com", "id": 1}
POST /login     {"email": "ana@example.com", "password": "secret123"}  →  200  {"token": "<token>"}
GET /me         Authorization: Bearer <token>  →  200  {"email": "ana@example.com", "id": 1}
GET /me         (no header)                    →  401  {"error": "Login required"}
GET /welcome    Authorization: Bearer <token>  →  200  Welcome back, ana@example.com!
GET /welcome    (no header)                    →  401  {"error": "Login required"}
GET /welcome    Authorization: Bearer 0123456789abcdef0123456789abcdef  →  401  {"error": "Login required"}

Your task: below currentUser, write loginRequired(view userHandler) http.HandlerFunc: the handler it returns answers 401 "Login required" when currentUser(r) finds nobody, and otherwise calls view with the user. Write me, which now receives user and answers it, and route GET /me and GET /welcome through loginRequired. The given welcome answers the text Welcome back, <email>!.


Assignment
Follow the Coding Tutorial to write the loginRequired wrapper and protect /me and /welcome with it.


Hint
There are two returns: the bare return after the 401 stops the handler, and loginRequired returns the new func(w, r) itself. loginRequired(me) is a whole handler: it goes where a func literal went in app.HandleFunc.


STEP 1 OF 11
    Ctrl + Enter to check