Understand JSON Web Token

Andre Vianna
My Dev Zone
Published in
16 min readJan 17, 2022

JWT are an open, industry standard RFC 7519 method

https://jwt.io/
jwt.io

Summary

  1. JWT History
  2. JWT is an industry-standard (RFC-7519)
  3. JWT Process
  4. Components of JWT
  5. JWT in Detail
  6. JSON Web Signatures
  7. JSON Web Encryption (JWE)
  8. JSON Web Keys (JWK)
  9. JSON Web Algorithms
  10. References

1. JWT History

1.1 What is a JSON Web Token?

A JSON Web Token looks like this (newlines inserted for readability):
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

While this looks like gibberish, it is actually a very compact, printable representation of a series of claims, along with a signature to verify its authenticity.
{
“alg”: “HS256”,
“typ”: “JWT”

}

{

“sub”: “1234567890”,
“name”: “John Doe”,
“admin”: true
}

Claims are definitions or assertions made about a certain party or object. Some of these claims and their meaning are defined as part of the JWT spec.
Others are user defined. The magic behind JWTs is that they standardize certain claims that are useful in the context of some common operations. For example, one of these common operations is establishing the identity of certain party. So one of the standard claims found in JWTs is the sub (from “subject”) claim. We will take a deeper look at each of the standard claims.

Another key aspect of JWTs is the possibility of signing them, using JSON Web Signatures (JWS,RFC 75156), and/or encrypting them, using JSON Web Encryption (JWE, RFC 75167). Together with JWS and JWE, JWTs provide a powerful, secure solution to many different problems.

1.2 What problem does it solve?

Although the main purpose of JWTs is to transfer claims between two parties, arguably the most important aspect of this is the standardization effort in the form of a simple, optionally validated and/or encrypted, container format. Ad hoc solutions to this same problem have been implemented both privately and publicly in the past. Older standards8 for establishing claims about certain parties are also available. What JWT brings to the table is a simple, useful, standard container format.

Although the definition given is a bit abstract so far, it is not hard to imagine how they can be used:
login systems (although other uses are possible). We will take a closer look at practical applications. Some of these applications include:

• Authentication
• Authorization
• Federated identity
• Client-side sessions (“stateless” sessions)
• Client-side secrets

Encryption group

1.3 A little bit of history

The JSON Object Signing and Encryption group (JOSE) was formed in the year 20119. The group’s objective was to “standardize the mechanism for integrity protection (signature and MAC) and encryption as well as the format for keys and algorithm identifiers to support interoperability of security services for protocols that use JSON”.

With different examples of the use of the ideas produced by the group, were available. These drafts would later become the JWT, JWS, JWE, JWK and JWA RFCs. As of year 2016, these RFCs are in the standards track process and errata have not been found in them. The group is currently inactive.
The main authors behind the specs are Mike Jones10, Nat Sakimura11, John Bradley12 and Joe Hildebrand13.

https://openid.net/

2. JWT is an industry-standard (RFC-7519)

JWT is an industry-standard (RFC-7519) that defines how to transmit and store JSON objects compactly and securely between different applications. The data contained therein can be validated at any time as the token is digitally signed.

RFC-7519 Reference

3. JWT Process

Before taking a deep dive into the structure and construction of a JWT, we will take a look at several practical applications. This chapter will give you a sense of the complexity (or simplicity) of common JWT-based solutions used in the industry today. All code is available from public repositories for your convenience. Be aware that the following demonstrations are not meant to be used in production. Test cases, logging, and security best practices are all essential for production-ready code. These samples are for educational purposes only and thus remain simple and to the point.

3.1 Client-side/Stateless Sessions

The so-called stateless sessions are in fact nothing more than client-side data. The key aspect of this application lies in the use of signing and possibly encryption to authenticate and protect the contents of the session. Client-side data is subject to tampering. As such it must be handled with great care by the backend. JWTs, by virtue of JWS and JWE, can provide various types of signatures and encryption. Signatures are useful to validate the data against tampering. Encryption is useful to protect the data from being read by third parties.
Most of the time sessions need only be signed. In other words, there is no security or privacy concern when data stored in them is read by third parties. A common example of a claim that can usually be safely read by third parties is the sub claim (“subject”). The subject claim usually identifies one of the parties to the other (think of user IDs or emails). It is not a requirement that
this claim be unique. In other words, additional claims may be required to uniquely identify a user. This is left to the users to decide.
A claim that may not be appropriately left in the open could be an “items” claim representing a user’s shopping cart. This cart might be filled with items that the user is about to purchase and thus are associated to his or her session. A third party (a client-side script) might be able to harvest these items if they are stored in an unencrypted JWT, which could raise privacy concerns.

