09 Using Oauth 2.0 in Practice to Implement an Open ID Connect User Authentication Protocol

09 Using OAuth 2 #

Hello, I am Wang Xindong.

If you are a third-party software developer, when implementing user authentication logic, besides allowing users to create a new account and log in, you can also integrate with platforms such as WeChat and Weibo, allowing users to log in with their WeChat or Weibo accounts. At the same time, if your application has multiple sub-applications, you can enable users to log in only once and access all the sub-applications, thus improving the user experience.

This is where federated login and single sign-on come into play. If we delve deeper, we will realize that they are both implementations of the OpenID Connect protocol (abbreviated as OIDC). But what exactly is OIDC?

Today, let’s learn about the relationship between OIDC and OAuth 2.0, and how to use OAuth 2.0 to implement an OIDC user authentication protocol.

What is OIDC? #

OIDC is an open standard for user authentication. The scenario of using a WeChat account to log in to Geek Time is an implementation of this open standard.

You may ask, “Wait a minute, isn’t WeChat login for third-party apps using the OAuth 2.0 protocol? Why are we talking about OIDC?”

You’re right, WeChat login for third-party apps does indeed use OAuth 2.0. However, OAuth 2.0 is an authorization protocol, not an authentication protocol. OIDC is the authentication protocol, and it is an interoperable protocol for user authentication built on top of OAuth 2.0. In other words, OIDC is an identity authentication framework protocol directly built on OAuth 2.0.

To put it another way, OIDC = Authorization Protocol + Authentication, and it is a superset of OAuth 2.0. For better understanding, we can think of OAuth 2.0 as flour and OIDC as bread. Now, do you understand their relationship? So, it is correct to say that “a third-party app uses OAuth 2.0 for WeChat login” and even more correct to say “OIDC is used.”

Considering that single sign-on and federated login both follow the standard OIDC flow, today we will discuss how to use OAuth 2.0 to implement OIDC and take a “big picture” view of the problem. Once we grasp this, scenarios such as single sign-on, federated login, and many other identity authentication scenarios will no longer be a problem.

Correspondence between OIDC and OAuth 2.0 Roles #

When it comes to “how to use OAuth 2.0 to build authentication protocols like OIDC,” we can think of one starting point, which is the four roles in OAuth 2.0.

The operation of the OAuth 2.0 authorization code flow requires smooth communication and coordination among the four roles: resource owner, third-party software, authorization server, and protected resource. If we want to build OIDC on the basis of the OAuth 2.0 authorization code grant type, these four roles still need to continue to “play their value.” So, how do these four roles correspond to the participants in OIDC?

So, let’s first think about a protocol framework for identity authentication and consider the roles that should exist. You may already have an idea: it needs an end user who logs in to third-party software, a third-party software, and an authentication service that provides identity proofing and validation for the user.

That’s right, these are the three main roles in OIDC. In the official standard framework of OIDC, these three roles are named as:

  • EU (End User), representing the end user.
  • RP (Relying Party), representing the relying party of the authentication service, which is the third-party software mentioned above.
  • OP (OpenID Provider), representing the provider of identity authentication services.

EU, RP, and OP are essential for OIDC, and I will also use the abbreviations later, so I hope you can remember them first.

Nowadays, many apps have integrated WeChat login, so WeChat login is a big identity authentication service (OP). Once we have a WeChat account, we can log in to all apps that have integrated the WeChat login system (RP). This is what we often call federated login.

Now, let’s use the example from GeekTime to see the correspondence between the four roles in OAuth 2.0 and the three roles in OIDC:

Figure 1 Correspondence between OAuth 2.0 and OIDC roles.

Key Differences between OIDC and OAuth 2.0 #

Looking at this role correspondence diagram, do you have a “sudden realization” feeling: to implement an OIDC protocol, isn’t it just implementing an OAuth 2.0 protocol directly? Yes, as I mentioned at the beginning of this lecture, OIDC is an identity authentication protocol framework based on OAuth 2.0.

Let me draw an OIDC communication flow chart for you, and you will have a clearer understanding of the relationship between OIDC and OAuth 2.0:

Figure 2 OIDC Communication Flow based on Authorization Code Flow

