DEV Community

Cover image for Authorization & Authentication from backend perspective pt1
Andrii Bui
Andrii Bui

Posted on • Updated on

Authorization & Authentication from backend perspective pt1

Introduction

I keep doing my tech blog here: https://andreyka26.com/

To be honest I planned to write this article about a year ago, because for a long time the authentication & authorization process was for me kind of not so clear. I didn’t find any book or article which in simple words can show the whole picture of that process, especially digging into details.

So in that article we are covering:

  • What is auth
  • What types of auth could be
  • As one of the types we will consider Basic and Digest(with our samples in .NET).
  • In the next part of this article we will take a look at OAuth and OpenId Connect.

This article should be useful for people who managed to get the RFC but didn’t get completely OAuth flow. I am here to help you, because I was in the same situation.
Note: I am using official info like RFC or the documentation provided by specific tools, so you can check everything by yourself if you want to.

Git repository with samples: https://github.com/andreyka26-git/dot-net-samples/tree/main/AuthorizationSample

Definitions

Prior to starting to talk about different approaches to authentication and authorization, we should consider the definitions.

1. Authorization & Authentication
From Wikipedia: “Authorization is the function of specifying access rights/privileges to resources, which is related to general information security and computer security, and to access control in particular”.

From MSDN: “Authorization is the process of determining whether a user has access to a resource. Authentication is the process of determining a user's identity”.

From Martin Fowler’s blog: “Authorization defines whether a user is allowed to do something.
Authentication confirms that the users are who they claim to be”.

You could pick up whatever definition you like, they are basically the same.
I unite Authorization & Authentication to “auth” because most usually (and in examples we’ll see that) - the authorization is impossible without authentication. If you don’t know who the User is - you cannot say what the User is allowed to do via Client.

2. API or Resource Server

In terms of OAuthit is named “Resource Server”, but for simplicity let me put it in the following way: it is the Server (just a piece of software running on some machine) that handles requests from the Client and sometimes needs the Client to be authorized to get the correct response.
Why sometimes? Because API may contain a public endpoint which doesn’t need auth at all. If all endpoints in the API are public (can be accessed without authorization) - there is no need to use auth. That’s why we will consider all endpoints on the API as protected ones.

3. Client
The Client is basically a piece of software which talks to the API and needs to authorize requests (when the endpoints are not public). Most usually this software runs on some device (mobile, desktop, and browser) and User is interacting with the API using Client.

4. User
In terms of OAuth the resource owner - is most usually a person who would like to use the Client and the application itself.

5. Authorization Server
This Server is fully responsible for identity of the user, authentication, and creating tokens. This enables you just to use some verification based on signatures (on Resource Server) and to not worry about implementation details of authentication.
We will use this term in the next part for OAuth and OpenId Connect. For this part it is not possible to decouple Authorization Server from Resource Server. So I will refer to API, Resource Server, Authorization Server or just Server as to the same thing in this part.

Flow

The basic flow looks like that:

  • The User wants to interact with the application somehow, this makes the Client send and receive data from API back and forth. Since the API has protected endpoints - it requires the Client to authorize its requests, and responds with 401 Not Authorized.

  • The Client then should firstly authenticate the user - make the request to the Server in order it confirms the User is who he claims to be. It may be the same Server where the API is serving or a standalone Server.
    Most usually authentication is performed by sending the login and password (some identity) of the User to the Server which responds with a ticket (sequence of characters, typically signed or encrypted so the API can validate). This ticket can be used to access the API. Most usually this ticket is either a token or cookies (see section about OAuth and OpenId Connect). Meanwhile, for sure, the Server should have information about this user (to be able to say this login + password does exist and what he has access to).

  • The Client performs a request to the API including the authorization ticket (either token or cookies) - and then it can get the response from it.

The most common approach for implementing auth is to use auth tokens in headers. For sure there are other ways like sessions. But the main advantage of tokens is that they are stateless: you are not required to make other requests prior to the current one to be authorized. You only need to use the appropriate token.
The token is kept via request headers in the following format: “Authorization: ”.
Basically scheme means the way you can create or get your token and validate it, so let’s consider most popular auth schemes.

Auth schemes

There are many schemes that can be used to make the auth. There may be custom ones, but we will consider the most popular ones.
There are resources where you can find all commonly used schemes and RFC documentation for each of them: https://www.iana.org/assignments/http-authschemes/http-authschemes.xhtml.

Basic Scheme

RFC: https://www.rfc-editor.org/rfc/rfc7617.html
Wikipedia: https://en.wikipedia.org/wiki/Basic_access_authentication

It is the simplest auth scheme:

  1. get username and password
  2. concatenate them with “:”
  3. encode with Base64
  4. send request with Authorization header
  5. Server validates username and password internally

Using this scheme we cannot decouple Auth from Resource server. So for each request the Server should get all info from the Authorization header, authenticate the User and then authorize him as well. It means that API should know who is the User (store him or know where to get it). To be able to decouple we will consider JWT tokens later on.

Note: encoding to base64 is needed for encoding special characters, to ensure the string contains only ASCII characters it will not give you any security layer.
Since the user passes his credentials in a raw state - it is required to use one more security layer on top of HTTP application layer - TLS.

Basic Auth Server: https://github.com/andreyka26-git/dot-net-samples/tree/main/AuthorizationSample/Basic.Server

Basic Auth Client:
https://github.com/andreyka26-git/dot-net-samples/tree/main/AuthorizationSample/Basic.WebClient

Image description

BasicAuthenticationHandler first does the authentication: it extracts username and password from header (decoding base64) and ensures this user does exist (in our case it is a simple if statement, but in real case it should hash the pass and check it along with username). It does it for every request, because we used .UseAuthorization() .UseAuthentication() middleware registration methods.

