Server Side Swift with Vapor - Part 27: Passwords | Ray Wenderlich

Learn how to change your user models to allow authentication with passwords and create a token model.


This is a companion discussion topic for the original entry at https://www.raywenderlich.com/4493-server-side-swift-with-vapor/lessons/27

Just to prevent confusion, there’s one thing to keep in mind for this video - adding the password property to the User class misses out on modifying the init method for the class to include the new property. In fact, the code shown at around 2:40 does not show the init method at all, even though it is needed. But if you look at the video at around 3:45, you’ll see the working User class code with the proper init method. You will get errors if you do not modify the init method as shown there though the video does not mention this :slight_smile:

Hmm I’m sure I would have changed that when I did the latest round of re-records! But good spot! There may be some new edits going up so I’ll try and squeeze it in then

Hmm thanks for the heads up @fahim … same with on 4:18, the User class suddenly already has that extensions BasicAuthenticatable and TokenAuthenticatable which i’m not sure when did @0xtim add that :thinking:

Ouch getting Value of type 'EventLoopFuture<User>' has no member password error on Xcode

func createHandler(_ req: Request) throws -> Future<User> {
        let user = try req.content.decode(User.self)
        let hasher = try req.make(BCryptHasher.self)
        user.password = try hasher.make(user.password)
        return user.save(on: req)
    }

Hmm looks something has gone wrong somewhere - those extensions are in the next video! As for the createHandler issue - try req.content.decoder(User.self) returns a Future<User> so you need to unwrap the user somehow. In the video it is done as:

func createHandler(_ req: Request) throws -> Future<User> {
  return try req.content.decoder(User.self).flatMap(to: User.self) { user in
    let hasher = try req.make(BCryptHasher.self)
    user.password = try hasher.make(user.password)
    return user.save(on: req)
  }
}

Ouh again you help me fix it! Guess i tried to be smart after seeing the other createHandler :disappointed_relieved: … thank you!

1 Like

Guys, Im encountering an error :

incorrect argument labels in call (have 'message:matches:', expected '_:created:') return try BCrypt.verify(message: password, matches: hash)

It happens in AuthenticationProvider.swift to be exact in :

/// A struct password verifier around bcrypt
public final class BCryptVerifier: PasswordVerifier, Service {
/// Create a new bcrypt verifier
public init() {}
/// See PasswordVerifier.verify public func verify(password: String, matches hash: String) throws -> Bool { return try BCrypt.verify(message: password, matches: hash) } }

My dependencies are exactly the same as the ones used in video:
// 💧 A server-side Swift web framework. .package(url: "https://github.com/vapor/vapor.git", from: "3.0.0-rc"), // 🔵 Swift ORM (queries, models, relations, etc) built on SQLite 3. .package(url: "https://github.com/vapor/fluent-mysql.git", from: "3.0.0-rc"), .package(url: "https://github.com/vapor/leaf.git", from: "3.0.0-rc"), .package(url: "https://github.com/vapor/auth.git", from: "2.0.0-rc")

So Im not sure why is this happening. Did I forget to import something ?

@deor run vapor update to pull down the latest changes - those issues should be fixed.

There are a couple of changes required from what’s in the video - see Changes for the Ray Wenderlich Vapor Videos for Vapor 3 RC 2 · GitHub

The new videos will be recorded when Vapor 3 is released, which should be end of this week/next week

@0xtim Updating worked perfectly, Thanks again!

1 Like

Hello @0xtim

I am very confused now with the code because, first I got a couple of errors pointing that BCryptHasher is deprecated and BCryptoDigest then started to make changes I think I do not get it and at the website that tracks all the changes I could not find something about it. Below is a list of comments:

1.- Crypto has to be added as import
2.- BCryptHasher had been renamed to BCryptoDigest
3.- It does not have a “make” method but a “hash” one.
4.- Once the name is changed to “BCryptoDigest” the framework “Crypto” has to be imported
5.- In the video at 4:14 all of a sudden there is some code related to Authentication
6.- Once the method is changed from “make” to “hash” the resulting datatype is “Data” not a String therefore I am trying to rewrite the function for this, I am learning Swift so probably I am making a lot of mistakes, probably need a more secure way to handle the hasher:

	func createHandler(_ req: Request) throws -> Future<User> {
		return try req.content.decode(User.self).flatMap(to: User.self) { user in
			let hasher = try req.make(BCryptDigest.self)
			let dataPassword: Data? = try hasher.hash(user.password)
			user.password = String(data: dataPassword!, encoding: String.Encoding.utf8)!
			return user.save(on: req)
		}
	}

7.- My Package.swift
$ cat Package.swift
// swift-tools-version:4.0
import PackageDescription

let package = Package(
name: “TILApp”,
dependencies: [
// :droplet: A server-side Swift web framework.
.package(url: “GitHub - vapor/vapor: 💧 A server-side Swift HTTP web framework.”, from: “3.0.0-rc.2”),
.package(url: “GitHub - vapor/fluent-mysql-driver: 🖋🐬 Swift ORM (queries, models, relations, etc) built on MySQL.”, from: “3.0.0-rc.2”),
.package(url: “GitHub - vapor/leaf: 🍃 An expressive, performant, and extensible templating language built for Swift.”, from: “3.0.0-beta.2”),
.package(url: “GitHub - vapor/auth: 👤 Authentication and Authorization framework for Fluent.”, from: “2.0.0-rc”)
],
targets: [
.target(name: “App”, dependencies: [“FluentMySQL”, “Vapor”, “Leaf”, “Authentication”]),
.target(name: “Run”, dependencies: [“App”]),
.testTarget(name: “AppTests”, dependencies: [“App”])
]
)

8.- Crypto has to be added to Package.swift?, if so, would it be 3.0.1?

Does anyone have encountered the same issue?, am I missing the point here?

I was hoping you can shine a light on this, thank you very much in advanced.

Have a good one.

As mentioned in Slack for the benefit of anyone else with these issues - see this gist for changes that should help! Once 3.0 is tagged I’ll redo the videos

Hello,

Thank you very much for your support.

I have included the changes suggested in slack. Now I am getting the following situation:

1.- Using rested to create a user (username, name, password)
2.- When sending the request it times out, therefore no answer.
3.- When refreshing the list of users in the browser the user had been created, then checking the DB I can see the password is there (hashed)
4.- After refreshing the browser. I see this in XCode console:

[ INFO ] Migrating 'mysql' database (FluentProvider.swift:28)
[MySQL] ⚠️ [MySQLError.basicPacket: Unrecognized basic packet.] [/Users/fran/rwvapor/TILApp/.build/checkouts/mysql.git-1890032512239690518/Sources/MySQL/Pipeline/MySQLPacketDecoder.swift:83:111]
[ INFO ] Migrations complete (FluentProvider.swift:32)
Running default command: /Users/fran/Library/Developer/Xcode/DerivedData/TILApp-hgmdcbhkkhsvbgfpkjxciebtjygj/Build/Products/Debug/Run serve
Server starting on http://localhost:8080[ ERROR ] read(descriptor:pointer:size:) failed: Connection reset by peer (errno: 54)  (EngineServer.swift:166)

Just one more thing: I have, cleaned, then built, recreated the DB a couple of times, restarted XCode, just in case and finally upgraded vapor

Thank you very much for your support

1 Like

Just an update @tanner0101 suggested to use 127.0.0.1 due to a bug in Swift NIO. Looks like this also does not solve the issue.

I am cleaning again just to double check.

Thank you very much @tanner0101 ++ :stuck_out_tongue:

Hello @0xtim and @tanner0101 this is how I got the issue solved:

1.- Checking the GitHub of Crypto I realized that there is 3.1.0 version so I verified if I am using that very same version which I did not (I was on 3.0.1)
2.- Changed the version Package.swift to 3.1.0
3.- Did “vapor update”
4.- Cleaned and rebuilt in Xcode
5.- Changed all the deprecated methods
6.- One of the things I just noticed is that “hash” returns String now not Data so one more change.
7.- Built and run et voilá! it worked :smiley:

Thank you very much for your support and patience :smiley:

1 Like

Looks like you didn’t need much help at all! I’ll update the gist at some point with the new crypto changes

Hello @0xtim , may I help somehow with that?

Thank you very much again.

Feel free to add a comment to the Gist until I get a chance to update it!

I had to use import Random to have OSRandom. import Crypto did not work. (Using Crypto 3.1.0)

That sounds like a bug, I’ll investigate