Home

Exploring authentication of routes

31/01/2023

So far all the functioning routes in the Cantilever have been GET requests which trigger a process, sure, but do not add any new information to the application. This isn't much use - very soon I'll want to write the POST /new route, to create a new markdown source file.

But I can't have the application open to just anyone creating new files. They must be authenticated first.

I'm not very good at "auth"; I've only implemented it once before using Amazon Cognito, and I'm sure that's what I'll do again. But I've never written a router which requires authentication before, so I'm starting out by sketching out how I'd like it to work.

My preference is to have a mechanism like this:

override val router: Router = Router.router {
    // no authentication
    get("/noAuthRequires") { _: Request<Unit> -> ResponseEntity.ok("Welcome")}
    
    // authentication required
    authorize("PERMISSION_NAME") {
        post("/new") { request: Request<NewPost> -> ResponseEntity.ok("You have been approved to create post ${request.body}")}
    }
}

But I don't think I can make this work. Nesting these RequestPredicates (as the get and post methods are internally) is proving very confusing. I have seen it done this way in the Osiris library (for example, in this class from an older project of mine). The library which has inspired Cantilevers handles authentication like this:

    post("/new"){ request: Request<NewPost> -> 
        ResponseEntity.ok("You have been approved to create post ${request.body}")}
        .requiresPermission("PERMISSION_NAME")

I might have to go down this path. But solving the nested RequestPredicate issue will be essential for another feature I'd really like to add, route grouping:

    route("/news") {
        get("/", newsController::get) // matches path /news/
        post("/add", newsController::add) // matches path /news/add
    }

So the goal remains. Other libraries do support route nesting like this, and it would be a shame to lose that option.

Prev: Serialization Progress Next: Starting the web front-end