As you can see, an OIDC protocol flow based on the authorization code flow is almost identical to the flow of the authorization code grant in OAuth 2.0, with the only difference being the addition of an ID_TOKEN, which we refer to as the ID token. This token is the key to identity authentication. Therefore, I will now focus on explaining this token in detail, rather than going into the whole flow of OIDC.

ID Token Generation and Parsing in OIDC #

In step 6 of the OIDC communication flowchart in Figure 2, we can see that the ID token (ID_TOKEN) and the access token (ACCESS_TOKEN) are returned together. I will analyze why both tokens are returned later. Let’s focus on the ID token for now.

As we know, the access token does not need to be parsed by third-party software because it is opaque to them. However, the ID token needs to be parsed by third-party software because the third-party software needs to access the content inside the ID token to handle user login status logic.

So, what is the content of the ID token?

First of all, the ID token is a JWT formatted token. You can review the relevant content of JWT in Lecture 4. Here it is worth mentioning that although the JWT token is a self-contained information body that brings convenience for it to be used as an ID token, it is necessary to have the following 5 JWT claims parameters when using it as an OIDC ID token, as these attributes are necessary to identify the user and achieve the purpose of identity authentication:

  • iss: The issuer of the token, which is the URL of the Identity Provider (OP).
  • sub: The subject of the token, which is a globally unique identifier that represents the end user (EU).
  • aud: The audience of the token, which is the app_id of the third-party software (RP).
  • exp: The expiration timestamp of the token. All ID tokens have an expiration time.
  • iat: The timestamp when the token was issued.

Here is an example of code for generating the ID token:

      String id_token=genrateIdToken(appId,user);
      private String genrateIdToken(String appId,String user){
          String sharedTokenSecret="hellooauthhellooauthhellooauthhellooauth";
          Key key = new SecretKeySpec(sharedTokenSecret.getBytes(),
                  SignatureAlgorithm.HS256.getJcaName());
        
           Map<String, Object> headerMap = new HashMap<>();
          headerMap.put("typ", "JWT");
          headerMap.put("alg", "HS256");
    
          Map<String, Object> payloadMap = new HashMap<>();
          payloadMap.put("iss", "http://localhost:8081/");
          payloadMap.put("sub", user);
          payloadMap.put("aud", appId);
          payloadMap.put("exp", 1584105790703L);
          payloadMap.put("iat", 1584105948372L);
          return Jwts.builder().setHeaderParams(headerMap).setClaims(payloadMap).signWith(key,SignatureAlgorithm.HS256).compact();
      }

Next, let’s take a look at how the logic of handling user login status is handled.

First, let’s consider the case of building a user authentication login system without involving OIDC. In this case, we would need to save the user’s login session either on a remote server or in a browser cookie, and set an expiration time for the session ID.

However, when we have a structured information body like JWT, especially when it contains the token’s subject and expiration time, we have a “natural” session relationship information.

Therefore, relying on the ID token in JWT format is enough to solve the problem of login status after authentication. This is why OIDC requires the return of the ID token, as the ID token is the key to OIDC as an identity authentication protocol.

So, once we have the ID token, how should third-party software parse it? Let’s take a look at some specific code for parsing the ID token:

private Map<String,String> parseJwt(String jwt){
        String sharedTokenSecret="hellooauthhellooauthhellooauthhellooauth";
        Key key = new SecretKeySpec(sharedTokenSecret.getBytes(),
                SignatureAlgorithm.HS256.getJcaName());

        Map<String,String> map = new HashMap<String, String>();
        Jws<Claims> claimsJws = Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(jwt);
        Claims body = claimsJws.getBody();
        map.put("sub",body.getSubject());
        map.put("aud",body.getAudience());
        map.put("iss",body.getIssuer());
        map.put("exp",String.valueOf(body.getExpiration().getTime()));
        map.put("iat",String.valueOf(body.getIssuedAt().getTime()));
        return map;
    }

It’s important to note that once the third-party software has parsed and verified the legitimacy of the ID token, it does not need to save the entire JWT. It only needs to keep the PAYLOAD (data body) part of the JWT, as this part contains the necessary user identification information for authentication.

Also, when validating the legitimacy of the JWT, the key point is to do signature verification because the ID token itself has been signed with the authentication service’s (OP) key. For the specific encryption method and verification method, you can review [Chapter 4].

