JSON Web Tokens (JWT)
To understand the concept of JWT, we need to understand the authentication and authorisation.
- Authentication
- Authorisation
- JWTs
Authentication
Authentication is a process in which a user is determined if he is exactly who he is claiming to be. For eg. a user "John Smith" requesting for his FB messages needs to be authenticated by the FB server that he is indeed John Smith, not someone impersonating him.
Username, password, OTP are common ways of authenticating a user.
Authorisation
John, say, needs to access messages of a group he is part of. Now server needs to verify if John is indeed an admin of the group and he is authorised to view the group's inbox.
Authorisation is the process of checking for the access privilege of the user. Server typically does this by verifying the user id against the roles mapped to it.
JSON Web Tokens
JWTs are used to handle both authentication and authorisation. Let us see how JWTs work before we dive into how it is used to ensure authentication and authorisation.
Like the name says, JWTs are JSON format based tokens used in the web. JWTs usually look like aaaaaaaa.bbbbbbb.ccccccc
. Notice the .
delimiter between sections. These three sections in a JWT are
- Header
- Payload
- Signature respectively. Each section is in JSON format as specified above.
Header
A JWT header consists of two parts. The type of the token typ
and the algorithm used to sign this token alg
. This alg
section contains an identifier for a hashing algorithm such as HMAC
, SHA256
or RSA
.
A typical header section looks like
{
"typ": "JWT",
"alg": "SHA256"
}
Payload
JWT's payload contains claims related to the token. Claims are basically statements about a specific section of data. Examples of claims in a JWT as issuer_name
, creation_time
, expiration_time
etc.
The claims are represented by either reserved keywords or custom keywords. The advtange of a reserved keyword is for universal interoperability. As in, any receiver can make a reserved claim mandatory and sender would know what keyword would use without explicit agreement.
Examples of reserved claims are iss
for issuer, exp
for expiry date, sub
for subject. A JWT can contain both reserved claims and custom claims.
{
"iss": "server_identifier",
"sub": "login_auth",
"exp": "1621177091", // unix epoch for standardization,
"user_id": "IN876TY34"
}
In this example, user_id
is a custom claim.
Note: Both header and payload are Base64Url encoded.
Signature
Signature is what provides security to a JWT and prevents tampering of the data. Anyone can see the contents of a JWT payload if it is not encrypted, but any tampering will make the token invalid because tampering of the data changes the signature.
A signature of a JWT is created with the algorithm specified in the header section along with a secret, encoded sections of header and payload. Since signature is created based on the encoded payload, any changes in the payload will invalidate the signature.
Though attacker has access to encoded header, payload and algorithm from the JWT, he won't know the secret.
SHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
A JWT after creating these sections look like
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InNoYXNoYW5na2EiLCJwYXNzd29yZCI6IjEyMzQ1IiwiaWF0IjoxNTM2MjgwMjM1LCJleHAiOjE1MzYyODAyNjV9.iplar3jWiW8rh1gU1H6pYaPu6-njCfflrP8GLbx9Imw
Now that we have created a JWT, let us look into how it is used in authentication and authorisation.
How to use JWTs ?
JWTs are created during the authentication flow. Once the user logs in with username/password details, a JWT is generated by the server. This JWT is stored by client , usually in local storage, and is sent to server for each subsequent request as part of Authorisation
header.
'Authorisation': 'Bearer <jwt>'
In the payload section of JWT, data related to the authentication and authorisation can be stored which is used by the server to authenticate the user and also verify the user's authorisation for various API access etc.
Example of creating JWT using Node.js
We use a npm package
npm install jsonwebtoken
const jwt = require('jsonwebtoken')
const jwtSecretKey = '<JWT_SECRET_KEY_STORED_AS_ENVIRONMENT_VARIABLE>';
const data = {
"time": Date(),
"iss": "server_identifier",
"sub": "login_auth",
"exp": "1621177091",
"user_id": "IN876TY34"
}
const token = jwt.sign(data, jwtSecretKey);
A JWT is not to be confused with a cookie. More discussion into this comparison will be subject of next post. A JWT is usually used to store and transmit authorisation data.
One downside of using a JWT is all the data is stored in it as claims. Since it is passed to server from client for every request, there is lot of data being transmitted over the network everytime and this only gets worse as more and more data is stored in a JWT.
Top comments (0)