25 Authentication Mechanism How Applications Perform Access Authentication

25 Authentication Mechanism How Applications Perform Access Authentication #

Hello, I’m Kong Lingfei. Today, let’s talk about how to perform access authentication.

Ensuring the security of an application is the most basic requirement in software development. We have multiple methods to secure an application, such as network isolation, firewall settings, and IP blacklisting/whitelisting. However, in my opinion, these methods are more focused on solving security issues from an operational perspective. As developers, we can also ensure the security of an application at the software level, and this can be achieved through authentication.

In this lecture, I will use HTTP services as an example to introduce the four common authentication methods: Basic, Digest, OAuth, and Bearer. There are also many variations based on these four methods, but I won’t introduce them here.

The IAM project uses the Basic and Bearer authentication methods. In this lecture, I will first introduce these four authentication methods. In the next lecture, I will explain how the IAM project is designed and implemented for access authentication.

What is the difference between authentication and authorization? #

Before introducing the four basic authentication methods, I would like to first clarify the difference between authentication and authorization, as these are two concepts that many developers easily confuse.

  • Authentication: Used to verify whether a user has permission to access a system. If authentication is successful, the user can access the system and create, modify, delete, and query resources supported by the platform.
  • Authorization: Used to verify whether a user has permission to access a specific resource. If authorization is successful, the user can perform operations such as adding, deleting, modifying, and querying the resource.

Here, I will use the following image to help you understand the difference between the two:

In the image, we have a repository system where users James, Colin, and Aaron have created Product-A, Product-B, and Product-C, respectively. Now, user Colin logs into the repository system successfully using his username and password (authentication), but he fails to access Product-A and Product-C because he is not authorized to do so. However, he can successfully access the resource Product-B that he created (authorization succeeded). Therefore, authentication proves who you are, while authorization determines what you can do.

Now that we have introduced the difference between authentication and authorization, let’s move on to the focus of this lesson: how applications can perform access authentication.

Four Basic Authentication Methods #

There are four common authentication methods: Basic, Digest, OAuth, and Bearer. Let’s start with Basic authentication.

Basic #

Basic authentication is the simplest authentication method. It simply encodes the username:password in base64 and puts it in the HTTP Authorization Header. When the HTTP request reaches the backend service, the service parses the base64 string in the Authorization Header, decodes it to obtain the username and password, and compares them with the values recorded in the database. If there is a match, the authentication is successful. For example:

$ basic=`echo -n 'admin:Admin@2021'|base64`
$ curl -XPOST -H"Authorization: Basic ${basic}" http://127.0.0.1:8080/login

By using base64 encoding, the password can be transmitted in a non-plaintext manner, increasing security to some extent. However, base64 is not an encryption technology; an intruder can still intercept the base64 string and decode it to obtain the username and password. In addition, even if the password is encrypted in Basic authentication, an intruder can still launch a replay attack using the encrypted username and password.

Therefore, although Basic authentication is simple, it is extremely insecure. The only way to use Basic authentication is to combine it with SSL to ensure the security of the entire authentication process.

In the IAM project, Basic authentication is still used to support frontend login with usernames and passwords, but HTTPS is used for communication between the frontend and backend to ensure the security of the authentication.

It is important to note that when designing a system, a general principle should be followed: do not use plaintext passwords in request parameters or store them in any storage.

Digest #

Digest authentication is another HTTP authentication protocol that is compatible with Basic authentication but fixes the serious flaws of Basic authentication. Digest authentication has the following characteristics:

  • It never sends passwords in clear text over the network.
  • It can effectively prevent replay attacks by malicious users.
  • It can selectively prevent tampering with message content.

The process of digest authentication is shown in the following diagram:

In the diagram above, the following four steps are required to complete digest authentication:

  1. The client requests a resource from the server.
  2. Before the server can authenticate the client by confirming its identity by knowing the password, the server fails authentication and returns 401 Unauthorized along with a WWW-Authenticate header containing the required authentication information.
  3. The client uses the information in the WWW-Authenticate header to select the encryption algorithm and calculates the password digest response with the password nonce, and then makes another request to the server.
  4. The server compares the password digest provided by the client with the digest calculated internally by the server. If they match, it means that the client knows the password and the authentication is successful. The server then returns additional information related to the authorization session in the Authorization-Info field.

The information contained in the WWW-Authenticate header is as follows:

Although using digest can prevent passwords from being sent in plaintext, it only hides the password and does not guarantee that the request is secure. Because the request (including the password digest) can still be intercepted, it can be replayed to the server, causing security issues.

To prevent replay attacks, the server sends a password nonce to the client, which changes with each request. The client will generate a password digest based on the nonce. In this way, the digest changes with the random number. The password digest received by the server is only valid for a specific nonce, and without the password, the attacker cannot calculate the correct digest, which allows us to prevent replay attacks. Abstract authentication can protect passwords and is much more secure than basic authentication. However, digest authentication does not protect the content, so it still needs to be used in conjunction with HTTPS to ensure communication security.

OAuth #

OAuth (Open Authorization) is an open authorization standard that allows users to grant third-party applications access to their private resources stored in a web service (such as photos, videos, audios, etc.) without providing the username and password to the third-party application. The current version of OAuth is version 2.0.

