JSON Web Token (JWT, often pronounced “jot”) is a powerful tool for confidently transmitting data between two parties through tokens. These parties can consist of users, servers, or any other combination of services. Based on an open standard (RFC-7519), JWTs are digitally signed with an encryption algorithm, so the receiving party can trust the information contained within. In computer security this concept is known as Data Integrity. One main benefit of using a JWT is that it’s very compact (assuming the issuer uses , which is recommended). They are generally small enough to be sent through a POST request, in an HTTP Header, or even as a query string within a URL. However, the more claims you add to a JWT, the more bloated it becomes. You theoretically create a JWT that exceeds the maximum length of a URL (~2000 characters), but don’t do that! It’s best practice to only add information that the receiving party . JWS Compact Serialization could needs JWTs are also self-contained, meaning that a JWT can neatly encompass: identifying information about a user what a user can access an expiration date a signature for content validation any other serializable information Keep in mind that anyone can decode the information contained in a JWT without knowing the private keys. . The purpose of a JWT isn’t to encrypt data so it can’t be read during transport (that’s SSL), instead it allows the receiving party to trust that the data received was unaltered during transport. With that out of the way, we can dive into what makes up a JWT. For this reason, you should never put secret information like passwords or cryptographic keys in a JWT Structure of a JWT Every JWT is generated with the same structure. There are three parts, separated by a period. Each section is comprised of -encoded JSON containing specific information for that token. Let’s break down each section below: base64url Header: The first section is known as the . This is where a few key pieces of information are contained: header : the algorithm used to sign the token (e.g. for for RSA SHA-256). is recommended because it uses asymmetric (public/private) keys instead of relying on a shared private key. alg HS256 HMAC SHA-256, or RS256 RS256 : an optional certificate thumbprint containing a -encoded SHA-1 of the certificate corresponding to the encryption key used. x5t base64url X.509 : the JSON Web Key (JWK) url. We’ll cover this in the JWK(S) section below. jku : an optional parameter indicating which encryption key was used. This can be used as a signal to recipients that a key was changed. kid : the type of token. This parameter is completely optional, however if preset, it’s recommended that the value be “JWT” (always capitalized) typ This isn’t a comprehensive list of all the parameters you can find in this section, but is instead a highlight of some more common parameters you’ll likely encounter. You may have noticed the overall theme of this section is encryption — which is why you may also see this section referred to as the . JSON Web Encryption (JWE) header Payload (Claims): The second section is the (also known as ), where the JWT issuer can store custom information for the receiving party. There are three types of claims that can be used in the payload: , , and . payload claims Public Private Registered must be collision-resistant — so they should be extremely unique to ensure no two public claims can have the same name. All public claims should be defined in the or defined as a URI that contains a unique namespace. Public Claims IANA JSON Web Token Registry on the other hand do not have to be collision resistant, and can be named anything as long as the issuing and receiving party both agree on the use of the claim. It also cannot conflict with a public or registered claim for obvious reasons. Private Claims are universally defined claims which are reserved for specific purposes. Some common Registered Claims are: Registered Claims : token audience (who the token is intended for) aud : token expiration in NumericDate format. exp : time the token was issued (issued at) iat : token issuer iss : unique identifier for a token jti : time before which the token should not be accepted (not before) nbf : token’s subject sub Signature: The final section is the . This is what makes a JWT secure and ensures the integrity of your JWT during transport. The signature is simply a hash of all the content that was generated with the JWT. That means if any part of the JWT changes, the signature will be invalidated — rendering the JWT malformed. A JWT is signed with a . The algorithm for generating a signature is shown below: signature JSON Web Algorithm (JWA) RS256 RSASHA256( ( ) + "." + ( ), ) base64UrlEncode header base64UrlEncode payload secret As you can see, the algorithm takes the -encoded and , concatenated with a period, and signs it with a key that is only known to the service that generated the JWT. You may also see this referred to as the . base64url header payload secret JSON Web Signature (JWS) : validate your JWTs on the receiving end. Given that claims in a JWT are simply -encoded, you can decode a JWT without actually validating where it came from, or whether it was properly signed. If you don’t validate JWTs when decoding, anyone could send your application a custom-made JWT, rendering the security useless. Most JWT packages and middleware handle validation for you automatically, but don’t assume that they all will. Important Note Always base64url If your issuer uses (or another asymmetric algorithm) to sign tokens, you’ll require a public key to validate the token. But how do you get the public key? Great question! And a perfect segue to our next topic. RS256 JWK(S) The JSON Web Key (JWK) is a JSON object that contains a well-known public key which can be be used to validate the signature of a signed JWT. If the issuer of your JWT used an asymmetric key to sign the JWT, it will likely host a file called a JSON Web Key Set (JWKS). The JWKS is a JSON object that contains the property , which in turn holds an array of JWK objects. See an example of a JWKS below: keys The service may only use one JWK for validating web tokens, however the JWKS may contain multiple keys if the service rotates signing certificates. The endpoint for retrieving a JWK(S) can vary and should be documented for your issuer. For example, the the standard location for auth0 is: . Okta, on the other hand makes use of metadata for storing the endpoint. Others may make use of the JWE header parameter discussed above. https://YOUR-TENANT.auth0.com/.well-known/jwks.json jwks_uri jku Any time your application validates a JWT, it will attempt to retrieve the JWK(S) from the issuer in order to ensure the JWT signature matches the content. This process is made simple with open source packages like . node-jsonwebtoken JWT Usage A JWT is usually attached to a HTTP request via the HTTP header as a Bearer token. See below for an example (line-breaks added for readability): Authorization The JWT will have to be sent with every request to the backend, which is a tradeoff to consider. The great benefit of this approach is that this provides a stateless form of authentication since the server doesn’t have to remember the user’s information in session storage, significantly reducing the amount of work required to manage that state on the backend. A drawback is that since JWTs are stateless, you cannot invalidate them without storing session state. JWTs will automatically be invalidated after their expiration date, but depending on how long the expiration was set for (10 hours is common), a user could retain access to a service after being removed. Putting it all together Understanding the theory behind generating and using a JWT is great, but I always find a practical example helps solidify understanding. So, I’ll leave you with a diagram that shows a typical flow where a JWT is used: I want to clarify steps 4–7 by pointing out that it would be odd to send a JWT to a server simply for the purpose of sending a JWT. The JWT should accompany an actual protocol request, like an HTTP Request ( / / / ), as a voucher for that specific request. Moreover, by living on the header, it keeps your query strings and payloads clear of extra data so they can stay neat and focused. POST PUT GET DELETE Authorization Thanks for reading, and please leave some claps if you found this useful!