I recently learned about OIDC in the authentication system and put together a blog that covers the concepts, processes, and usage of OIDC. Before introducing these, we need to clarify two terms Authorization and Authentication.

Authorization vs Authentication

Here is OKTA’s definition of the two terms.

  • Authentication, in the form of a key. The lock on the door only grants access to someone with the correct key in much the same way that a system only grants access to users who have the correct credentials.
  • Authorization, in the form of permissions. Once inside, the person has the authorization to access the kitchen and open the cupboard that holds the pet food. The person may not have permission to go into the bedroom for a quick nap.

Authentication usually refers to the process of verifying that the user is the user, while Authorization refers more to whether the user has permissions. Usually we verify that the user is the user first, and then verify that the user has permissions. The following diagram shows the difference between the two, as organized in the Auth0 article.

Authorization vs Authentication

Introduction to OAuth 2

Before we understand OIDC, we need to understand OAuth 2, because OIDC is based on the OAuth 2 process. Before that, let’s introduce the whole scenario with an example.

Suppose now we want to log in to the Slack iOS client with a Google account, what do we need to do? One easy way to do this is to give Slack our username and password so that Slack can log in as us, but this is a huge security risk, and there’s basically no good way to do this if Slack accidentally leaks the username and password, or if we want to revoke the authorization.

So we need an authorization protocol that does not give the user name and password directly to the third-party application, but introduces an intermediate layer, access_token. access_token binds the user to authorize the third-party application with certain privileges, such as reading the user name and other information, updating the user name, etc. OAuth 2 is the protocol used to do this.

OAuth 2 provides a total of four authorization modes, with a new enhanced mode for authorization code mode added in 2015. Let’s start with the simple one.

Client Credentials Grant

Client Credentials Grant

This mode is the simplest, in fact, the client tells the server which client it is, and the server sends the access_token.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
     +---------+                                  +---------------+
     |         |                                  |               |
     |         |>--(A)- Client Authentication --->| Authorization |
     | Client  |                                  |     Server    |
     |         |<--(B)---- Access Token ---------<|               |
     |         |                                  |               |
     +---------+                                  +---------------+

                     Figure 6: Client Credentials Flow

   The flow illustrated in Figure 6 includes the following steps:

   (A)  The client authenticates with the authorization server and
        requests an access token from the token endpoint.

   (B)  The authorization server authenticates the client, and if valid,
        issues an access token.

This mode is rarely used and is mainly used when the client is very reliable, for example, when logging into a hosted cloud desktop system, the system requests a token from the server and the server immediately issues it, after which the user uses it as a specific user of the cloud desktop.

Resource Owner Password Credentials Grant

Resource Owner Password Credentials Grant

User-password mode, this process is relatively suitable for trusted clients, such as self-developed clients, where the client asks the user to enter a user name and password, and then requests the server, which checks the user name and password and issues the access_token.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
     +----------+
     | Resource |
     |  Owner   |
     |          |
     +----------+
          v
          |    Resource Owner
         (A) Password Credentials
          |
          v
     +---------+                                  +---------------+
     |         |>--(B)---- Resource Owner ------->|               |
     |         |         Password Credentials     | Authorization |
     | Client  |                                  |     Server    |
     |         |<--(C)---- Access Token ---------<|               |
     |         |    (w/ Optional Refresh Token)   |               |
     +---------+                                  +---------------+

            Figure 5: Resource Owner Password Credentials Flow

   The flow illustrated in Figure 5 includes the following steps:

   (A)  The resource owner provides the client with its username and
        password.

   (B)  The client requests an access token from the authorization
        server's token endpoint by including the credentials received
        from the resource owner.  When making the request, the client
        authenticates with the authorization server.

   (C)  The authorization server authenticates the client and validates
        the resource owner credentials, and if valid, issues an access
        token.

Implicit Grant

Implicit Grant

