I recently joined Fiber in April 2020, after looking for a fast and reliable web framework to build my applications. I first started by translating Fiber's documentation to my native language, Dutch, which helped me understood the framework much better, and after my first Pull Request got merged, it helped other Dutch users of the Fiber framework to read Fiber's documentation in their native language as well!

Since my initial contribution to Fiber, things went skyrocket! I was asked to join Fiber's core maintainers team along with József and Vic which are both doing an excellent job maintaining Fiber's source code along with its community. Joe and Vic both have fantastic projects in their public repositories, which I recommend checking out.

Click here if you want to skip staight ahead to the examples of my implementation of web-based authentication.

Session middleware

My second and slightly more significant contribution was to repair and release a new version of the session middleware together with Fenny, Fiber's author. Fenny has built Fiber with the ease of use and simplicity of Express in mind. Express is a popular web application framework based on Node.js. Express has a well-thought-out structure, which makes developing an application based on Express an excellent and robust feeling to work with.

With the same stream of thoughts, Fiber would like to continue its success with the Go programming language, built as a structured abstraction layer on top of fasthttp, one of the fastest HTTP packages according to TechEmpower Framework Benchmarks.

My contributions within Fiber are usually created during the calls with Fenny. Fenny is a very nice, coincidentally also a Dutch person, who is very passionate to make Fiber just perfect and to improve and expand (in a reasonable sense) the framework where possible; in short, Fenny is awesome!

Writing the new session middleware and Fenny has gone through several stages, including experimenting and using proprietary methods to support multiple session providers, to the point that we both spent several hours skewing at a missing double quote. Yep, we've all been there!

The current version of the session middleware, after some experimentation, uses the session implementation of fasthttp with an abstraction layer which makes it possible to write own session providers when the providers available out-of-the-box are not sufficient. The available session providers are Memcached, MySQL, PostgreSQL, Redis, SQLite3, and, by default, the well-known and trusted RAM!

Password hashing

After completing the valued task of repairing and improving the session middleware, I decided to delve deeper into the necessities of implementing web-based authentication in a modern and secure way.

From here, I started to delve into the authentication lifecycle of other web frameworks such as Django and Laravel. While I was studying these frameworks, I noticed one thing; any modern web framework uses password hashing to store passwords in a centralized way. This method makes it possible to compare the same hash with a so-called hash function, for example, during a login attempt. While hash functions are already a whole study on itself, I have made an illustration that briefly visualizes the operation of a hash function.

Simplified working principle of a hash function

As you can see, a hash function converts plain text to a humanly unreadable series of letters, numbers, and symbols. Please note that a hash function, unless poorly designed, can only convert the plain text to a hash and not the other way around. This makes storing passwords using a hash the modern best practice when using authentication systems.

For the development of my hashing library for Fiber, I added two hash functions as drivers; bcrypt and argon2id. I am very proud of implementing argon2id, a hash function that I would like to recommend as a very secure option. There have been many papers studying Argon2's security proofs, and many more exploring potential ways to accelerate the algorithm on dedicated hardware. Despite Argon2's recent success stories, I also chose to implement bcrypt as a hash function because it is still widely used.

Please check out the source-code of my hashing library for Fiber, give it a star if you fancy and raise any issues you find along the way.

Authentication development

Then it was time for the final step in this process, making a proof-of-concept for web-based authentication. First of all, I added my newly created password hashing repository along with Fiber and its session middleware to my project using the code example below.

import (
    "github.com/thomasvvugt/fiber-hashing"
    "github.com/gofiber/fiber"
    "github.com/gofiber/session"
)

Because I mainly use Go Modules for my projects, running the following command takes care of all my dependency requirements. The command grabs the latest releases of all dependencies and updates existing ones.

go mod tidy

Next, I added a login view with an HTML Form that posts a request containing a username and password to the PostLoginForm() function in an AuthController. You can check out a fully working example in my boilerplate repository, but I wanted to share my newly login function below.

func PostLoginForm(c *fiber.Ctx) {
	username := c.FormValue("username")
	// Find user
	user, err := FindUserByUsername(username)
	if err != nil {
		log.Fatalf("Error when finding user: %v", err)
	}
	// Check if password matches hash
	if providers.HashProvider() != nil {
		password := c.FormValue("password")
		match, err := providers.HashProvider().MatchHash(password, user.Password)
		if err != nil {
			log.Fatalf("Error when matching hash for password: %v", err)
		}
		if match {
			store := providers.SessionProvider().Get(c)
			defer store.Save()
			// Set the user ID in the session store
			store.Set("userid", user.ID)
			fmt.Printf("User set in session store with ID: %v\n", user.ID)
			c.Send("You should be logged in successfully!")
		} else {
			c.Send("The entered details do not match our records.")
		}
	} else {
		panic("Hash provider was not set")
	}
}

This function generates a hash from the user's entered password and tries to match it with the one stored in the database. I use GORM as an ORM library to manage database connections and also a User model. And finally, the user's ID is stored in the user's session. By calling store.Get("userid") on a session store we can retrieve the ID of a user that is logged in.

And that's all! I highly recommend checking out my boilerplate repository for more detailed information about my implementation to web-based authentication, star the repository if you liked what you've just seen or raise an issue if you stumble upon one. Thank you for reading!

Post image by Tomislav Jakupec