# Type 'User' does not conform to protocol 'ModelAuthenticatable'

**URL:** <https://forums.swift.org/t/type-user-does-not-conform-to-protocol-modelauthenticatable/84977>\
**Category:** Vapor\
**Created:** [February 25, 2026, 1:19pm UTC](https://forums.swift.org/t/type-user-does-not-conform-to-protocol-modelauthenticatable/84977 "2026-02-25T13:19:31Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![simon68](https://avatars.discourse-cdn.com/v4/letter/s/9d8465/32.png) [@simon68](https://forums.swift.org/u/simon68)\
**Post date:** [February 25, 2026, 1:19pm UTC](https://forums.swift.org/t/type-user-does-not-conform-to-protocol-modelauthenticatable/84977/1 "2026-02-25T13:19:31Z")

</div>

Trying to apply [Vapor Doc Security -\> Authentication](https://docs.vapor.codes/security/authentication/) when adding the snippet:

```swift
extension User: ModelAuthenticatable {
    static let usernameKey = \User.$email
    static let passwordHashKey = \User.$passwordHash

    func verify(password: String) throws -> Bool {
        try Bcrypt.verify(password, created: self.passwordHash)
    }
}

```

I get the message from Xcode:

 ![Capture d’écran 2026-02-25 à 14.14.47](https://global.discourse-cdn.com/swift/original/3X/8/1/81923b3b3eb4c3e1ab2f6c4cde05f163c733bb81.png)

Is it safe to `Add stubs for conformance` ?

If so, what exactly should be the code to be added in the closures ?

```swift
    static var usernameKey: KeyPath<User, FluentKit.FieldProperty<User, String>> {
        <#code#>
    }
    
    static var passwordHashKey: KeyPath<User, FluentKit.FieldProperty<User, String>> {
        <#code#>
    }

```

Sorry I am too new in Vapor to find out by myself ! Thanks for helping.

---

<div class="post-metadata">

**Author:** ![0xTim](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/0xtim/32/27968_2.png) [@0xTim](https://forums.swift.org/u/0xTim)\
**Post date:** [February 25, 2026, 1:49pm UTC](https://forums.swift.org/t/type-user-does-not-conform-to-protocol-modelauthenticatable/84977/2 "2026-02-25T13:49:23Z")

</div>

What does your user model look like?

---

<div class="post-metadata">

**Author:** ![simon68](https://avatars.discourse-cdn.com/v4/letter/s/9d8465/32.png) [@simon68](https://forums.swift.org/u/simon68)\
**Post date:** [February 25, 2026, 1:55pm UTC](https://forums.swift.org/t/type-user-does-not-conform-to-protocol-modelauthenticatable/84977/3 "2026-02-25T13:55:38Z")

</div>

Quite the same as in the Docs I believe :

```swift
import Fluent
import Vapor

final class User: Model, @unchecked Sendable, Content {
    static let schema = "users"
    
    @ID(key: .id)
    var id: UUID?
    
    @Field(key:"name")
    var name: String
    
    @Field(key:"password_hash")
    var passwordHash: String
    
    @Field(key:"email")
    var email: String
    
    @Children(for: \.$user)
    var todos: [Todo]
    
    init(){}
    
    init(id: UUID? = nil, name: String, email: String, passwordHash: String) {
        self.id = id
        self.name = name
        self.email = email
        self.passwordHash = passwordHash
    }
    func toDTO() -> UserDTO {
        .init(
            id: self.id,
            name: self.$name.value,
            email: self.$email.value,
            passwordHash: self.$passwordHash.value
        )
    }
}

```

only a `children` field more.

I am using Xcode 26.2. Is this not linked to the new thread safe policy ?

---

<div class="post-metadata">

**Author:** ![0xTim](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/0xtim/32/27968_2.png) [@0xTim](https://forums.swift.org/u/0xTim)\
**Post date:** [February 25, 2026, 11:41pm UTC](https://forums.swift.org/t/type-user-does-not-conform-to-protocol-modelauthenticatable/84977/4 "2026-02-25T23:41:18Z")

</div>

Ah yeah it’s the Sendable issues with key-paths. Try something like [VaporAuthExample/Sources/App/Models/User.swift at main · 0xTim/VaporAuthExample · GitHub](https://github.com/0xTim/VaporAuthExample/blob/main/Sources/App/Models/User.swift#L6-L7)

---

<div class="post-metadata">

**Author:** ![simon68](https://avatars.discourse-cdn.com/v4/letter/s/9d8465/32.png) [@simon68](https://forums.swift.org/u/simon68)\
**Post date:** [February 26, 2026, 9:16am UTC](https://forums.swift.org/t/type-user-does-not-conform-to-protocol-modelauthenticatable/84977/5 "2026-02-26T09:16:45Z")

</div>

yes @0xTim that's what I did, following assistant's tip:

```swift
extension User: ModelAuthenticatable {
    static var usernameKey: KeyPath<User, FluentKit.FieldProperty<User, String>> { \User.$email }
    
    static var passwordHashKey: KeyPath<User, FluentKit.FieldProperty<User, String>> { \User.$password }

    func verify(password: String) throws -> Bool {
        try Bcrypt.verify(password, created: self.password)
    }
}

```

(Note that in the meantime, for convenience, I changed `passwordHash` to `password`)

Now my problem is I do not get the login route as per Vapor Doc to run:

Here is the controller :

```swift
    func boot(routes: any RoutesBuilder) throws {
        let users = routes.grouped("users")
        users.post(use: self.create)
        let passwordProtected = users.grouped(User.authenticator(), User.guardMiddleware())
        passwordProtected.post("login", use: self.login)
    }
    
    @Sendable
    func login(req: Request) async throws -> ClientTokenResponse {
        let user = try req.auth.require(User.self)
        print(user)
        let payload = try SessionToken(with: user)
        return ClientTokenResponse(token: try await req.jwt.sign(payload))
    }

```

In my database I have at least one user:

 ![Capture d’écran 2026-02-26 à 10.07.59](https://global.discourse-cdn.com/swift/original/3X/8/c/8cf6afe95b20e419a85119a832c9280b27daeb34.png)

Let's consider `Emile`. The command :

```swift
% echo -n "emile@example.com:secret" | base64

```

produces:  
`ZW1pbGVAZXhhbXBsZS5jb206c2VjcmV0`

But when I send the request:  
`% curl -H "Authorization: Basic ZW1pbGVAZXhhbXBsZS5jb206c2VjcmV0" http://127.0.0.1:8080/users/login`

I receive an error:  
`{"reason":"Not Found","error":true} **%** `

I can't figure out what I am doing wrong. Can someone help ?

---

<div class="post-metadata">

**Author:** ![ptoffy](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ptoffy/32/44481_2.png) [@ptoffy](https://forums.swift.org/u/ptoffy)\
**Post date:** [February 26, 2026, 9:44am UTC](https://forums.swift.org/t/type-user-does-not-conform-to-protocol-modelauthenticatable/84977/6 "2026-02-26T09:44:40Z")

</div>

For one adding `User.guardMiddleware()` on the login route doesn't really make sense as that throws a 401 if the user is not logged in. You're also trying to verify a plaintext password using bcrypt, or at least that's what you showed in your DB screenshot/token creation command. Finally, regarding your 404, did you register your controller using `app.register(collection: YourControllerHere()`?

---

<div class="post-metadata">

**Author:** ![simon68](https://avatars.discourse-cdn.com/v4/letter/s/9d8465/32.png) [@simon68](https://forums.swift.org/u/simon68)\
**Post date:** [February 26, 2026, 10:15am UTC](https://forums.swift.org/t/type-user-does-not-conform-to-protocol-modelauthenticatable/84977/7 "2026-02-26T10:15:58Z")

</div>

@ptoffy doesn't make sense ? Yet this is the snippet from `Vapor Doc -> Authentication -> JWT` :

```swift
let passwordProtected = app.grouped(User.authenticator(), User.guardMiddleware())
passwordProtected.post("login") { req async throws -> ClientTokenResponse in
    let user = try req.auth.require(User.self)
    let payload = try SessionToken(with: user)
    return ClientTokenResponse(token: try await req.jwt.sign(payload))
}

```

Regarding the paswword for Basic Authorization, I followed this section of the doc:

 ![Capture d’écran 2026-02-26 à 11.11.13](https://global.discourse-cdn.com/swift/original/3X/6/b/6b3800e3eddf5092984d0a394c053aa3d4201ebe.png)

Should I not ?

Finally, at least I can confirm the controller is registred in the routes:

```swift
func routes(_ app: Application) throws {
    app.get { req async in
        "It works!"
    }
    app.get("hello") { req async -> String in
        "Hello, world!"
    }
    try app.register(collection: UserController())
}

```

So what can I do next ?

---

<div class="post-metadata">

**Author:** ![0xTim](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/0xtim/32/27968_2.png) [@0xTim](https://forums.swift.org/u/0xTim)\
**Post date:** [February 26, 2026, 11:22am UTC](https://forums.swift.org/t/type-user-does-not-conform-to-protocol-modelauthenticatable/84977/8 "2026-02-26T11:22:13Z")

</div>

Looking at your database - is your password stored in plaintext?

---

<div class="post-metadata">

**Author:** ![simon68](https://avatars.discourse-cdn.com/v4/letter/s/9d8465/32.png) [@simon68](https://forums.swift.org/u/simon68)\
**Post date:** [February 26, 2026, 11:39am UTC](https://forums.swift.org/t/type-user-does-not-conform-to-protocol-modelauthenticatable/84977/9 "2026-02-26T11:39:39Z")

</div>

Obviously, yes.  
OK @0xTim I got it. Password hashigh is in the next lab !

---

<div class="post-metadata">

**Author:** ![0xTim](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/0xtim/32/27968_2.png) [@0xTim](https://forums.swift.org/u/0xTim)\
**Post date:** [February 26, 2026, 11:53am UTC](https://forums.swift.org/t/type-user-does-not-conform-to-protocol-modelauthenticatable/84977/10 "2026-02-26T11:53:00Z")

</div>

👍

Just to be clear, the issue you’re hitting is that your verify function is trying to check the hash of your password provided with the password stored and they obviously don’t match because they’re stored in plaintext (and please, please, please never do this)

---

<div class="post-metadata">

**Author:** ![simon68](https://avatars.discourse-cdn.com/v4/letter/s/9d8465/32.png) [@simon68](https://forums.swift.org/u/simon68)\
**Post date:** [February 26, 2026, 12:09pm UTC](https://forums.swift.org/t/type-user-does-not-conform-to-protocol-modelauthenticatable/84977/11 "2026-02-26T12:09:37Z")

</div>

So I suppose a good practice would be to have a `passwordHash` property in `User` and a `password` one in UserDTO. And make the conversion in the `toModel()` function at the call of `UserController`'s `create()` function.

Right?

---

<div class="post-metadata">

**Author:** ![0xTim](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/0xtim/32/27968_2.png) [@0xTim](https://forums.swift.org/u/0xTim)\
**Post date:** [February 26, 2026, 12:24pm UTC](https://forums.swift.org/t/type-user-does-not-conform-to-protocol-modelauthenticatable/84977/12 "2026-02-26T12:24:26Z")

</div>

Have a `password` in the `CreateUserDTO` but not in the `UserDTO` - you don’t want to be returning password hashes in your responses

---

<div class="post-metadata">

**Author:** ![simon68](https://avatars.discourse-cdn.com/v4/letter/s/9d8465/32.png) [@simon68](https://forums.swift.org/u/simon68)\
**Post date:** [February 26, 2026, 12:56pm UTC](https://forums.swift.org/t/type-user-does-not-conform-to-protocol-modelauthenticatable/84977/13 "2026-02-26T12:56:39Z")

</div>

Ok @0xTim that's how I did it:

I replaced the `password` column in Users by `password_hash`, and updated the migration `CreateUser` accordingly.

In the UserDTO however I kept the password property and modified the toModel() function as follows:

```swift
        if let password = self.password {
            model.passwordHash = try Bcrypt.hash(password)
        }

```

I update the user authentication verify func:

```swift
extension User: ModelAuthenticatable {
    static var usernameKey: KeyPath<User, FluentKit.FieldProperty<User, String>> { \User.$email }
    
    static var passwordHashKey: KeyPath<User, FluentKit.FieldProperty<User, String>> { \User.$passwordHash }

    func verify(password: String) throws -> Bool {
        try Bcrypt.verify(password, created: self.passwordHash)
    }
}

```

Which now operates on passwordHash indeed.

By the way, in the reverse function `toDTO()` in `User` model, I just set password to nil:

```swift
    func toDTO() -> UserDTO {
        .init(
            id: self.id,
            name: self.$name.value,
            email: self.$email.value,
            password: nil
        )
    }

```

I also updated the `create()` function of the `UserController` to decode on `userDTO`:

```swift
    @Sendable
    func create(req: Request) async throws -> UserDTO {
        let user = try req.content.decode(UserDTO.self).toModel()
        try await user.save(on: req.db)
        return user.toDTO()
    }

```

At least I managed to have the passwordHashes stored in the database, instead of the passwords themselves 😌

 ![Capture d’écran 2026-02-26 à 13.35.53](https://global.discourse-cdn.com/swift/original/3X/6/f/6f4872bb0c9a9cbbdb9d0f55b6b20d41000a64a7.png)

The bad news is I still have the same error when trying to login as previously 🙁

---

<div class="post-metadata">

**Author:** ![ptoffy](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ptoffy/32/44481_2.png) [@ptoffy](https://forums.swift.org/u/ptoffy)\
**Post date:** [February 27, 2026, 8:23am UTC](https://forums.swift.org/t/type-user-does-not-conform-to-protocol-modelauthenticatable/84977/14 "2026-02-27T08:23:50Z")

</div>

@simon68 it looks like you're making a GET request to a POST endpoint, that's why you get a 404. You need to add `-X POST` to your curl request

```swift
curl -X POST \
  -H "Authorization: Basic ZW1pbGVAZXhhbXBsZS5jb206c2VjcmV0" \
  http://127.0.0.1:8080/users/login

```

---

<div class="post-metadata">

**Author:** ![simon68](https://avatars.discourse-cdn.com/v4/letter/s/9d8465/32.png) [@simon68](https://forums.swift.org/u/simon68)\
**Post date:** [February 27, 2026, 8:49am UTC](https://forums.swift.org/t/type-user-does-not-conform-to-protocol-modelauthenticatable/84977/15 "2026-02-27T08:49:41Z")

</div>

ok @ptoffy I thought it had to be something stupid, and it was ! Thanks.

---

<div class="post-metadata">

**Author:** ![simon68](https://avatars.discourse-cdn.com/v4/letter/s/9d8465/32.png) [@simon68](https://forums.swift.org/u/simon68)\
**Post date:** [March 18, 2026, 1:44pm UTC](https://forums.swift.org/t/type-user-does-not-conform-to-protocol-modelauthenticatable/84977/17 "2026-03-18T13:44:40Z")

</div>

Hi all,

got a new issue I want to share.

Testing my login route manually with :

```swift
curl -X POST http://127.0.0.1:8081/users/login -H "Authorization: Basic Ym9iQGV4YW1wbGUuY29tOnNlY3JldA=="

```

Produces the expected result :

```swift
{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiIwQTI0MzI5NS0xNUI4LTRERUMtQTExMi03MzczNTk0QTgxRUYiLCJleHBpcmF0aW9uIjoxNzczODQxNTcxLjE1MjQ3N30.ZM6ogjEIxo3ogVp996da9eMu8zaP1_fqjl-05MbPjPk"}

```

However, loging in from my app with:

```swift
    func login() async {
        guard let login = profile.login else { return }
        print ("login \(login)")
        let url = URL(string: API_URL + "login")!
        var request = URLRequest(url: url)
        request.httpMethod = "GET"
        request.setValue("Basic \(login)", forHTTPHeaderField: "Authorization")
        do {
            let (data, _) = try await URLSession.shared.data(for: request)
            let decoded = try JSONDecoder().decode(ClientTokenResponse.self, from: data)
            profile.token = decoded.token
        } catch {
            print("Erreur : \(error)")
        }
    }

```

Where :

```swift
struct ClientTokenResponse: Codable {
    var token: String
}

```

Produces an error:

```swift
keyNotFound(CodingKeys(stringValue: "token", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"token\", intValue: nil) (\"token\").", underlyingError: nil))

```

How can `token` be `nil` in this case ? What does `intValue` mean where a `String` in expected ?

By the way, I ensured the login was correct with the line :

```swift
print ("login \(login)")

```

And it matches the one sent manually :

```swift
login Ym9iQGV4YW1wbGUuY29tOnNlY3JldA==

```

What do I miss here ? Thanks for your help.

---

<div class="post-metadata">

**Author:** ![0xTim](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/0xtim/32/27968_2.png) [@0xTim](https://forums.swift.org/u/0xTim)\
**Post date:** [March 18, 2026, 2:01pm UTC](https://forums.swift.org/t/type-user-does-not-conform-to-protocol-modelauthenticatable/84977/18 "2026-03-18T14:01:28Z")

</div>

What is in the Vapor logs? Is the response status code 200 or something else?

---

<div class="post-metadata">

**Author:** ![simon68](https://avatars.discourse-cdn.com/v4/letter/s/9d8465/32.png) [@simon68](https://forums.swift.org/u/simon68)\
**Post date:** [March 18, 2026, 2:15pm UTC](https://forums.swift.org/t/type-user-does-not-conform-to-protocol-modelauthenticatable/84977/19 "2026-03-18T14:15:36Z")

</div>

Sorry @0xTim , what do you mean by Vapor logs?

The server log ?

```swift
[INFO] POST /users/login [request-id: 68FE08E8-96DA-4430-AB81-1D55F5A1C5CF]
User(output: SQLiteSQLRow(row: [users_id: "0A243295-15B8-4DEC-A112-7373594A81EF", users_name: "Bob", users_email: "bob@example.com", users_password_hash: "$2b$12$MfmSkmmSpAKokRVBm7r6Q.jkENJKO0DR9gqHV5.Rb.lSTgaHFVfxi"], decoder: SQLiteKit.SQLiteDataDecoder(json: SQLiteKit.SQLiteDataDecoder.FakeSendable<Foundation.JSONDecoder>(value: Foundation.JSONDecoder))))

```

---

<div class="post-metadata">

**Author:** ![0xTim](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/0xtim/32/27968_2.png) [@0xTim](https://forums.swift.org/u/0xTim)\
**Post date:** [March 18, 2026, 2:55pm UTC](https://forums.swift.org/t/type-user-does-not-conform-to-protocol-modelauthenticatable/84977/20 "2026-03-18T14:55:52Z")

</div>

Yeah are here any errors in the server log? And if you print the response body and status code back from the server, it should show you some hints as to what’s going on

---

<div class="post-metadata">

**Author:** ![0xTim](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/0xtim/32/27968_2.png) [@0xTim](https://forums.swift.org/u/0xTim)\
**Post date:** [March 18, 2026, 2:56pm UTC](https://forums.swift.org/t/type-user-does-not-conform-to-protocol-modelauthenticatable/84977/21 "2026-03-18T14:56:48Z")

</div>

> [@simon68](#):
>
> `let url = URL(string: API_URL + "login")!`

This line looks suspicious though, given the log you showed - they don’t line up

[Next page](https://forums.swift.org/t/type-user-does-not-conform-to-protocol-modelauthenticatable/84977.md?page=2)