So, once the third-party software (RP) obtains the ID token, it has obtained the information for handling identity authentication actions, which means it has obtained the ID value that uniquely identifies the end user (EU), such as 3521.

Retrieving Information Beyond the ID Token with an Access Token #

However, in order to improve the user-friendliness of the third-party software, displaying “Hello, 3521” on the page is definitely not as good of an experience as displaying “Hello, Xiaoming”. Here, “Xiaoming” is the user’s nickname.

So how can we retrieve this nickname “Xiaoming”? It’s simple - by sending another request using the returned access_token. Of course, we are already familiar with this process, as it belongs to the flow of requesting protected resource services in the OAuth 2.0 standard flow.

This is why in the OIDC protocol, we are returned both the ID token and the access token. Under the premise of ensuring the functionality of user authentication, if we want to obtain more user information, we can do so through the access token. In the OIDC framework, this part is called creating the UserInfo endpoint and obtaining UserInfo information.

From this perspective, taking a closer look at the OIDC process is: generating an ID token -> creating the UserInfo endpoint -> parsing the ID token -> recording login status -> obtaining UserInfo.

Alright, we have finished implementing an OIDC framework using OAuth 2.0. You can check out the complete code for these processes on GitHub. Now, let me summarize for you.

The most critical method for implementing OIDC with OAuth 2.0 is to add the ID token and UserInfo endpoint based on the original OAuth 2.0 flow, in order to ensure that third-party software in OIDC can record user status and obtain user details.

Because the third-party software can record user status by parsing the key user identification information in the ID token, and can obtain more detailed user information through the UserInfo endpoint. With user status and user information, identity authentication is inherently achieved.

Next, let’s take a detailed look at how to implement Single Sign-On (SSO).

Single Sign-On #

A user G wants to log in to a third-party software A, which has three sub-applications with the domain names a1.com, a2.com, and a3.com. If A wants to provide a smoother login experience for user G, allowing them to log in to a1.com and smoothly log in to the other two domain names as well, they can create an identity authentication service to support the login for a1.com, a2.com, and a3.com.

This is what we call single sign-on, where “one login, access all”.

So, can we use the OIDC (OpenID Connect) protocol standard to implement such single sign-on? I can only say “absolutely”. As shown in the diagram below, the third-party software (RP) only needs to replicate our OIDC communication process.

Communication process of single sign-on

Figure 3 Communication process of single sign-on

You see, single sign-on is just one specific application of the OIDC framework. Once you understand the principles of the OIDC framework, implementing single sign-on becomes straightforward. For specific implementations of single sign-on, you can search for “implementing single sign-on using OIDC” on GitHub, and you will find many related open-source content.

Summary #

In some larger platforms with identity authentication services, you may not see a description of OIDC, but there’s no need to worry about it. Sometimes, we may be confused about whether the OIDC standard came first or the authentication implementation methods like WeChat login.

In fact, to understand this order, we can take design patterns as an example. When you want to design a loosely coupled and scalable system, even if you have never encountered design patterns before, through continuous modification attempts, you will gradually come up with a code architecture idea that resembles the “flavor” of design patterns. Understanding the train of thought behind OIDC in solving identity authentication issues is also the same.

Today, we implemented an OIDC process based on OAuth 2.0, and I hope you remember the following two points.

OAuth 2.0 is not an identity authentication protocol, please remember this. Identity authentication focuses on “whose problem,” while OAuth 2.0 focuses on authorization, the question of “can or cannot.” However, we can use OAuth 2.0 as a foundation and add an ID token to obtain the user’s unique identifier, thus implementing an identity authentication protocol.

Some apps do not want to hassle with designing their own registration and login authentication processes, so they seek a unified solution, which will inevitably lead to a platform that handles all similar authentication login scenarios. The opposite understanding is also valid. If there’s a platform with a massive number of users and high traffic that provides a unified login authentication service for other third-party applications to connect to, wouldn’t it solve the problem of users logging into numerous third-party apps with the same account? OIDC is the open solution for such authentication login scenarios.

By now, do you have a deeper understanding of OIDC? Alright, let’s see what thought-provoking questions I’ve left for you today.

Thought-provoking Questions #

If you were to implement an identity authentication protocol similar to OIDC using OAuth 2.0, what issues do you think you should pay attention to?

Feel free to share your thoughts in the comments section or share today’s content with other friends. Let’s exchange ideas together.