Hot questions for Using Vapor in backend

Question:

I just started swift backend development. I noticed Vapor & installed swift packange manager (Swiftenv). I follow its instruction to install Vapor CLI by :

wget cli.qutheory.io -O vapor

But constantly get error:

Resolving cli.qutheory.io... 138.197.255.240
Connecting to cli.qutheory.io|138.197.255.240|:80... connected.
HTTP request sent, awaiting response... 404 Not Found
2016-08-07 10:41:15 ERROR 404: Not Found.

Anyone faces the same problem? How to get rid of it?

I also tried another way :

curl -L cli.qutheory.io -o vapor

The output is:

% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    38  100    38    0     0     21      0  0:00:01  0:00:01 --:--:--    21

Then, I run command:

chmod +x vapor

and then:

sudo mv vapor /usr/local/bin

and then:

vapor self-update

But I get error:

/usr/local/bin/vapor: line 1: error:1: command not found

How to install vapor CLI ?


Answer:

The Vapor team just released new setup tools. With xCode Beta 4, the following worked for me:

  • Move into your project folder
  • swiftenv local DEVELOPMENT-SNAPSHOT-2016-07-25-a
  • curl -sL toolbox.qutheory.io | bash
  • vapor build
  • vapor xcode
  • Then you are auto prompted to open the project in xCode. Press y

You should then be able to Import Vapor and start to play.

If that does not work try, first check the Vapor status: curl -sL check.vapor.sh | bash

& then follow this article [https://stormpath.com/blog/tutorial-build-first-swift-web-app-vapor] word for word. You have to match his swift development snapshot version, otherwise it won't work.

Question:

I want to create login and logout methods and routes. I've done already basic authentication but now I'm stuck how to continue. How should I do that, should I use sessions?

I'm using Vapor 3, Swift 4 and PostgreSQL and followed this tutorial https://medium.com/rocket-fuel/basic-authentication-with-vapor-3-c074376256c3. I'm total newbie so I appreciate a lot if you can help me!

my User model

struct User : Content, PostgreSQLModel, Parameters {

var id : Int?
private(set) var email: String
private(set) var password: String

}

extension User: BasicAuthenticatable {

static let usernameKey: WritableKeyPath<User, String> = \.email
static let passwordKey: WritableKeyPath<User, String> = \.password

}

UserController.swift, registering user.

private extension UserController {

func registerUser(_ request: Request, newUser: User) throws -> Future<HTTPResponseStatus> {

    return try User.query(on: request).filter(\.email == newUser.email).first().flatMap { existingUser in

    guard existingUser == nil else {
            throw Abort(.badRequest, reason: "a user with this email already exists" , identifier: nil)
        }

        let digest = try request.make(BCryptDigest.self)
        let hashedPassword = try digest.hash(newUser.password)
        let persistedUser = User(id: nil, email: newUser.email, password: hashedPassword)

    return persistedUser.save(on: request).transform(to: .created)
  }
 }
}

Answer:

So in Basic authentication there is no 'logout' per se as there's no login. With HTTP Basic Auth you transmit the user's credentials with each request and validate those credentials with each request.

You mention sessions, but first it's important to know what type of service you are providing? Are you providing an API or a website? They are different use cases and have different (usually) methods for authentication and login.

For an API you can use Basic Authentication and generally in your login function you exchange the credentials for some sort of token. Clients then provide that token with future requests to authenticate the user. To log out you simply destroy the token in the backend so it is no longer valid.

For a website, things are a little different since you can't manipulate the requests like you can with a normal client (such as setting the Authorization header in the request). HTTP Basic authentication is possible in a website, though rarely used these days. What traditionally happens is you submit the user's credentials through a web form, authenticate them and then save the authenticated user in a session and provide a session cookie back to the browser. This authenticates the user in future requests. To log a user out you just remove the user from the session.

Vapor's Auth package provides everything you need to do both of these scenarios. See https://github.com/raywenderlich/vapor-til for examples of both

Question:

Can somebody tell me what I need to use to make user data private that only authenticated user can see their own data. What should I implement to my code ?

I have already basic authentication and I have authorized users but the problem is that all data is available to all authenticated users who have logged in. I'm using PostgreSQL and Vapor 3 and a newbie with backend.

Thanks!


Answer:

If you have implemented basic authorisation and used a standard Vapor User model, then you will have a unique id for each authorised user. You then just need to add this as a foreign key to each model containing the data you wish to protect. When the user answers the questions, make sure you save the User Id for the current user into the foreign key fields. Then, when you wish to retrieve a user's private data, you just select those records that match the same User Id.

To create the table in the database, put this in the model:

extension ExampleModel: Migration
{
    public static func prepare(on connection: MySQLConnection) -> Future<Void> {
        return Database.create(self, on: connection) { builder in
            try addProperties(to: builder)
            builder.reference(from: \.userId, to: \User.id)
        }
    }
}

When I create a new (private) record, I pass the current user's Id as a parameter to the init function, thus:

init(user:User) throws
{
    createdByUserId = user.id!
    // initialise the remaining fields as well
}

To restrict records in a route to just those created by a user, I use:

events.get("/")
{
    req -> Future<View> in
    let query = try Event.query()
    query.filter(\.userID == theUserId )            
    // the rest of your route
}