Figure 2.1: Client-side Signed Data

3.1.1 Security Considerations

3.1.1.1 Signature Stripping

A common method for attacking a signed JWT is to simply remove the signature. Signed JWTs are constructed from three different parts: the header, the payload, and the signature. These three parts are encoded separately. As such, it is possible to remove the signature and then change the
header to claim the JWT is unsigned. Careless use of certain JWT validation libraries can result in unsigned tokens being taken as valid tokens, which may allow an attacker to modify the payload at his or her discretion. This is easily solved by making sure that the application that performs the validation does not consider unsigned JWTs valid.

Signature Stripping

3.1.1.2 Cross-Site Request Forgery (CSRF)

Cross-site request forgery attacks attempt to perform requests against sites where the user is logged in by tricking the user’s browser into sending a request from a different site. To accomplish this, a specially crafted site (or item) must contain the URL to the target. A common example is an <img> tag embedded in a malicious page with the src pointing to the attack’s target.

For instance:
<! — This is embedded in another domain’s site →
<img src=”http://target.site.com/add-user?user=name&grant=admin">

The above <img> tag will send a request to target.site.com every time the page that contains it is loaded. If the user had previously logged in to target.site.com and the site used a cookie to keep the session active, this cookie will be sent as well. If the target site does not implement any CSRF mitigation techniques, the request will be handled as a valid request on behalf of the user. JWTs, like any other client-side data, can be stored as cookies.

Cross-Site Request Forgery

Short-lived JWTs can help in this case. Common CSRF mitigation techniques include special headers that are added to requests only when they are performed from the right origin, per session cookies, and per request tokens. If JWTs (and session data) are not stored as cookies, CSRF attacks are not possible. Cross-site scripting attacks are still possible, though.

3.1.1.3 Cross-Site Scripting (XSS)

Cross-site scripting (XSS) attacks attempt to inject JavaScript in trusted sites. Injected JavaScript can then steal tokens from cookies and local storage. If an access token is leaked before it expires, a malicious user could use it to access protected resources. Common XSS attacks are usually caused by improper validation of data passed to the backend (in similar fashion to SQL injection attacks).
An example of a XSS attack could be related to the comments section of a public site. Every time a user adds a comment, it is stored by the backend and displayed to users who load the comments section. If the backend does not sanitize the comments, a malicious user could write a comment in such a way that it could be interpreted by the browser as a <script> tag. So, a malicious user could insert arbitrary JavaScript code and execute it in every user’s browser, thus, stealing credentials stored as cookies and in local storage.

Persistent Cross Site Scripting
Reflective Cross Site Scripting

Mitigation techniques rely on proper validation of all data passed to the backend. In particular, any data received from clients must always be sanitized. If cookies are used, it is possible to protect them from being accessed by JavaScript by setting the HttpOnly flag2. The HttpOnly flag, while useful, will not protect the cookie from CSRF attacks.

3.1.2 Are Client-Side Sessions Useful?

There are pros and cons to any approach, and client-side sessions are not an exception. Some applications may require big sessions. Sending this state back and forth for every request (or group of requests) can easily overcome the benefits of the reduced chattiness in the backend. A certain balance between client-side data and database lookups in the backend is necessary. This depends on the data model of your application. Some applications do not map well to client-side sessions. Others may depend entirely on client-side data. The final word on this matter is your own! Run benchmarks, study the benefits of keeping certain state client-side. Are the JWTs too big? Does
this have an impact on bandwidth? Does this added bandwidth overthrow the reduced latency in the backend? Can small requests be aggregated into a single bigger request? Do these requests still require big database lookups? Answering these questions will help you decide on the right approach.

3.2 Federated Identity

Federated identity systems allow different, possibly unrelated, parties to share authentication and authorization services with other parties. In other words, a user’s identity is centralized. There are several solutions for federated identity management: SAML and OpenID Connect are two of the most common ones. Certain companies provide specialized products that centralize authentication and authorization. These may implement one of the standards mentioned above or use something completely different. Some of these companies use JWTs for this purpose. The use of JWTs for centralized authentication and authorization varies from company to company, but the essential flow of the authorization process is:

Common Federated Identity Flow