Implicit Grant mode is suitable for places where it is not convenient to expose client_secret, such as browser side (web page), distributed apps, which are easy to be cracked, so you can use Implicit Grant.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
     +----------+
     | Resource |
     |  Owner   |
     |          |
     +----------+
          ^
          |
         (B)
     +----|-----+          Client Identifier     +---------------+
     |         -+----(A)-- & Redirection URI --->|               |
     |  User-   |                                | Authorization |
     |  Agent  -|----(B)-- User authenticates -->|     Server    |
     |          |                                |               |
     |          |<---(C)--- Redirection URI ----<|               |
     |          |          with Access Token     +---------------+
     |          |            in Fragment
     |          |                                +---------------+
     |          |----(D)--- Redirection URI ---->|   Web-Hosted  |
     |          |          without Fragment      |     Client    |
     |          |                                |    Resource   |
     |     (F)  |<---(E)------- Script ---------<|               |
     |          |                                +---------------+
     +-|--------+
       |    |
      (A)  (G) Access Token
       |    |
       ^    v
     +---------+
     |         |
     |  Client |
     |         |
     +---------+

   Note: The lines illustrating steps (A) and (B) are broken into two
   parts as they pass through the user-agent.

                       Figure 4: Implicit Grant Flow

   The flow illustrated in Figure 4 includes the following steps:

   (A)  The client initiates the flow by directing the resource owner's
        user-agent to the authorization endpoint.  The client includes
        its client identifier, requested scope, local state, and a
        redirection URI to which the authorization server will send the
        user-agent back once access is granted (or denied).

   (B)  The authorization server authenticates the resource owner (via
        the user-agent) and establishes whether the resource owner
        grants or denies the client's access request.

   (C)  Assuming the resource owner grants access, the authorization
        server redirects the user-agent back to the client using the
        redirection URI provided earlier.  The redirection URI includes
        the access token in the URI fragment.

   (D)  The user-agent follows the redirection instructions by making a
        request to the web-hosted client resource (which does not
        include the fragment per [RFC2616]).  The user-agent retains the
        fragment information locally.

   (E)  The web-hosted client resource returns a web page (typically an
        HTML document with an embedded script) capable of accessing the
        full redirection URI including the fragment retained by the
        user-agent, and extracting the access token (and other
        parameters) contained in the fragment.

   (F)  The user-agent executes the script provided by the web-hosted
        client resource locally, which extracts the access token.

   (G)  The user-agent passes the access token to the client.

In our example at the beginning, Slack pops up a webview, loads Google’s authorization page, Google displays the corresponding account, and then prompts the client to read the user’s information, and when the user clicks to confirm, Google checks the page and redirects the page to the address specified by the client, and brings the access_token in the URL fragment.

Authorization Code Grant

Authorization Code Grant

Authorization Code mode, which is the most complex and secure authorization mode, flows as follows.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
     +----------+
     | Resource |
     |   Owner  |
     |          |
     +----------+
          ^
          |
         (B)
     +----|-----+          Client Identifier      +---------------+
     |         -+----(A)-- & Redirection URI ---->|               |
     |  User-   |                                 | Authorization |
     |  Agent  -+----(B)-- User authenticates --->|     Server    |
     |          |                                 |               |
     |         -+----(C)-- Authorization Code ---<|               |
     +-|----|---+                                 +---------------+
       |    |                                         ^      v
      (A)  (C)                                        |      |
       |    |                                         |      |
       ^    v                                         |      |
     +---------+                                      |      |
     |         |>---(D)-- Authorization Code ---------'      |
     |  Client |          & Redirection URI                  |
     |         |                                             |
     |         |<---(E)----- Access Token -------------------'
     +---------+       (w/ Optional Refresh Token)

   Note: The lines illustrating steps (A), (B), and (C) are broken into
   two parts as they pass through the user-agent.

                     Figure 3: Authorization Code Flow

   The flow illustrated in Figure 3 includes the following steps:

   (A)  The client initiates the flow by directing the resource owner's
        user-agent to the authorization endpoint.  The client includes
        its client identifier, requested scope, local state, and a
        redirection URI to which the authorization server will send the
        user-agent back once access is granted (or denied).

   (B)  The authorization server authenticates the resource owner (via
        the user-agent) and establishes whether the resource owner
        grants or denies the client's access request.

   (C)  Assuming the resource owner grants access, the authorization
        server redirects the user-agent back to the client using the
        redirection URI provided earlier (in the request or during
        client registration).  The redirection URI includes an
        authorization code and any local state provided by the client
        earlier.

   (D)  The client requests an access token from the authorization
        server's token endpoint by including the authorization code
        received in the previous step.  When making the request, the
        client authenticates with the authorization server.  The client
        includes the redirection URI used to obtain the authorization
        code for verification.

   (E)  The authorization server authenticates the client, validates the
        authorization code, and ensures that the redirection URI
        received matches the URI used to redirect the client in
        step (C).  If valid, the authorization server responds back with
        an access token and, optionally, a refresh token.

