swiftvaporvapor-fluent

Vapor SessionAuthenticator not matching Session


I am trying to use the ModelSessionAuthenticator in Vapor (Swift), but it seems like Sessions are not persisted, while manually set SessionData is. Here is all code to reproduce. Run it and open /login. Then reload in your browser and you should see in the console that cookies are persisted but not matched to the session. Could you please help me?

func configure(_ app: Application) async throws {
    app.databases.use(DatabaseConfigurationFactory.postgres(configuration: .init(
        hostname: Environment.get("DATABASE_HOST") ?? "localhost",
        port: Environment.get("DATABASE_PORT").flatMap(Int.init(_:)) ?? SQLPostgresConfiguration.ianaPortNumber,
        username: Environment.get("DATABASE_USERNAME") ?? "vapor_username",
        password: Environment.get("DATABASE_PASSWORD") ?? "vapor_password",
        database: Environment.get("DATABASE_NAME") ?? "vapor_database",
        tls: .prefer(try .init(configuration: .clientDefault)))
    ), as: .psql)

    app.migrations.add(CreateUser())
    app.sessions.use(.memory)
    try await app.autoMigrate()
    app.middleware.use(app.sessions.middleware)

    try routes(app)
}

func routes(_ app: Application) throws {
    app.grouped(User.sessionAuthenticator()).get("login") { req in
        let users = try await User.query(on: req.db).all()
        if let user = users.first {
            print("new request..........................................")
            print("user id is: ", user.sessionID.uuidString)
            print(req.session.data)
            print(req.cookies)
            print("authenticated: ", req.session.authenticated(User.self) as Any)
            req.session.data["test"] = Date().formatted(date: .omitted, time: .complete) //this works
            print("authenticating.......................................")
            req.session.authenticate(user)
            print(req.session.data)
            print(req.cookies)
            print("authenticated: ", req.session.authenticated(User.self) as Any)
            print("request finished....................................")
            return HTTPStatus.ok
        } else {
            try await User(id: .generateRandom(), password: "abc123")?.create(on: req.db)
            return .created
        }
    }
}


final class User: Model, @unchecked Sendable, Authenticatable, ModelSessionAuthenticatable {

    static let passwordHashKey: KeyPath<User, Field<String>> = \.$passwordHash

    static let schema = "users"

    @ID(key: .id)
    var id: UUID?


    @Field(key: "password_hash")
    var passwordHash: String

    init() {}

    init(id: UUID, passwordHash: String) {
        self.id = id
        self.passwordHash = passwordHash
    }

    init?(id: UUID, password: String) {
        guard let hash = try? Bcrypt.hash(password) else { return nil }
        self.id = id
        self.passwordHash = hash
    }

    func verify(password: String) -> Bool {
        (try? Bcrypt.verify(password, created: passwordHash)) == true
    }
    var sessionID: UUID {id!}
}

Solution

  • Alright, I found the solution. Using req.session.authenticate doesn't work. I should use req.auth.login(user) instead.