Start learning free

LESSON 90 OF 173 · BACKEND: USERS AND SECURITY

A loginRequired Guard

Practice

Every page 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 route works until the day you forget them on one route, and that route is open to everybody. A guard method writes the check once, and one line puts it in front of any route.

The guard receives the route's own work as a lambda: a small function written in place, like user -> user. Its type, Function<Map<String, Object>, Object>, says it takes one value and gives back an answer, and view.apply(...) runs it. Here is a small guard that has nothing to do with logins: it prints the path of each request before running the route's work.

Object logCalls(HttpServletRequest request, Function<String, Object> view) {
    String path = request.getRequestURI();
    System.out.println("calling " + path);
    return view.apply(path);
}

@GetMapping("/hello")
Object hello(HttpServletRequest request) {
    return logCalls(request, path -> "Hello from " + path + "!");
}

Passing a lambda doesn't run it: only apply does, so the guard decides whether the route's work runs at all. A lambda can also use the variables around it, like a route's @PathVariable values, so one guard 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(request, view): it answers 401 "Login required" when currentUser finds nobody, and otherwise runs the view with the user. Make /me return loginRequired with a view that gives back the user, and add a protected GET /welcome that answers the text Welcome back, <email>!.


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


Hint
The check comes before view.apply(user): inside the if, the 401 returns early, so the view never runs without a user. Each route then needs one line, return loginRequired(request, …);, with its own lambda.


STEP 1 OF 9
    Ctrl + Enter to check