Now that there is a FoodServer backend for the FoodTracker app, you can add HTTP basic authentication to the routes. This will allow your users to log in, have the server identify them and respond accordingly.
These instructions follow on from the FoodTracker application and server created by following the FoodTrackerBackend tutorial. If you have completed the FoodTracker Backend there are no further pre-requisites.
If you have not completed the FoodTrackerBackend tutorial go to the CompletedFoodTracker branch and follow the README instructions.
HTTP Basic authentication transmits credentials in an “Authorization” header as base64 encoded user ID/password pairs. Kitura also allows you to send the username and password in the URL as follows:
https://username:[email protected]/
Note: some web browsers disable this for security reasons.
Kitura-CredentialsHTTP is a Kitura-Credentials plugin that lets you perform HTTP basic authentication and needs to be added to our Package.swift
file.
- In the terminal, go to your server's
Package.swift
file.
cd ~/FoodTrackerBackend/FoodServer
open Package.swift
- Add the
Kitura-CredentialsHTTP
package:
.package(url: "https://github.com/IBM-Swift/Kitura-CredentialsHTTP", from: "2.1.0"),
- Change the target for Application to include "CredentialsHTTP":
.target(name: "Application", dependencies: [ "Kitura", "CloudEnvironment", "SwiftMetrics", "Health", "SwiftKueryORM", "SwiftKueryPostgreSQL", "CredentialsHTTP"]),
- Regenerate your FoodServer Xcode project:
swift package generate-xcodeproj
We will declare a struct 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.
- Open the FoodServer Xcode project
open FoodServer.xcodeproj/
- Inside
Sources > Application > Application.swift
addCredentialsHTTP
to your imports:
import CredentialsHTTP
Below the Persistence
Class, define a public struct called MyBasicAuth
that conforms to the TypeSafeHTTPBasic
protocol:
public struct MyBasicAuth: TypeSafeHTTPBasic {
}
- Xcode should display the message:
Type 'MyBasicAuth' does not conform to protocol 'TypeSafeCredentials'
Click "Fix" to autogenerate the stubs below:
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"]
- The function,
verifyPassword
, takes a username and password and, on success, returns aMyBasicAuth
instance. We want to check if the password matches the user's stored password. On successful match, we initializeMyBasicAuth
with anid
equal to username.
if let storedPassword = authenticate[username], storedPassword == password {
callback(MyBasicAuth(id: username))
return
}
callback(nil)
This function is async, so that you can perform async actions to verify the password, e.g. looking up the username and password in a database. You must call the callback closure with either an instance of 'Self' or 'nil' before exiting 'verifyPassword'. If you do not, the server will not know to continue and you will receive a 503 "Service Unavailable" error, when you call the route.
Your complete 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 {
callback(MyBasicAuth(id: username))
return
}
callback(nil)
}
public var id: String
}
MyBasicAuth
is a Type-safe middleware and can be registered to a Codable route by adding it to the handler signature.
Add auth: MyBasicAuth
to the completion closure in the storeHandler signature.
func storeHandler(auth: MyBasicAuth, meal: Meal, completion: @escaping (Meal?, RequestError?) -> Void ) {
Add auth: MyBasicAuth
to the completion closure in the loadHandler signature.
func loadHandler(auth: MyBasicAuth, completion: @escaping ([Meal]?, RequestError?) -> Void ) {
Add auth: MyBasicAuth
to the completion closure in the summaryHandler signature.
func summaryHandler(auth: MyBasicAuth, completion: @escaping (Summary?, RequestError?) -> Void ) {
These routes now require basic authentication. You can test this by running the server and going to http://localhost:8080/summary.
The request will be rejected as unauthorized and your browser will offer a window for you to input the username and password.
Enter "username" and "password" to be allowed to view the route or any other combination to have the request rejected.
The browser will store correct credentials so use a private window if you want to test rejected credentials.
Congratulations!!! You have just added HTTP basic authentication to your Kitura server.
You need to enable your food tracker mobile application to send the username and password so that it can continue to connect to your now protected routes. You can do this by setting the default credentials on the KituraKit client as shown below.
- Open the FoodTracker workspace:
cd ~/FoodTrackerBackend/iOS/FoodTracker/
open FoodTracker.xcworkspace
- Open the
FoodTracker > MealTableViewController.swift
file - Add default credentials inside the
saveToServer
function to be used by the client:
client.defaultCredentials = HTTPBasic(username: "username", password: "password")
This will add the credentials to all your KituraKit requests.
- You could also add credentials on individual routes as follows:
client.post("/meals", data: meal, credentials: HTTPBasic(username: "username", password: "password"))
However that is not required for this example.
Now your Foodtracker app and server will be able to send and receive requests using basic authentication!