First, Slack client opens webview, loads Google’s authorization page, and specifies the mode as Authorization Code, Google lets the user confirm, then redirects back to the URL specified by Slack, and includes a parameter called code in the parameter, then Slack’s server takes the code, takes client_id, client_secret and requests Google again. Then Slack’s server takes the code and requests Google again with client_id, client_secret, and Google verifies the token and sends access_token if it is successful. Since the token is requested directly by Slack’s server, it is more secure than the previous three.

Authorization Code PKCE extension

Authorization Code PKCE extension

This pattern, which is basically the same as Authorization Code, differs in that it does not expose client_secret, so it can be used in browsers, etc. The process is as follows.

  • First the browser generates a random string, which we call code_verifier, limited to 43-128 characters in length
  • Then, we do sha256 encryption of code_verifier, and then do URL safe base64 encode, the resulting characters we call code_challenge
  • Next, the client opens the page of the authorization server, and in the parameters with code_challenge=foo&code_challenge_method=S256, code_challenge is generated in the second step, code_challenge_method specifies the hash method, S256 refers to sha256, and the authorization server logs this information
  • Then the server prompts the user for authorization, and when the user is authorized, the server calls back the URL specified by the browser with the code
  • The client extracts the code and then requests the authorization server with the code and code_verifier
  • The authorization server extracts the code_verifier from the request, sha256 it, and compares it with the code_challenge in step 3, and if it is consistent, the authorization is successful and the access_token is issued.

JWT

Next, let’s take a look at JWT. The full name of JWT is JSON Web Token, which simply means that three JSONs are linked together with a dot, for example

1
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

There are actually three parts.

  • Header, which indicates the token type and the signature algorithm
  • Payload, which contains the really useful information, often also called claims
  • Signature, which is the result of signing the Header and Payload

For example, in the JWT above, we decode it.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
In [1]: import base64

In [2]: header = b"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"

In [3]: payload = b"eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ"

In [4]: payload = payload + b'=' * (-len(payload) % 4)

In [5]: base64.urlsafe_b64decode(header)
Out[5]: b'{"alg":"HS256","typ":"JWT"}'

In [6]: base64.urlsafe_b64decode(payload)
Out[6]: b'{"sub":"1234567890","name":"John Doe","iat":1516239022}'

The advantage of JWT compared to the server-side session method is that you don’t need to read the database every time to get the information, the information is stored in the payload, just take it out and parse it, then compare the Signature and whether it expires. However, the disadvantage is that once issued, it can not be revoked, as long as someone gets the JWT, before the expiration date, you can operate with this identity. There is a remedy, which is to store the disabled JWT in the database, but then you have to read the database every time, so I don’t like JWTs very much.

OpenID Connect(OIDC)

Finally we come to our main point, OIDC, the main purpose of OIDC is to log in users and get user information, not to get other permissions. As we said before, OIDC is based on OAuth 2, so OIDC can use Authorization Code mode, Implicit mode, and a Hybrid mode.

Authentication using the Authorization Code Flow

Authentication using the Authorization Code Flow

The Authorization Code Flow goes through the following steps:

  • Client prepares an Authentication Request containing the desired request parameters.
  • Client sends the request to the Authorization Server.
  • Authorization Server Authenticates the End-User.
  • Authorization Server obtains End-User Consent/Authorization.
  • Authorization Server sends the End-User back to the Client with an Authorization Code.
  • Client requests a response using the Authorization Code at the Token Endpoint.
  • Client receives a response that contains an ID Token and Access Token in the response body.
  • Client validates the ID token and retrieves the End-User’s Subject Identifier.