OAuth 2.0 consists of four authorization grant types: password grant, implicit grant, client credentials grant, and authorization code grant. Now, let’s introduce each grant type in detail.

The first one, password grant. The password grant type is when a user directly provides the username and password to the third-party application, which then exchanges the username and password for a token. Therefore, this grant type can only be used when no other grant types are available, and the user highly trusts a certain application.

The authentication flow is as follows:

  1. Website A requests the username and password from the user.
  2. After the user agrees, website A exchanges the username and password for a token from website B.
  3. After verifying the user’s identity, website B grants a token to website A, which allows website A to access resources with the corresponding permissions on website B.

The second one, implicit grant. This grant type is suitable for frontend applications. The authentication flow is as follows:

  1. Website A provides a link that redirects to website B. When the user clicks on the link, they are redirected to website B and asked for authorization.
  2. After logging into website B and granting authorization, the user is redirected back to the specific redirect URL of website A, carrying the token returned by website B. The data from website B can be used by website A.

This grant type carries the risk of “man-in-the-middle attacks” and can only be used in scenarios with low security requirements. The token’s validity period should be very short.

The third one, client credentials grant. This grant type is used to request authorization in a command-line interface, suitable for command-line applications without a frontend. The authentication flow is as follows:

  1. Application A requests authorization from application B through the command line, with the secret ID and secret key previously issued by application B. The secret key should be sent in the backend for security considerations.
  2. Application B receives the secret ID and secret key, verifies the identity, and returns a token to application A.

The fourth one, authorization code grant. This grant type is when a third-party application first applies for an authorization code and then uses the authorization code to obtain a token. This grant type has higher security because the frontend transmits the authorization code and the backend stores the token, and the communication with resources is done in the backend, avoiding security issues caused by token leakage. The authentication flow is as follows:

  1. Website A provides a link to website B with a redirect URL. When the user clicks on the link, they are redirected to website B.
  2. The user carries the client ID, pre-applied with website B, and sends an identity verification request to website B.
  3. After the user logs into website B and passes the verification, granting permissions to website A, the website redirects back to the redirect URL. The authorization code granted by website B is attached to the URL.
  4. Website A uses the authorization code to request a token from website B. After verifying the authorization code, website B returns the access token.

Bearer #

Bearer authentication, also known as token authentication, is an HTTP authentication method. The core of bearer authentication is the bearer token. The bearer token is an encrypted string typically generated by the server based on a secret key. When a client requests the server, it must include Authorization: Bearer <token> in the request header. When the server receives the request, it parses out <token> and verifies its legitimacy. If the verification is successful, the authentication is passed. Just like basic authentication, bearer authentication needs to be used with HTTPS to ensure authentication security.

The most popular token encoding method currently is JSON Web Token (JWT, pronounced “jot,” see JWT RFC 7519). Next, I will explain JWT authentication to help you understand the principles of bearer authentication.

Implementation of Token Authentication Mechanism Based on JWT #

In typical business scenarios, in order to differentiate users and ensure security, API requests need to be authenticated, but it is not practical to require a login for every request. A common practice is to generate a token with a certain validity period after the first login and store it in the browser’s cookie or local storage. Subsequent requests include this token, which is then used by the server to authenticate the request. After the first login, the server stores this token using methods such as files, databases, or cache servers for future comparison in subsequent requests.

Alternatively, a simpler method can be used: issuing a token directly using a secret key. This eliminates the need for additional storage and reduces the database query pressure for each request. This method is already implemented as a standard in the industry and is known as JWT.

Next, I will introduce JWT in detail.

Introduction to JWT #

JWT is a specific implementation of Bearer Token, consisting of JSON data and generating a string using a HASH hashing algorithm. This string can be used for authorization and information exchange.

There are many advantages to using JWT Token for authentication, such as not needing to store user data on the server, reducing server load; and using JSON data format, which is more readable. In addition, using JWT Token has advantages such as cross-language compatibility and lightweight.

JWT Authentication Process #

The authentication process using JWT Token is as shown in the following diagram:

It can be divided into four steps:

  1. The client requests login with username and password.

  2. Upon receiving the request, the server will verify the username and password. If they don’t match the database records, the verification fails; if they match, the verification passes, and the server will issue a Token and return it to the client.

  3. The client receives the request and caches the Token, such as storing it in the browser’s cookie or local storage. Subsequently, each request will include this Token.

  4. Upon receiving the request, the server will verify the Token in the request. If the verification passes, it will proceed with the business logic and return the processed result.

JWT Format #

JWT consists of three parts: Header, Payload, and Signature, which are connected by dots (.), for example:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJpYW0uYXBpLm1hcm1vdGVkdS5jb20iLCJleHAiOjE2NDI4NTY2MzcsImlkZW50aXR5IjoiYWRtaW4iLCJpc3MiOiJpYW0tYXBpc2VydmVyIiwib3JpZ19pYXQiOjE2MzUwODA2MzcsInN1YiI6ImFkbWluIn0.Shw27RKENE_2MVBq7-c8OmgYdF92UmdwS8xE-Fts2FM

