Implementing simple authentication middleware
This week, I wrote a simple piece of middleware to verify a session token, but I haven’t been able to test it yet.
Writing middleware
Building the middleware was simpler than expected. Referencing axum-login’s middleware, I wrote the parts to fulfill three basic steps.
- Store a handle to a database.
- Upon receiving a request, verify a session token, if it exists, an attach it to the request.
- Defer processing to an inner service.
To store the database, I wrote a
Layer containing a
database client and a prepared statement. The client is wrapped in an
Arc<Mutex<...>>, so both the client and statement are easy to clone.
When the first request arrives, Axum calls layer with the inner service, constructing a Service with a given inner layer.
The service mainly needs to define the
call
method, which returns any future. For authentication, this is an async function
of type Pin<Box<dyn Future<Output = ...>>>, which queries the database and
defers to the inner layer.
To attach the user id, I defined a struct UserId(i32), implementing
FromRequestParts. This custom type can be added to a request through
extensions_mut,
where it can then be extracted.
The additional
poll_ready
function can be used to signal readiness. Since the middleware is ready when it
is constructed, this can return inner.poll_ready().
During implementation, one complication was the return type of the Future. Although the Future returns a Result, axum receives middleware which never returns an Err.
Testing
I wanted to test this middleware immediately, but there were a number of barriers to doing so.
- This middleware is part of a separate binary and is meant to be multiplexed with nginx. This makes it difficult to test with the frontend.
- The middleware uses Postgres directly, and I haven’t established a testing database.
An internal test won’t care about the first point, since it can construct the server directly, but the second point seems impossible to do portably with lightweight technology.
Next week
Without easy testing, it will be difficult to develop any new features. Next week, I will develop an automated test for the basic authentication middleware. If this turns out well, I aim to also test the main authentication flow as well.