The difference is that there must be an item called openid in the scope parameter, and another point is that there is an additional id_token in the final return result.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{
    "access_token": "SlAV32hkKG",
    "token_type": "Bearer",
    "refresh_token": "8xLOxBtZp8",
    "expires_in": 3600,
    "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjFlOWdkazcifQ.ewogImlzc
        yI6ICJodHRwOi8vc2VydmVyLmV4YW1wbGUuY29tIiwKICJzdWIiOiAiMjQ4Mjg5
        NzYxMDAxIiwKICJhdWQiOiAiczZCaGRSa3F0MyIsCiAibm9uY2UiOiAibi0wUzZ
        fV3pBMk1qIiwKICJleHAiOiAxMzExMjgxOTcwLAogImlhdCI6IDEzMTEyODA5Nz
        AKfQ.ggW8hZ1EuVLuxNuuIJKX_V8a_OMXzR0EHR9R6jgdqrOOF4daGU96Sr_P6q
        Jp6IcmD3HP99Obi1PRs-cwh3LO-p146waJ8IhehcwL7F09JdijmBqkvPeB2T9CJ
        NqeGpe-gccMg4vfKjkM8FcGvnzZUN4_KSP0aAp1tOJ1zZwgjxqGByKHiOtX7Tpd
        QyHE5lcMiKPXfEIQILVq0pc_E2DzL7emopWoaoZTF_m0_N0YzFC6g6EJbOEoRoS
        K5hoDalrcvRYLSrQAZZKflyuVCyixEoV9GfNQC3_osjzw2PAithfubEEBLuVVk4
        XUVrWOLrLl0nx7RkKU8NXNHq-rvKMzqg"
}

This is a JWT that, when parsed, contains the user’s information.

1
2
3
4
5
6
7
8
{
  "iss": "http://server.example.com",
  "sub": "248289761001",
  "aud": "s6BhdRkqt3",
  "nonce": "n-0S6_WzA2Mj",
  "exp": 1311281970,
  "iat": 1311280970
}

where.

  • iss is the abbreviation of Issuer, the value is the URL that issued this OpenID
  • sub is the abbreviation of Subject, which refers to the user corresponding to this OpenID
  • aud is the abbreviation of Audience(s), which refers to the client id authorized by this OpenID (there may be more than one)
  • exp is the timeout period
  • iat is the issuance time
  • nonce is a random string

There are a few other fields, which can be found at https://openid.net/specs/openid-connect-core-1_0.html#IDToken

Authentication using the Implicit Flow

Authentication using the Implicit Flow

The steps are as follows.

  • Client prepares an Authentication Request containing the desired request parameters.
  • Client sends the request to the Authorization Server.
  • Authorization Server Authenticates the End-User.
  • Authorization Server obtains End-User Consent/Authorization.
  • Authorization Server sends the End-User back to the Client with an ID Token and, if requested, an Access Token.
  • Client validates the ID token and retrieves the End-User’s Subject Identifier.

The process is basically the same as the Implicit mode of OAuth 2, the difference is that the value of response_type is not token but id_token token or id_token, and for the response result, a new id_token will be added in the URL fragment.

Authentication using the Hybrid Flow

Authentication using the Hybrid Flow

The steps are as follows.

  • Client prepares an Authentication Request containing the desired request parameters.
  • Client sends the request to the Authorization Server.
  • Authorization Server Authenticates the End-User.
  • Authorization Server obtains End-User Consent/Authorization.
  • Authorization Server sends the End-User back to the Client with an Authorization Code and, depending on the Response Type, one or more additional parameters.
  • Client requests a response using the Authorization Code at the Token Endpoint.
  • Client receives a response that contains an ID Token and Access Token in the response body.
  • Client validates the ID Token and retrieves the End-User’s Subject Identifier.

The difference is that the value of response_type in the request parameter is code id_token or code token or code id_token token.

  • When the value is code token or code id_token token, access_token is returned
  • When the value is code id_token or code id_token token, id_token is returned
  • code will definitely return

The above are the three authorization methods of OIDC, through OIDC, we can login to another system as a third party system and get some basic information, such as first name, last name, username, email, avatar url, etc.

Summary

This article summarizes the 5 authorization processes of OAuth 2, and then on top of that, introduces the OIDC process, and its role. I hope it will be helpful to readers.