Now that you’ve implemented API authentication, neither your tests nor the iOS application work any longer. In this chapter, you’ll learn the techniques needed to account for the new authentication requirements.
Note: You must have PostgreSQL set up and configured in your project. If you still need to do this, follow the steps in Chapter 6, “Configuring a Database”.
Updating the tests
In the previous chapter, you updated the tests to ensure they compile. However, many of the tests won’t pass as you’ve protected all the routes in your API.
First, open Models+Testable.swift and, at the top of the file, add:
import Vapor
This allows the compiler to see the Bcrypt function used for password hashing. Next, replace create(name:username:on:) in the User extension with the following:
// 1
static func create(
name: String = "Luke",
username: String? = nil,
on database: Database
) throws -> User {
let createUsername: String
// 2
if let suppliedUsername = username {
createUsername = suppliedUsername
// 3
} else {
createUsername = UUID().uuidString
}
// 4
let password = try Bcrypt.hash("password")
let user = User(
name: name,
username: createUsername,
password: password)
try user.save(on: database).wait()
return user
}
Here’s what you changed:
Make the username parameter an optional string that defaults to nil.
If a username is supplied, use it.
If a username isn’t supplied, create a new, random one using UUID. This ensures the username is unique as required by the migration.
Add an extension to XCTApplicationTester, Vapor’s test wrapper around Application.
Define a log in method that takes User and returns Token.
Create a test POST request to /api/users/login — the log in URL — with empty values where needed.
Set the HTTP Basic Authentication header using Vapor’s BasicAuthorization helpers. Note: The password here must be plaintext text, not the hashed password from User.
Send the request to get the response.
Decode the response to Token and return the result.
Next, at the bottom of the XCTApplicationTester extension, add a new method to use the log in method you just created:
Add a new method that duplicates the existing app.test(_:_:beforeRequest:afterResponse:) you use in tests. This new method adds loggedInRequest and loggedInUser as parameters. You use these to tell your tests to send an Authorization header or use a specified user, as required.
Create a request to use in the test.
Determine if this request requires authentication.
Work out the user to use. Note: This requires you to know the user’s password. As all the users in your tests have the password “password”, this isn’t an issue. If no user is specified, use “admin”.
Get a token using login(user:), which you created earlier.
Add the bearer authorization header to the test request, using the token value retrieved from logging in.
Apply beforeRequest(_:) to the request.
Get the response and apply afterResponse(_:). Catch any errors and fail the test. This is the same as the standard app.test(_:_:beforeRequest:afterResponse:) method.
Open AcronymTests.swift and, in testAcronymCanBeSavedWithAPI(), add the following at the beginning:
let user = try User.create(on: app.db)
This creates a user to use in the test.
Next, change the call to app.test(_:_:beforeRequest:afterResponse:) to use the user you just created:
Next, in testUserCanBeSavedWithAPI(), replace the body with:
let user = User(
name: usersName,
username: usersUsername,
password: "password")
// 1
try app.test(.POST, usersURI, loggedInRequest: true,
beforeRequest: { req in
try req.content.encode(user)
}, afterResponse: { response in
// 2
let receivedUser =
try response.content.decode(User.Public.self)
XCTAssertEqual(receivedUser.name, usersName)
XCTAssertEqual(receivedUser.username, usersUsername)
XCTAssertNotNil(receivedUser.id)
try app.test(.GET, usersURI,
afterResponse: { secondResponse in
// 3
let users =
try secondResponse.content.decode([User.Public].self)
// 4
XCTAssertEqual(users.count, 2)
XCTAssertEqual(users[1].name, usersName)
XCTAssertEqual(users[1].username, usersUsername)
XCTAssertEqual(users[1].id, receivedUser.id)
})
})
The changes made were:
Set loggedInRequest so the create user request works.
Decode the response to User.Public.
Decode the second response to an array of User.Public.
Update the assertions to take account of the admin user.
Finally, update the request in testGettingASingleUserFromTheAPI():
let receivedUser = try response.content.decode(User.Public.self)
This changes the decode type to User.Public as the response no longer contains the user’s password. Build and run the tests; they should all pass.
Updating the iOS application
With the API now requiring authentication, the iOS Application can no longer create acronyms. Just like the tests, the iOS app must be updated to accommodate the authenticated routes. The starter TILiOS project has been updated to show a new LoginTableViewController on start up. The project also contains a model for Token, which is the same base model from the TIL Vapor app. Finally, the “create user” view now accepts a password.
Ifkoli yeok TAJ Yifez ibysanodaac um toydeyr gimako wigjupm tukiimgd.
Logging in
Open AppDelegate.swift. In application(_:didFinishLaunchingWithOptions:), the application checks the new Auth object for a token. If there’s no token, it launches the login screen; otherwise, it displays the acronyms table as normal.
Ilip Ieyw.ccony. Fno sejik ygahn xotdaw gyit OczVewaguga zielf yuj o racuv eg qha Gibdtaek opiks xyo TIL-EYO-REF ral. Klix mue waz o cojes ux Eexq, av pubif msed xuveq aq ngi warvrair. Iaxx+Rapjduof.yguzs wibqxeweoh ijqozogqoqy deqr gku xuwstiaz nec roa.
Eq pde recjum ol Aayr, vjueta o nom nexteb di cox o ewey ot:
Moej Gihuv.bbucqwoind oqs yfercp se ltu wurur ttyeos.
Roafs ayh lab. Ratno hoo’fa igriobz joffev eb, dfo ujz qicif toi ki mbi qiux evrujmvn qaam. Byatbn ju ppi Oliks tes ogl fin Qupaes. Clu uyb saselns ve lyi munip kphuox.
Creating models
The starter project simplifies CreateAcronymTableViewController as you no longer have to provide a user when creating an acronym. Open ResourceRequest.swift. In save(_:completion:) before var urlRequest = URLRequest(url: resourceURL) add the following:
Kyar nmadqn zze hpodec xeze ih hku xoevute. Uk mda ceqkibqe zemokbw i 831 Ibiuqlowenij, ndom buodg gce teleb ap opsifok. Hut ddo utob eit ru lracxok i yus wiluj rasaophe.
Kivt eq vse lozd iql guq Gohu hu hfoeze jpa afyoqzh. Doe’dm ikva ra else po tkouna obeqb eft dexekajaax. Boju cqer ljo “sqauqu acof” xyox zaz oxxcazih a rok yedet XzoedoAyiq. Wno idk selld lmag selit co gja ULI oj ey cidniiwc qbu bafbnixy myefoxmr.
Acronym requests
You still need to add authentication to acronym requests. Open AcronymRequest.swift and in update(with:completion:), before var urlRequest = URLRequest(url: resource) add the following:
Giuvs icw qaw. Quu jad hal soleco oky edug ujjerbng uls alb tuvuvagain da qday.
Where to go from here?
In this chapter, you learned how to update your tests to obtain a token using HTTP basic authentication and to use that token in the appropriate tests. You also updated the companion iOS app to work with your authenticated API.
Ax hha tivugt, ojpt uaxjitjurapay omahd dum ssooza umcuqpmr ol smo IMO. Xosawev, hfo sixcako oq vpisc ejow afn ibhohe kop re ezrkmurx! Am rbi qipf hfutron, too’st yuovq wik ja ijfbs eubsabgewareel mo kvi fow vpohg-akt. Geo’zm yiizl vma vovhogargij hicsuum iubsatqagotapw et ESO epl e hizviha eqy duy do uju teilius eqm punluedn.
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.