How A Login System Works

Creating accounts, authentication and authorisation

Audience

This article is aimed at engineers with at least a rudimentary understanding of HTML, HTTP, client/ server interactions, and a bit of Nodejs. It is a high level introduction to a website login, when using a laptop. The mechanics can change depending on the type of device you’re using (think verification codes on a phone), but those are a bit much to cover here.

Argument

We will split the article between creating an account and logging in.

Let’s say we have a form for creating a new user.

An incredibly simplified signup form

There are two fields, one to enter our username and one to enter our password. We then have a button to submit for sign up. The HTML may resemble the below.

When we hit submit we will send a POST request to our server from the client containing the new username and password. We will assume we are using TLS/SSL, which means that the message containing the username and password will be encrypted and only readable by the server.

Once our server receives the username and password we will want to store it. However, we will not want to store the password in plain text as if someone gains access to the database they will have access to all the passwords!

To mitigate this we will use two concepts, salt and hashing. On the server we will generate and store a random string to use as our salt and add it to the password pre-hashing. This means that two of the same passwords will not generate the same hash.

To demonstrate this idea we will use bcrypt to generate our hash and salt. There are other algorithms available, each with their own merits and demerits.

At this point we will have a username and password stored in our database, with a way of checking the user’s credentials if they try and log in.

Now we have a method of creating an account we can look to the login process. There are two ways we can do this, via HTTP, and using OAuth 2.0. Let’s look at HTTP first.

An example HTTP login form

The above is a form we would use for logging into our site. It functions almost exactly as our sign up form did. The HTML will be extremely similar, issuing a POST request (making sure to use SSL of course!), then once received the server will use a decrypt function (like bcrypt_decrypt in our previous example). This can then be used to compare the supplied password to the stored password, authenticating a user!

Once a user has been authenticated we may alter their cookie or session to say they are logged in. On further visits to the website we would validate their session to see if they were authenticated, and what their authorisation was.

However, if I’m honest, I probably wouldn’t trust me with your password, I’m about 99% sure it’d be fine, but who knows, programming is tricky! Also, users don’t want to have to set up separate passwords for every site, and sharing passwords over sites is a security risk.

More commonly sites will use platforms like Google or Facebook to provide their authentication for them. This means users don’t need to worry about multiple passwords, and sites can leverage the security, and types of login offered by these large providers. Enter Oauth 2.0.

An example login/ sign up form with a Google sign in button

Above is a sign up form that leverages a ‘Sign in with Google’ button. When we click the button it will open the Google sign in page, a user will sign in, and if this is the first time, they will be asked which permissions they would like to give our application.

An example of granting an application using OAuth 2.0 some permissions.

Once a user is signed in they will be authorised and redirected back to our application.

The key flow is:

  1. User goes to log in to our application.
  2. The user is redirected to our OAuth 2.0 provider.
  3. The user signs in (and is optionally asked what access they would like to provide).
  4. The OAuth 2.0 provider returns to the application with some information about the user’s authorisation.

Let’s put this into a technical context.

Sequence diagram based on article here.

We have four parties involved, a user (which for the example we can think of as a person entering their credentials in a browser), a client server (potentially a NodeJS server distributing the front end), an authorization server (owned by Google), and a resource server (let’s say it’s a Java Spring application). Our user would like to access some protected information on the resource server.

Our user hits the ‘log in’ button, our client server makes a request to the authorisation server to issue an authorisation code. This authorisation code is a one time code that signals a user has successfully authenticated.

The authorisation server issues a redirect to the user, taking them to the OAuth 2.0 provider’s login page. Once the user is logged in the authorisation code requested previously is issued to the client. The client utilises this in combination with a client Id and a client secret in order to get access and refresh tokens.

The client Id is how the OAuth 2.0 provider knows which application the user is logging into. Similarly, the secret must be held securely by the client server, as this is used to verify its identity.

There are then two tokens issued by the authorisation server. An access token, which is used to access resources and has a short expiry time, and a refresh token, which is used to request a new access token when the previous one has expired.

Although this may seem confusing, there are plenty of libraries and frameworks to help with this. A popular one for Nodejs is Passport. Let’s look at how we might use this to authenticate using Google.

The code is commented, however we can see how the theory we have learnt can be implemented!

In this article we have outlined how we might create an account, or log in, using HTTP. We have also covered the OAuth2.0 flow and how we might use that in a real world application.

Principal Software Engineer at the BBC