But this is the Authentication part of it, we only checked that the user is who he claims to be.
After BasicAuthenticationHandler did the authentication part we will have User.Identity in each endpoint, and based on this identity we can do Authorization - to verify whether this particular user is allowed to access this endpoint or not.

Image description

However Basic Auth is so simple it brings a few problems:

  • It is not secured, you need to send your unencrypted unhashed password through the network
  • It doesn’t have any defined way of logging out and logging in the user.
  • No built in protocol for giving different permissions for different endpoints, roles, etc. Meaning, that Resource Server will not have claims (role, static, etc) which it can validate in token. This forces each Resource Server to know all user permissions and store it inside.
  • The API should know user (store it)

Digest Scheme

RFC: https://datatracker.ietf.org/doc/html/rfc7616
Wikipedia: https://en.wikipedia.org/wiki/Digest_access_authentication

First of all I should mention that this sample I rewrite from this repo, so you can see original version: https://github.com/flakey-bit/DotNetDigestAuth

It is a little bit complicated auth flow compared to Basic Auth. It doesn’t pass a password through the network, instead of this we are creating and storing some hashes.

Using this scheme we cannot decouple Auth from Resource server. So for each request the Server should get all info from the Authorization header, authenticate the User and then authorize him as well. It means that API should know who is the User (store him or know where to get it). To be able to decouple we will consider JWT tokens later on.

This flow requires you to call the endpoint 2 times if you are not authenticated. First you should receive 401 with necessary headers and the next call is done with a generated digest token (on Client side) based on those headers.

Prior to explaining the flow we should introduce some terminology:

  • Username - just login provided by user
  • Password - just password provided by user
  • Realm - area which you can access with a particular digest ticket, something similar to scope in OAuth. You might return the same realm value for some particular set of endpoints and you will have access to those endpoints with the same auth ticket.
  • Nonce (number once) - Server generated string which should be unique for all 401 responses.
  • CNonce (client nonce) - Client generated string which should be sent and verified by the Client. The Server doesn’t care about it and includes it as a response so that the Client can confirm that it is the right Server. It is used for plain-text attack mitigation (when an attacker has a plaintext and encrypted version, based on that he could reveal secrets, code blocks, etc).
  • Nc (nonce count) - the number of requests (including the current one) that the Client has sent with the nonce value in it.
  • Opaque - Server generated string which should be sent unchanged by the Client.
  • Qop (quality of protection) - defines whether hash of entity-body is added to hashes or not. It brings additional integrity. It might contain either “auth” or “auth-int” values.
  • Response - in terms of Digest auth it is a kind of signature, just a hash, based on values that will be sent to the Server to be verified.

For this auth we may use different hash algorithms, but for simplicity we will use MD5.

The flow is the following:

  • The Client performs a request to Resource Server without any Auth.

  • The Client collects realm, qop, nonce and opaque from response header.

Image description

  • The Client generates request auth token:

First Hash (A1) = MD5 (username:realm:password)

Note: we can use MD5-sess algorithm, which essentially means to use the same algorithm, but in this format:

First Hash (A1) = MD5 (MD5 (username:realm:password):nonce:cnonce)

Why would we like to do it? It allows you to not care about user password. On top of that, I think, it allows you to use a different algorithm for hashing user credentials (username:realm:password) for security purposes. And for auth we can use another algorithm with nonce and cnonce value.

Second Hash (A2) = MD5 (httpMethod:requestUrl)

Response = MD5 ({First Hash}:nonce:nonceCount:cnonce:qop:{Second Hash})

Image description

  • The Client sends the same request including Authorization header in following format: Digest username=”{username}”, realm =”{realm }”, nonce=”{nonce}”, uri=”{uri}”, qop={qop}, nc={nc}, cnonce=”{cnonce}”, response=”{response}”, opaque=”{opaque}”

Image description

  • The Server verifies the hash by using values provided by the Client (authenticate and authorize). Then it either rejects with a 4XX error, or serves the response.

Image description

There is one interesting question: How is the Server supposed to generate the first hash without knowing the password, since the Client doesn’t pass it on request?

And there are 2 main solutions:

  • we either store plain passwords (VERY VERY BAD APPROACH),
  • or we store hashed value of (username:realm:password) to be able to get the hash by username and realm. And this brings another problem. Once you want to change a hash (for example you used firstly MD5 and then some security weaknesses were detected and you want to change to SHA-256) you cannot do much.

Comparing to Basic Auth this flow fixed a lot of problems:

  • There is no plain password passed through the network
  • You can configure nonce lifetime to allow access only for some certain period of time. It is possible to log out all users by changing opaque value, this will force everybody to reauthenticate.
  • You can specify different access areas (different controllers, endpoints, etc) via realm.

However it still has some problems:

  • No built in protocol for giving different permissions for different endpoints, roles, etc. Meaning, that Resource Server will not have claims (role, static, etc) which it can validate in token. This forces each Resource Server to know all user permissions and store it inside.
  • The API should know the user (store it). You can fix it by storing a hash for a particular username and realm, but still it is some kind of knowledge of the user compared to JWT for example.

Conclusion

Basic Auth is pretty simple to implement but it has a lot of functionality and security lacks. Digest Auth is much more complicated, but solves some of the problems that Basic Auth has, but still it has some lack of functionality that regular applications would need for the Auth process.

So in the next part we will consider OAuth and OpenId Connect protocols which solved those problems.

To reach me out or to follow me:

Instagram: https://www.instagram.com/andreyka26_programmer/
Telegram: https://t.me/programming_space
Facebook: https://www.facebook.com/groups/719475658991936
LinkedIn: https://www.linkedin.com/in/andrii-bui-a55b39166/
GitHub: https://github.com/andreyka26-git

Top comments (0)