Oh, auth! While signing users in and deciding what resources they can access is crucial, implementing authentication and authorization (which are the two terms that hide under the "auth" umbrella) is often a major bump in the road that is the process of developing software.

As a developer, you can probably attest to the fact that implementing auth eats up a lot of precious time. Ever wondered why that is? In most cases, the main culprits are information overload and the complicated nature of the topic.

What's the best auth setup to implement? What is the most secure approach? Are JWT tokens the way to go? What about OAuth2? Cookies? The list of questions goes on and on.

Coding for great user experience in different stacks is tricky enough on its own and throwing auth into the mix makes things even harder.

Although we cannot write code or implement auth for you, we can do something just as impactful and (hopefully) helpful - educate you on authorization and authentication! With a better understanding of the basic terminology and important concepts, you'll be able to focus on fleshing out your application's business logic.

Ready to step up your auth game? Let's start with the basics.

Terminology

It's hard to talk about auth without understanding the basic concepts and terminology. As our first order of business, let's make sure that we're all on the same page.

tip

If you've got the basics covered and want to focus on the tech, skip to the The Modern Stack section.

AuthN & AuthZ, a.k.a. Authorization & Authentication

As mentioned in the opening paragraph, "auth" is an umbrella name used when talking about authorization and authentication.

When the communication calls for a distinction between these processes, their names are often shortened to:

  • AuthN for authentication
  • AuthZ for authorization

Although the names and the way they're abbreviated are very similar, AuthZ and AuthN are not that hard to distinguish once you realize that they apply to two different (albeit inherently connected) ideas:

Authentication is the process of verifying that entities (individuals, devices, or programs) are what they say they are.

Authorization is the process of verifying if that entity is allowed to access resources or perform operations on specific resources.

To put it simply, authentication is all about performing login/registration in your application and the management of the identity of your user. Once your user is logged in and starts interacting with resources, the system performs authorization to see if the identity can view, edit, or remove the selected resource.

Think about giving someone access to a spreadsheet in Google docs. You choose whom you want to allow to acces the document and whether that person can edit the cells or just view them. To start working on it, the collaborator must first authenticate by signing in to their account. Upon accessing the file, the system checks what kind of access has been given to that user and authorizes them to edit or view the file.

Makes sense, right? If you want to explore this topic further, we highly recommend reading these Stack Overflow threads:

Sessions

Now that you understand authentication better, let's take a closer look at how it works. The following diagram shows a typical authentication scenario: the user signs in by providing their credentials to "Project X" which validates them with the database.

Simple Authentication

How do you ensure a great user experience and don't ask users to re-enter their credentials every time they navigate to a different page or perform a different action?

This is where sessions come in handy. You probably heard this term before, but do you know what role it plays in the auth landscape? When you're using sessions, it means that you created a mechanism that stores information about the user in the application. In terms of authentication, sessions store information about a successful login which is a great way to prove that they are who they say they are, without the need to re-login for every subsequent action in the app.

What are sessions in technical terms? How's the data stored? Everything depends on the technology you use and the specific use case, but sessions can be represented in many different forms and formats, such as URL query params, cookies, browser local storage, or tokens.

The Modern Stack

In the current software climate, the expectation for every application is that it's going to support as many device types (Android, iOS, Web, Desktop) as possible while using a single codebase/framework/programming language. This makes adding features and maintaining the software much easier and helps increase user adoption.

As a result, nowadays devs deal with a whole bunch of frameworks which creates a lot of abstraction. Though frameworks are great and save us a lot of time, it can turn out that they don't support the underlying requirements of the feature they were chosen for.

This can be the case with auth, as some technologies aren't clear on how authentication works with them and how one's supposed to implement auth without completely butchering the user experience.

To get a better understanding of implementing authentication, let's look at the different elements at play when working with certain technologies.

Single-page Applications (SPA)

Single-page applications run completely in the user's browser and don't rely on any interaction with the server to maintain the state of your data. Main technologies used to create SPAs include HTML, CSS, and JavaScript. Some popular frameworks include React, Angular, and VueJs.

SPA Diagram

Server-side Rendering Applications (SSR)

Server-side rendered applications aim to improve load times to guarantee a snappy user experience. They do so by rendering pages on the server before they reach the client which takes the burden of downloading JS assets off the browser's shoulders. Some popular frameworks used for SSR include NextJs and NuxtJs.

SSR Diagram

Native Applications

Applications that run on Android, iOS, desktop or servers are all forms of native applications. These applications usually provide native performance and utilize the operating system to render the user interface (UI).

Examples of frameworks that allow for native application development are Android, iOS and .NET.

