Backend Design— Software Pattern For Authentication & Authorization

Micromike
Geek Culture
Published in
5 min readOct 6, 2021

This post will share some of my real-world review experiences and what software pattern is suitable for authn/authz checking logic.

Authentication & Authorization

Authentication and authorization play a very important role when implementing a backend service.

Before we discuss today’s main topic, let’s try to answer the following question.

What is the difference between authentication and authorization?

Authn (Authentication)

A mechanism that will check whether you are you.

For instance, when you try to log in to Medium, you will provide a username and password combination to the server. The username/password is a very simple authn mechanism to prove your identity.

Authz (Authorization)

A mechanism that will check whether you can access a certain operation or resources.

Let’s continue using Medium as an example.

You have logged in to Medium, and you are trying to delete my Medium post. If the Medium server allows this operation, this may quickly become a very serious security issue.

Luckily, the developers in Medium understand the concept of authorization and the server will try to block this action due to the reason that you do not allow to perform this operation.

Ok, we have a basic understanding between authentication & authorization. Let’s continue with today’s topic software pattern for authz/authn logic.

Take 1 — Naive Implementation

Now we know how important authn & authz are and try to implement the related checking logic into our services.

The very first approach is to write the checking logic inside your controller.

Let’s use an actix-web view.rs for example.

The code integrates the auth check logic into an existing service and is pretty straightforward,

  1. Get token from cookie header
  2. Check and validate the JWT token and get the user id and role
  3. Check the token is in blacklist token or not
  4. Check whether the role is Admin
  5. Otherwise, we return 401 Unauthorized

The above approach surely can do what it is meant to do, but some problems will bring future maintainability issues.

Take 2 — Spaghetti Code & Loose Coupling

As an experienced software engineer, one can quickly spot that the previous approach has a design defect: copy & paste programming pattern and spaghetti code.

Spaghetti Code

Spaghetti code is a pejorative phrase for unstructured and difficult-to-maintain source code.
From Wikipedia

Image from https://unsplash.com/photos/1ixfp1DDRA4

Image, you have to add a new API endpoint with proper auth checking, we have to copy/paste the above code into the other API endpoint.

After a while, we found out that the original auth checking logic has some defects and needs to be modified, the developer who is in charge of this task may need to modify multiple places.

And after a few months or a few years, the number of API endpoint become tons of thousands, the code will soon become unmaintainable and the future maintainer has no choice but to refactor the whole code.

A quick summary from the above example

(1) the controller business logic and auth checking logic are coupling together

(2) the same logic is copy and paste into many places and thus not easy to resolve the issue of that logic block

Loose Coupling

Components are weakly associated (have breakable relationship) with each other, and so, changes in one component least affect existence or performance of another component.

From wikipedia

To make the logic cleaner and better, one can separate the auth checking logic into a separate function and put it into something like app:utils Module that I mentioned in my previous post.

A better solution is to applying the decorator pattern to decorate the original controller function with the auth decorator.

Request Extractor

A more actix-web friendly solution will be utilizing the Request Extractor.

One can implement a struct that implements the FromRequest trait, and from that trait, perform the auth checking logic.

The actix-web identity plugin is a very good example to demonstrate this kind of pattern.

The controller implementation can use something like the following code snippet to perform the auth checking logic.

Take 3 — Take Security Best Practice Into Design

The Request Extractor approach is actually quite good from a software engineer's perspective, but from the security engineer's point of view, it may not be enough.

Security Anti Pattern— Default Allow

The Request Extractor approaches require the developer to explicitly decorates the controller function otherwise the controller will be an unauth endpoint.

This kind of approach is called default allow.

Default allow assume every endpoint can be access by anyone unless someone explicit forbid it.

In utopia, every software developer has the sense to write a proper auth checking logic; However, the crucial reality is on the opposite side.

Not every software engineer is security-aware. Moreover, even they have aware of the topic, humans tend to make mistakes.

Security Best Practice — Default Deny

As a result, from my security review experience, it is always better to use the default deny strategy.

Default deny assume every endpoint require auth unless that someone explicit allow it.

So our goal is to find a pattern that will force auth checking by default for every endpoint to prevent possible mistakes mentioned above.

Middleware Pattern

To me, I think middleware would be a very nice fit in this kind of situation.

A middleware is a software layer that will be called when a backend server receives any request and moreover, the middleware will be called before the controller logic.
We can put our auth checking logic into Middleware and hence all the endpoints will be forced to run the checking mechanism.

Thankfully, actix-web provides a very simple way to integrate middleware into the whole service implementation.

The following code snippet shows the idea of the middleware pattern. The code snippet will perform authn_authz_check before handle the remaining business logic.

Within the authn_authz_check, what we need is to do maintain a table explicitly define which endpoint does not require auth at all.

Final Words

In this post, I have briefly described some software patterns for authn/authz checking based on my security code review experienced.

Let’s quickly summarized some of the crucial topics,

Loose Coupling

Auth checking logic should be highly decoupled from business logic to avoid copy and paste programming and spaghetti code to improve future maintainability.

Default Deny Is Better Than Default Allow

Humans tend to make mistakes and not all software engineers put auth n/authz into design consideration.
As a result, use the default deny strategy instead of default allow to further decrease the future security concern.

Future topic

I only cover the software design pattern for a monolithic backend implementation. I did not talk in any detail about how to implement authentication and authorization.

Moreover, the above software pattern may only be suitable for monolithic service implementation and may not be suitable for modern web service infrastructure design.

These would be some great topics for my future posts so stay tuned.

--

--

Micromike
Geek Culture

Senior product developer in Synology. Programmer, #infosec enthusiasm, #linux, #python #Rust