JWT: On Focus
In our day-to-day life, we may have come across the term called “tokens”. In general, tokens are used as an exchange mechanism in order to get some items or get some work done. For example, if you visit a doctor a token number will be given to you at the reception. By providing that token you will be able to meet the doctor when your turn comes. Another example would be, assume that you are at your school sports day. For all the students the school has provided lunch tokens which you can provide at the canteen and get your lunch.
Similar to these scenarios, tokens are used to get things done in an orderly manner in the digital world; majorly in authentication and authorization processes. A huge chunk of internet communication relies on the availability of different types of tokens. For example, JSON Web Token (JWT) and SAML token. The motive of this blog is to provide a base knowledge of JWT and its signature verification process.
The background story of JWT
The Json Web Token is a self-contained token that contains all the necessary details including user details and privileges in it. JSON Web Token (JWT) is an open standard (RFC 7519) that defines a way for transferring information between two parties. This can be used as a medium to send end-user details to the backend. JWT has 3 main components. They are,
- Header
- Payload
- Signature
The format of a JWT is Header.Payload.Signature. To be more specific the token format would be,
base64URL(header).base64URL(payload).
alg(
base64UrlEncode(header) + “.” +
base64UrlEncode(payload),
secret)
Header
The header component contains the meta-data of the token validation. In other words, the details related to signature verification are presented in the header. In general, the header contains 2 parts. They are the token type and the signing algorithm used in the token.
{
"typ":"JWT",
"alg":"HS256"
}
Sometimes we might see some of the JWTs to have the header as follows.
{
“x5t”: “NTAxZmMxNDMyZDg3MTU1ZGM0MzEzODJhZWI4NDNlZDU1OGFkNjFiMQ”,
“kid”: “NTAxZmMxNDMyZDg3MTU1ZGM0MzEzODJhZWI4NDNlZDU1OGFkNjFiMQ_RS256”,
“alg”: “HS256”
}
These are known as JSON Web Key(JWK) which holds a set of public keys to verify the JWT. The JWK contains many other keys such as the kid, x5t, kty, x5c and etc. A sample key object is as follows.
{
"keys": [
{
"kty": "RSA",
"use": "sig",
"x5t": "M2maFm3VYlMBOn3GetVWGXkrKrk",
"kid": "SIGNING_KEY",
"x5c": [
"MIIE………(base64 encoded cert)………..zM=",
"MIIC………(base64 encoded cert)………..tow=="
],
"alg": "HS256"
}
]
}
- kty: Key Type — The algorithms type/family that is used with this key
- alg: Algorithm — The specific algorithm
- use: Usage — ‘sig’ for signing keys, ‘enc’ for encryption keys
- x5t: X.509 Certificate Thumbprint — Used to identify specific certificates
- kid: Key Identifier — Acts as an ‘alias’ for the key
- x5c: X.509 Certificate Chain — Chain of certificates used for verification. The first entry in the array is always the cert to use for token verification. The other certificates can be used to verify this first certificate.
In JWT, the header value is presented as a base64 encoded value.
Payload
The payload is the second part of the JWT that contains user details or any entity details. These details are named as “claims”. For example, getting the logged-in user’s role can be considered as a claim. The claim can be further categorized into 3 types. They are,
- Registered claims(Reserved claims): A set of predefined claims that are recommended but not mandatory to be in the payload.
- E.g: iss (issuer), exp (expiration time), sub (subject), aud (audience)
2. Custom claims: These are the claims created by the user based on their use cases. They can be classified into public claims and private claims.
a. Public claims: These are created for public consumptions for example, “name”, “email”. In order to use this claim without any clashes, the created claims should be registered or use collision-resistant names through namespacing.
b. Private claims: These are created specifically to be used to share details about the user’s applications. For example, “employee code”, “department” are private claims. When naming the private claims we should be careful in order to prevent the collision.
An example of a JWT payload will be something similar to the below one.
{
"sub": "admin",
"aud": [
"5phi4esTwaJeBSwmtMbA21OOqQ0a",
"https://192.168.8.102:9444/oauth2/token"
],
"nbf": 1587047924,
"azp": "5phi4esTwaJeBSwmtMbA21OOqQ0a",
"scope": "app_eid",
"iss": "https://localhost:9443/oauth2/token",
"exp": 1587051524,
"iat": 1587047924,
"jti": "1a82da55-0156-4ed9-a645-8c6516bd8fd0"
}
Please note that all the date/time values in the token are presented as epochs which can be converted to the standard format of date/time.
Signature
Signature is an important element in the JWT. Signature is used to verify whether the token is coming from the intended sender and whether the data integrity is preserved while passing it from the source to the destination. In short security in JWT is achieved via the signature component.
Before moving into the signature verification process, let’s see how the signature is constructed. For example,
HMACSHA256
(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)
The signature is constructed by hashing the base64URL of header concatenated with “.”, base64URL of payload, and a secret with HMACSHA256 aka HS256 hashing algorithm.
Signature verification process
When a JWT token is sent, the signature is encrypted with the sender’s private key. So that anyone who has the public key of the sender can decrypt the token. All seems to be good. But wait…! Where are these public and private keys are stored?
The public key is embedded in the SSL certificate and the private key is stored on the server and kept secret. During the SSL handshake, the server will share its certificate with the client so that the client will be aware of the server’s public key. Asymmetric keys are used for signing and verifying the JWT signature.
Using the sender’s public certificate the signature will be decrypted as they are signed/encrypted with the sender’s private key. This will be a hashed value. Assume this value as “A”.
A = Decrypted signature(hashed value)
Next, the base64UrlEncode(header) + “.” + base64UrlEncode(payload) values will be hashed along with the secret, if exists, using the hashing algorithm mentioned in the header. Let this value be “B”.
B = hash[(encoded header . encoded payload), secret]
If both the hash values are the same (A==B) then we can confirm this as a genuine token from the intended user and the token has not been modified in between the process. That’s how the signature verification process takes place in JWT.
Summary…!
This blog gives an overall idea about JWT token, its structural components, how it is secured, and the signature verification process. This blog can be used as a heads up for beginners and as a reference to quickly go through the important points in JWT. I hope that this was a good startup to learn and keep track of JWT. I hope to see you with a practical implementation of JWT in my next blog. Until then tata 👋🏻
Stay home! Stay hygiene! Stay safe!
Happy Learning!!!