Command Line Interface (CLI) applications executed using a terminal or command line on the operating system also fall into the category of native applications. They can be built with such languages as Go, C++, Python, or NodeJs.

Native Diagram

The Problem

As you can see, modern developers can choose from a plethora of approaches, architectures, and technologies when creating their applications. What about authentication in that context?

Every approach and technology requires a unique, specialized approach that changes depending on the application and the infrastructure it runs on.

Digging deeper into the app classification established in the previous section, let's see how authentication can be handled in applications depending on their runtime environment: browser, server-side, and native.

Browsers

When it comes to browser-rendered apps, a lot's been said about implementing authentication. This leads to many different (often conflicting) views on what's the best solution and approach for handling sessions, which include:

  • JWT tokens
  • OAuth2 access tokens
  • Cookies

To choose the best approach for browser-rendered apps, we must explore the different available session storage mechanisms, their limitations, and advantages.

Storage TypeCookie (httpOnly)sessionStoragelocalStorageWeb Workers
Persists
Origin Scoped
Accessible to JavaScript

To better understand the drawbacks of each of these storage types, let's use a hypothetical user who interacts with the app. We shall call her Alice.

Alice visits example.com and gets a token with session data stored in her browser. When it comes to any of the storage types, we can see that all of them are scoped to the origin, meaning the data stored on example.com can only be extracted on example.com.

With the token saved in localStorage and sessionStorage, any rogue JavaScript on example.com can extract that information and use the token somewhere else to impersonate her.

The third mechanism is cookies - note the httpOnly attribute - which gives you data-scoping, data persistence (until the set expiry time), but no JS access.

Web Workers is JavaScript code that runs in the background, independently of other scripts, without affecting the performance of the page. Storing the session data there looks like a perfect solution since no limitations of the other 3 mechanisms apply: the data is scoped, persistent, and JS accessible.

So I guess it's settled then? Web Workers are the way to go, right? 🤔

The answer isn't so simple. Quoting from the OWASP Session Management Cheat sheet we can see that access to secrets (session values) complicates things a bit:

The advantage of a Web Worker implementation compared to an HttpOnly cookie is that a Web Worker allows for some isolated JavaScript code to access the secret; an HttpOnly cookie is not accessible to any JavaScript. If the frontend JavaScript code requires access to the secret, the Web Worker implementation is the only browser storage option that preserves the secret confidentiality.

We can consider the requirement to access session values by JavaScript a niche case, but it still has some major implications. It makes you responsible for making sure the secret is not leaked, either by careless coding or introducing bugs, just like with localStorage and sessionStorage.

Naturally, you don't want this responsibility to be yours. Using cookies is a great alternative in this situation as the responsibility for keeping secrets secure is transferred over to the browser.

What's great about cookies is that they provide all the features we want and need for session storage minus the headache of managing them as they're browser-managed. This also decreases the risk of a security vulnerability leaking the session. They persist after tabs or windows close (until their expiry date) and are scoped to the origin. We can even ensure they are not transmitted over unsecured channels by using the Secure attribute.

Server-Side and Native Applications

Server-side and native applications have their own set of problems, however, they are usually easier in storing sensitive information since the environment they run in is by definition more secure. This of course does rely on your security practices, such as who has access to your servers.

But - for argument’s sake - let's assume that the environment the application works in is secure. In such a case, tokens can easily be issued to a server and kept in memory. There's no need to store them on disk since, hopefully, your server does not restart every couple of hours. 😉

Mobile device running Android and iOS each have their own methodologies for storing tokens, but it all comes down to the same concept, as these systems inherently do not share storage with other apps on the device (which is not guaranteed on rooted/jailbroken devices, though).

Conclusion

As it's usually the case with complex problems, there's no one-size-fits-all security solution that's suitable for use in every scenario.

It's always a good idea to rely on good, factual resources such as OWASP to learn how to better understand and avoid security pitfalls.

When making the choice, you should evaluate all the pros and cons with your specific requirements in mind, rather than make choices based on opinions or conventions. You should treat articles like this one as a means to learn more about the approaches you know and to stay up to date with the latest developments in the field.

At Ory, we decided that Kratos should use cookies (httpOnly) when using browsers, and tokens when using native applications. As you can see, we mixed and matched to make sure we get exactly what we need.

No matter what approach you choose, keep your cookies crisp and your apps secure. Happy hacking!


tip

If you want to know more about cookies and their attributes, I would strongly suggest checking out OWASP Testing Cookies.

To understand more about the vulnerabilities associated with JavaScript and Cross Site Scripting (XSS) please refer to the OWASP XSS document.

Never miss an article - Subscribe to our newsletter!