Authentication Middleware
Whenever you’re browsing your favorite social media website, it would make sense that you could only see your personal content if you’re logged in, right? Why would you even want to waste time performing an operation in a route handler if the request is unauthenticated? You’re going to implement a route handler that uses Codable Routing in Kitura with type-safe middleware to ensure that the request is authenticated.
Note: Both Server-Side Swift books,
Kitura and
Vapor, go into the details of HTTP authentication. HTTP basic is often used as a means of demonstration authentication on a server, and it is generally not recommended for production.
First, open RazeRoutes.swift and register your route in your initalizeRazeRoutes()
function:
app.router.get("/auth", handler: authHandler)
Next, scroll to the bottom of this file and add the following handler:
func authHandler(profile: RazeAuth, completion: (RazeAuth?, RequestError?) -> Void) {
completion(profile, nil)
}
Your server should not compile properly at this point, because you have not yet defined RazeAuth
. Open Middleware.swift and import the following module at the top of your file underneath your import of KituraCORS
:
import CredentialsHTTP
Next, scroll to the bottom of this file and add the following code to define your middleware instance:
// 1
public struct RazeAuth: TypeSafeHTTPBasic {
// 2
public var id: String
// 3
static let database = ["David": "12345", "Tim": "54321"]
// 4
public static func verifyPassword(username: String,
password: String,
callback: @escaping (RazeAuth?) -> Void) {
}
}
Take a moment to examine what you’ve added:
- The main requirement of your middleware is that it must conform to the
TypeSafeHTTPBasic
protocol.
- The first required implementation in the
TypeSafeBasicHTTP
protocol is the id
property, to be able to identify an authenticated user.
- In this example, you are setting up a very small and simple database of usernames and passwords — this is here to demonstrate that you could use any existing database module to query by username!
- The other required implementation for the
TypeSafeBasicHTTP
protocol is the verifyPassword
method. After you have confirmed that the username and password match expected values, you can create a RazeAuth
object with the proper username, and pass it on in the callback. Since you registered the route with a non-optional RazeAuth
object, this means that calling callback()
with nil
will instead send a 401 Unauthorized response to the client.
Next, add this code inside verifyPassword()
to verify if the given username and password are valid according to your super secure database of usernames and passwords:
guard let storedPassword = database[username],
password == storedPassword else {
return callback(nil)
}
return callback(RazeAuth(id: username))
Lastly, go to RazeRoutes.swift and put a breakpoint inside your /auth
route handler. Build and run your server, and ensure your server is listening on port 8080. Open Terminal, and run the three following commands:
curl -u "Ray":"12345" localhost:8080/auth
curl -u "David":"12345" localhost:8080/auth
curl -u "Tim":"54321" localhost:8080/auth
For the commands that are properly authenticated (David and Tim’s), you should trigger your breakpoint, and your server should respond with the username that you sent over! Now, your server only has to do the work its authenticated to do!
Where to Go From Here?
Middleware opens up a large realm of possibilities for developers to enhance routes that might already exist on a server. This tutorial showed you how easy it is to both implement existing middleware libraries, and how you can roll your own library to add custom behavior to your server, like the koba library written by Caleb Kinney.
Both our Server Side Swift with Kitura and Server Side Swift with Vapor books have plenty of information about implementing middleware and authentication, and you can work on them in a real-life scenario!
If you want to learn more about how Kitura handles HTTP routing and works in general, read this beginner’s tutorial about it.
Please leave a comment below if you know about any other middleware libraries or if you have any questions!