1. The user attempts to access a resource controlled by a server.
2. The user does not have the proper credentials to access the resource, so the server redirects the user to the authorization server. The authorization server is configured to let users log-in using the credentials managed by an identity provider.
3. The user gets redirected by the authorization server to the identity’s provider log-in screen.
4. The user logs-in successfully and gets redirected to the authorization server. The authorization server uses the credentials provided by the identity provider to access the credentials required by the resource server.
5. The user gets redirected to the resource server by the authorization server. The request now has the correct credentials required to access the resource.
6. The user gets access to the resource successfully.

All the data passed from server to server flows through the user by being embedded in the redirection requests (usually as part of the URL). This makes transport security (TLS) and data security essential.
The credentials returned from the authorization server to the user can be encoded as a JWT. If the authorization server allows logins through an identity provider (as is the case in this example), the authorization server can be said to be providing a unified interface and unified data (the JWT) to
the user.
For our example later in this section, we will use Auth0 as the authorization server and handle logins through Twitter, Facebook, and a run-of-the-mill user database.

3.2.1 Access and Refresh Tokens

Access and refresh tokens are two types of tokens you will see a lot when analyzing different federated identity solutions. We will briefly explain what they are and how they help in the context of authentication and authorization. Both concepts are usually implemented in the context of the OAuth2 specification. The OAuth2 spec defines a series of steps necessary to provide access to resources by separating access from ownership (in other words, it allows several parties with different access levels to access the same
resource). Several parts of these steps are implementation defined. That is, competing OAuth2 implementations may not be interoperable. For instance, the actual binary format of the tokens is not specified. Their purpose and functionality is. Access tokens are tokens that give those who have them access to protected resources. These tokens are usually short-lived and may have an expiration date embedded in them. They may also carry or be associated with additional information (for instance, an access token may carry the IP address from which requests are allowed). This additional data is implementation defined. Refresh tokens, on the other hand, allow clients to request new access tokens.

For instance,
after an access token has expired, a client may perform a request for a new access token to the authorization server. For this request to be satisfied, a refresh token is required. In contrast to access tokens, refresh tokens are usually long-lived.

Refresh and access tokens

The key aspect of the separation between access and refresh tokens lies in the possibility of making access tokens easy to validate. An access token that carries a signature (such as a signed JWT) may be validated by the resource server on its own. There is no need to contact the authorization server for this purpose. Refresh tokens, on the other hand, require access to the authorization server. By keeping validation separate from queries to the authorization server, better latency and less complex access patterns are possible. Appropriate security in case of token leaks is achieved by making access tokens as short-lived as possible and embedding additional checks (such as client checks) into them. Refresh tokens, by virtue of being long-lived, must be protected from leaks. In the event of a leak, blacklisting may be necessary in the server (short-lived access tokens force refresh tokens to be used eventually, thus protecting the resource after it gets blacklisted and all access tokens are expired).

Note: the concepts of access token and refresh token were introduced in OAuth2. OAuth 1.0 and 1.0a use the word token differently.

3.2.2 JWTs and OAuth2

Although OAuth2 makes no mention of the format of its tokens, JWTs are a good match for its requirements. Signed JWTs make good access tokens, as they can encode all the necessary data to differentiate access levels to a resource, can carry an expiration date, and are signed to avoid validation queries against the authorization server. Several federated identity providers issue access tokens in JWT format. JWTs may also be used for refresh tokens. There is less reason to use them for this purpose, though. As refresh tokens require access to the authorization server, most of the time a simple UUID will
suffice, as there is no need for the token to carry a payload (it may be signed, though).

3.2.3 JWTs and OAuth2

OpenID Connect is a standardization effort to bring typical use cases of OAuth2 under a common, well-defined spec. As many details behind OAuth2 are left to the choice of implementers, OpenID Connect attempts to provide proper definitions for the missing parts. Specifically, OpenID Connect defines an API and data format to perform OAuth2 authorization flows. Additionally, it provides an authentication layer built on top of this flow. The data format chosen for some of its parts is JSON Web Token. In particular, the ID token is a special type of token that carries information about the authenticated user.

3.2.3.1 OpenID Connect Flows and JWTs

OpenID Connect defines several flows which return data in different ways. Some of this data may be in JWT format.
• Authorization flow: the client requests an authorization code to the authorization endpoint (/authorize). This code can be used againt the token endpoint (/token) to request an ID token (in JWT format), an access token or a refresh token.
• Implicit_flow: the client requests tokens directly from the authorization endpoint (/authorize). The tokens are specified in the request. If an ID token is requested, is is returned in JWT format.
• Hybrid flow: the client requests both an authorization code and certain tokens from the authorization endpoint (/authorize). If an ID token is requested, it is returned in JWT format. If an ID token is not requested at this step, it may later by requested directly from the token endpoint (/token).

