HTTP Basic Authentication with Codable routes

Authentication is the process of confirming a user’s identity, usually via a username and password. Authentication ensures that the user is allowed access to the secured system.

In this guide, we use Kitura-CredentialsHTTP to add HTTP basic authentication to Codable routes.


Step 1: Define the authentication middleware

To add basic authentication to our server, we need to add Kitura-CredentialsHTTP to our dependencies.

If you don't have a server, follow our Create a server guide.

Next, we will define a TypeSafeMiddleware which conforms to TypeSafeHTTPBasic.

This will be initialized when our route is successfully authenticated and we will be able to access the authenticated user's id within our Codable route.

If you don't already have one, create a Middlewares folder:

mkdir Sources/Application/Middlewares

Create a new file, called MyBasicAuth.swift:

touch Sources/Application/Middlewares/MyBasicAuth.swift

Open your MyBasicAuth.swift file:

open Sources/Application/Middlewares/MyBasicAuth.swift

Inside this file, define your MyBasicAuth struct:

import Credentials
import CredentialsHTTP
import LoggerAPI

public struct MyBasicAuth: TypeSafeHTTPBasic {

}

If you're using Xcode it should display the message: Type 'MyBasicAuth' does not conform to protocol 'TypeSafeCredentials'

Click "Fix" to autogenerate the stubs for verifyPassword and id:

public static func verifyPassword(username: String, password: String, callback: @escaping (MyBasicAuth?) -> Void) {

}

public var id: String

Inside MyBasicAuth, add an authentication dictionary:

public static let authenticate = ["username" : "password"]

In a real project, never store passwords in plain text!

The function, verifyPassword, takes a username and password and, on success, returns a MyBasicAuth instance.

We want to check if the password matches the user's stored password. On successful match, we initialize MyBasicAuth with an id equal to username.

if let storedPassword = authenticate[username], storedPassword == password {
    return callback(MyBasicAuth(id: username))
}
callback(nil)

This function is async, so that we can perform async actions to verify the password, e.g. looking up the username and password in a database.

We must call the callback closure with either an instance of Self or nil before exiting verifyPassword.

If we do not, the server will not know to continue and we will recieve a 503 "Service Unavailable" error, when we call the route.

Our completed struct should now look as follows:

public struct MyBasicAuth: TypeSafeHTTPBasic {

    public static let authenticate = ["username" : "password"]

    public static func verifyPassword(username: String, password: String, callback: @escaping (MyBasicAuth?) -> Void) {
        if let storedPassword = authenticate[username], storedPassword == password {
            return callback(MyBasicAuth(id: username))
        }
        callback(nil)
    }

    public var id: String
}

Step 2: Create your authentication routes

Firstly, open your Application.swift file in your default text editor:

open Sources/Application/Application.swift

Inside the postInit() function add:

initializeTypeSafeAuthRoutes(app: self)

Next, create a new file, called TypeSafeAuthRoutes.swift, to contain the code for our routes:

touch Sources/Application/Routes/TypeSafeAuthRoutes.swift

Open your TypeSafeAuthRoutes.swift file:

open Sources/Application/Routes/TypeSafeAuthRoutes.swift

Inside this file, add the framework for our routes code:

import KituraContracts
import Credentials
import CredentialsHTTP
import LoggerAPI

func initializeTypeSafeAuthRoutes(app: App) {
    app.router.get("/basic", handler: app.protectedGetHandler)
}

extension App {
    // Define handlers here
}

In this guide, we will have a single route GET route. This route will only return a book to a user who has authenticated using HTTP basic authentication.


Step 3: Using TypeSafeHTTPBasic in a route

Underneath our other Codable routes we will add:

func protectedGetHandler(user: MyBasicAuth, respondWith: (Book?, RequestError?) -> Void) {

}

This route is returning the Book model from the routing guide, however you could use any Codable type.

We have registered MyBasicAuth on our handler by adding it to the signature.

When the handler is called the middleware and the request is authenticated.

Within protectedGetHandler, we can then interact with our authenticated user:

Log.info("authenticated: \(user.id)")
let secretBook = Book(id: 451, title: "1984", price: 9001, genre: "Science Fiction")
respondWith(secretBook, nil)

That's it! We've implemented HTTP basic authentication of a Codable route.

Your completed TypeSafeAuthRoutes.swift should now look as follows:

import KituraContracts
import Credentials
import CredentialsHTTP
import LoggerAPI

func initializeTypeSafeAuthRoutes(app: App) {
    app.router.get("/basic", handler: app.protectedGetHandler)
}

extension App {
    func protectedGetHandler(user: MyBasicAuth, respondWith: (Book?, RequestError?) -> Void) {
        Log.info("authenticated \(user.id)")
        let secretBook = Book(id: 451, title: "1984", price: 9001, genre: "Science Fiction")
        respondWith(secretBook, nil)
    }
}

Step 4: Test TypeSafeHTTPBasic routes

We need to first start our Kitura server.

Then navigate to: localhost:8080/basic

Log in with the credentials we defined:

User Name: username
Password: password

We then should be logged in and see "username" in the browser.

The browser will store your login credentials and automatically log you in if you return to the route.

Use a private window if you would like to test incorrect authentication.


Next steps

Web Application: Learn about web applications and what Kitura provides.