Tutorial: How to build Web Auth with Session

Ohw this is a small step in coding but a huge step for our skillset! At the end of this tutorial you will have a register-view, a login-view and a profile-view and you will learn how to build authentication, use session to remember a logged in user and how to secure a view so only logged in user can access it✨

You can find the result of this tutorial on github here

This tutorial is a natural follow-up of How to write Controllers. You can either go for that tutorial first and come back later or be a rebel, skip it and read on 😊

1. Create a new project
2. Generate Xcode project
3. Adjust Model: User
4. Create View: Register
5. Adjust UserController: Add Register-Routes
6. Create View: Login
7. Adjust UserController: Add Login-Routes
8. Create View: Profile
9. Adjust UserController: Add Profile-Route
10. Adjust UserController: Add Logout-Route
11. Where to go from here

We will use the outcome of the aforementioned tutorial as a template to create our new project:

Before we generate an Xcode project we would have to change the package name within Package.swift and remove one dependency that we wont need:

Now in the terminal at the root directory projectName/ execute:

It may take a bit fetching the dependency, but when done you should have a project structure like this:

Since we are going to have the Authentication library do all the work for us regarding authentication, all we have to do is let it know what the credentials for the authentication are. And so in our Models/User.swift we will extend our User by PasswordAuthenticatable and for being able to store him in a session by SessionAuthenticatable:

We have removed the username property and introduced an email property. Conforming to the PasswordAuthenticatable protocol asks us to tell where to find the usernameKey and where to find the passwordKey and really a usernameKey could be an email, an actual username or even a phone number if you want your user to register with that instead 😊

Nice! Let’s implement a view with a form that has an input for an email and one for a password! First we’ll rename our userview.leaf to register.leaf within Resources/Views/ and then adjust the content to the following:

NOTE: The name attribute in our input-tags are the keys that are mapped to the value of the input and which must be named the same as the property of our User class that we will use in our controller function. That way we can use codable to create an instance of that user holding these values. More in a second 🤓

Next we need to implement a route that would return our register view. But first let’s remove all functions inside our Controllers/UserController.swift:

Now we’re good to go to implement a route that returns our register view:

Let’s not forget to remove/add our routes in routes.swift:

If you now cmd+r or run everything should built without any error and we should see our nice register-form when opening /register in our browser 😊!

Note: make sure to select Run as a scheme next to your button before running the app

To finish our registration all that’s left is the route that handles our register-form. So within our Controllers/UserController.swift add:

Okay so we are using codable to create an instance of user with the data that we’re sending with the form. Next is we’re making sure the email is unique by searching for a user that has the same email. If we have a result we know that email isn’t unique so we redirect back to the register screen. Otherwise we are continuing and override the password property with the password but hashed and then attempt to save the user and then return to the login screen.

Now finally in our routes within routes.swift add:

You could now cmd+r to run the project, go to /register and try to register yourself with an email. It should redirect you to /login that doesn’t exist, yet. Just go back to /register and try to register with that same email again, it will redirect you back to the register view! Means everything works perfectly 🙌🏻

Okay so in order to log in we’ll need a view with a form that has an input for an email and one for a password. So within Resources/Views/ create a new file and name it login.leaf and add:

NOTE: Select the login.leaf file and go to Editor>Syntax Coloring>HTML 😉

The first route for returning our login view is almost too simple. Add the following within our Controllers/UserController.swift:

And then go to our routes.swift and add our new route:

If you would now cmd+r and register yourself under /register you’ll be successfully redirected to our new shiny login view 😊!

The next route will take a little more. We need a route that would handle the login-values from our login-form. With handle I mean to receive, authenticate and persist the user in a session. So first in routes.swift add:

Second go to our configure.swift and add the following:

Let me explain for short what we did here. As mentioned we want to be able to persist our User within a session. It’s super nice we have a Middleware that do the work for us. In order to have persistence working we only need a few things. First: we need to conform our User to SessionAuthenticatable which we did in Step 3of this tutorial. Second: we initialize and create a Router with authSessionsMiddleware. Then we define a route that should have all the functionalities of the middleware exposed to it. In configure.swift we are registering our AuthenticationProvider, a SessionMiddleware and finally configure where to persist the session: in our memory (it also means all session data will be purged once we restart the application).

That’s why it’s safe to say within our yet to be implemented login function we can authenticate and persist a user. Let’s do it!

Within our Controllers/UserController.swift add the following:

Ohw I love how easy it actually is! So we are using a static function to verify that the send credentials are right. Since we hashed the password of the user with BCryptDigest in the register-function we pass it to the authentication so it is able to verify the password. If the user is not passing guard we will redirect to the login view. Otherwise we are authenticating (this is the part we are persisting the user to the session) the user to the session and redirect then to a yet not existing route /profile.

Let’s create a profile that is only accessible to a logged in user!

First let’s create a new file within Resources/Views/ and call it profile.leaf:

We will make the profile view only accessible for an authenticated user. Therefor within our routes.swift add the following:

Again we have an awesome Middleware that does the authentication work for us. If you look into the RedirectMiddleware it says “Basic middleware to redirect unauthenticated requests to the supplied path”. Well that’s exactly what we want! So all it needs to know is the class we want it to check the authentication for and also pass in the route to redirect to if unauthenticated.

All routes created with that router are secured now and can only be accessed by users that have logged in before. Let’s finish up the profile view and got into our Controllers/UserController.swift and add:

What if I tell you that if you go and cmd+r to run your project now and try to access /profile you will redirect you to the login view? And that if you go and register yourself and then login that you will be redirected to /profile and then be able to access this secured view? Yes, it’s that mind-blowingly easy!

Ready for a two liner? Within Controllers/UserController.swift add:

Next go to routes.swift and add:

And finally add a button to our Resources/Views/profile.leaf that fires it:

Our final cmd+r or run and refresh of our site and that’s it! You can try to access /profile it wont work, but if you register and login yourself you can access it and from there also logout again!

That’s it! You successfully implemented web auth using session 🎉🚀✨

You can find a list of all tutorials with example projects on Github here:
👉🏻 https://github.com/vaporberlin/vaporschool

I'm an always optimistic, open minded and knowledge seeking fullstack developer passionate about UI/UX and changing things for the better :)