3.2.4 Example

For this example we will use Auth0 as the authorization server. Auth0 allows for different identity providers to be set dynamically. In other words, whenever a user attempts to login, changes made in the authorization server may allow users to login with different identity providers (such as Twitter,
Facebook, etc). Applications need not commit to specific providers once deployed. So our example can be quite simple. We set up the Auth0 login screen using the Auth0.js library in all of our sample servers. Once a user logs in to one server, he will also have access to the other servers (even
if they are not interconnected).

Auth0 as Authorization Server

3.2.4.1 Setting up Auth0 Lock for Node.js Applications

Setting up the Auth0 library can be done as follows. We will use the same example used for the stateless sessions example:

const auth0 = new window.auth0.WebAuth({
domain: domain,
clientID: clientId,
audience: 'app1.com/protected',
scope: 'openid profile purchase',
responseType: 'id_token token',
redirectUri: 'http://app1.com:3000/auth/',
responseMode: 'form_post'
});
// (...)
$('#login-button').on('click', function(event) {
auth0.authorize({
prompt: 'none'
});
});

Note the use of the prompt: ‘none’ parameter for the authorize call. The authorize call redirects the user to the authorization server. With the none parameter, if the user has already given authorization for an app to use his or her credentials for access to a protected resource, the authorization server will simply redirect back to the application. This looks to the user as if he were
already logged-in in the app. In our example, there are two apps: app1.com and app2.com. Once a user has authorized both apps (which happens only once: the first time the user logs-in), any subsequent logins to any of
both apps will also allow the other app to login without presenting any login screens.
To test this, see the README file for the example located in the
samples/single-sign-on-federated-identity directory to set up both applications and run them. Once both are running, go to app1.com:300016 and app2.com:300117 and login. Then logout from both apps. Now attempt to login to one of them. Then go back to the other one and login.
You will notice the login screen will be absent in both apps. The authorization server remembers previous logins and can issue new access tokens when requested by any of those apps. Thus, as long as the user has an authorization server session, he or she is already logged-in to both apps.
Implementing CSRF mitigation techniques is left as en exercise for the reader.

4. Components of JWT

It consists of three sections: Header, Payload and Signature.

a.Header

A Header is a JSON object that defines information about the token type (typ), in this case JWT, and the encryption algorithm used in its signature (alg), usually HMAC SHA256 or RSA.

Header

b. Payload

The Payload is a JSON object with the Claims (information) of the treated entity, normally the authenticated user.

These claims can be of 3 types:
Reserved claims: non-mandatory (but recommended) attributes that are used in token validation by API security protocols.

sub (subject) = Entidade à quem o token pertence, normalmente o ID do usuário;
iss (issuer) = Emissor do token;
exp (expiration) = Timestamp de quando o token irá expirar;
iat (issued at) = Timestamp de quando o token foi criado;
aud (audience) = Destinatário do token, representa a aplicação que irá usá-lo.

Generally, the most used attributes are: sub, iss and exp.
Public claims: attributes that we use in our applications. We usually store authenticated user information in the application.

name
roles
permissions

Private claims: attributes defined especially for sharing information between applications.

Payload

For security, it is recommended not to store confidential or sensitive information in the token.

c. Signature

The signature is the concatenation of the hashes generated from the Header and Payload using base64UrlEncode, with a secret key or RSA certificate.

Signature

This signature is used to guarantee the integrity of the token, in case it was modified and if it was actually generated by you.

This prevents man-in-the-middle attacks, where the attacker could intercept the request and modify its content, thereby impersonating the user with false information. If the payload is changed, the final hash will not be valid as it was not signed with your secret key.

Only those in possession of the key can create, change and validate the token.

Final result

The end result is a token with three sections (header, payload, signature) separated by “.” — Score.

Token JWT

Using the token

When logging into an authentication service a JWT token is created and returned to the client. This token must be sent to the APIs through the Authorization header of each HTTP request with the Bearer flag, as shown in the diagram below.

Authorization: Bearer <token>
JWT Token Process Authentication Login

In possession of the token, the API does not need to go to the database to query the user’s information, as the JWT token itself contains its access credentials.

5. JWT in Detail

6.JSON Web Signatures

7.JSON Web Encryption (JWE)

8.JSON Web Keys (JWK)

9.JSON Web Algorithms

--

--

Andre Vianna
My Dev Zone

Software Engineer & Data Scientist #ESG #Vision2030 #Blockchain #DataScience #iot #bigdata #analytics #machinelearning #deeplearning #dataviz