In JWT, each part contains the following information as shown in the diagram below:

I will now explain these three parts and the information they contain.

  1. Header

The Header of the JWT Token contains two pieces of information: the type of the Token and the encryption algorithm used by the Token.

For example:

{
  "typ": "JWT",
  "alg": "HS256"
}

Parameter explanation:

  • typ: Specifies that the type of the Token is JWT.
  • alg: Specifies the encryption algorithm used by the Token, here it is HS256 (there can be multiple options for the alg algorithm).

Here, we encode the Header using base64:

$ echo -n '{"typ":"JWT","alg":"HS256"}'|base64
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

In some scenarios, there may also be a “kid” option to identify a key ID, for example:

{
    "alg": "HS256",
    "kid": "XhbY3aCrfjdYcP1OFJRu9xcno8JzSbUIvGE2",
    "typ": "JWT"
}
  1. Payload

The specific content of the token carried in the payload consists of three parts: the claims registered in the JWT standard (optional), public claims, and private claims. Let’s take a look at each of them.

The claims registered in the JWT standard have the following standard fields:

jwt-registered-claims

The payload content in this example is:

{
  "aud": "iam.authz.marmotedu.com",
  "exp": 1604158987,
  "iat": 1604151787,
  "iss": "iamctl",
  "nbf": 1604151787
}

Let’s base64 encode the payload:

$ echo -n '{"aud":"iam.authz.marmotedu.com","exp":1604158987,"iat":1604151787,"iss":"iamctl","nbf":1604151787}'|base64
eyJhdWQiOiJpYW0uYXV0aHoubWFybW90ZWR1LmNvbSIsImV4cCI6MTYwNDE1ODk4NywiaWF0IjoxNjA0MTUxNzg3LCJpc3MiOiJpYW1jdGwiLCJuYmYiOjE2MDQxNTE3ODd9

In addition to this, there are public claims and private claims. Public claims can add any required information, generally adding user-related information or other business-related information. Be careful not to add sensitive information. Private claims are claims commonly defined by the client and the server. Since base64 is symmetrically encrypted, it is generally not recommended to store sensitive information.

  1. Signature

The signature of the token is generated as follows: base64 encode the header and payload separately, then concatenate them with a .. Next, use the encryption method declared in the header and encrypt the concatenated string using the secretKey. The encrypted string is the final signature.

The secretKey is a key that is stored on the server and is generally saved through a configuration file, like this:

secret-key

Note that the secret key must never be leaked. If the secret key is leaked, an invader can use it to issue JWT tokens and gain unauthorized access to the system.

Finally, the generated token is as follows:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJpYW0uYXV0aHoubWFybW90ZWR1LmNvbSIsImV4cCI6MTYwNDE1ODk4NywiaWF0IjoxNjA0MTUxNzg3LCJpc3MiOiJpYW1jdGwiLCJuYmYiOjE2MDQxNTE3ODd9.LjxrK9DuAwAzUD8-9v43NzWBN7HXsSLfebw92DKd1JQ

After signing, the server will return the generated token, and the client will include this token in future requests. When the server receives the token, it will parse out the header.payload, then use the same encryption algorithm and secretKey to encrypt the header.payload once again and obtain the signature. Then, compare the encrypted signature with the received signature. If they are the same, the verification passes. Otherwise, return an HTTP 401 Unauthorized error.

Lastly, I have two recommendations regarding the use of JWT:

  • Do not store sensitive information in the token.
  • Do not set the exp value in the payload too large. Generally, set it to 7 days for development versions and 2 hours for production versions. However, you can adjust it according to your needs.

Summary #

When developing Go applications, we need to ensure the security of the application through authentication. Authentication is used to verify whether a user has the permission to access the system. If the authentication is successful, the user can access the system and create, modify, delete, and query the resources supported by the platform. There are currently four commonly used authentication methods in the industry: Basic, Digest, OAuth, and Bearer. Among them, Basic and Bearer are used the most.

Basic authentication verifies the user through username and password and is mainly used in user login scenarios. Bearer authentication verifies the user through a token and is usually used in API call scenarios. Whether it is Basic authentication or Bearer authentication, it needs to be used in conjunction with HTTPS to maximize the security of the request.

Basic authentication is simple and easy to understand, but Bearer authentication has a certain level of complexity. Therefore, in the second half of this lecture, the principle of Bearer Token authentication is explained through JWT Token.

JWT Token is a relatively good implementation of Bearer authentication, mainly consisting of three parts:

  • Header: It includes the type of the token and the encryption algorithm used by the token. In some scenarios, you can also add the “kid” field to identify a key ID.
  • Payload: The payload carries the specific content of the token, composed of three parts: registered claims, public claims, and private claims, as defined in the JWT standard.
  • Signature: The signature is the signature part of the token. The program verifies the legality of the signature to determine whether the authentication is passed.

Exercise #

  1. Think about: How to solve the token revocation problem when using JWT as login credentials?
  2. Think about: Is it better to store tokens in LocalStorage or in Cookie?

Feel free to leave a message in the comment section to discuss and exchange ideas. See you in the next lecture.