JWT
JSON Web Tokens (JWT) are an open, industry-standard method (RFC 7519) for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.
Construction of JWT
A JWT is composed of three parts, separated by dots (.), which are -
Header
Payload
Signature
Therefore, a JWT typically looks like the following -
Header
The header typically consists of two parts: the type of token, which is JWT, and the signing algorithm being used, such as HMAC SHA256 or RSA.
This JSON is Base64Url encoded to form the first part of the JWT. It contains the metadata about the token, typically identifying the token type (typ) and the hashing algorithm (alg) used, encoded in JSON format.
Payload
The payload contains the claims. Claims are statements about an entity (typically, the user) and additional data. Claims provide identifying information about the logged-in user. There are no restrictions on the payloads sizes; however, it should be kept short.
There are three types of claims: registered, public, and private claims.
Registered Claims: These are a set of predefined claims which are not mandatory but recommended, to provide a set of useful, interoperable claims. Some of them are: iss (issuer), exp (expiration time), sub (subject), aud(audience), and others.
Public Claims: These can be defined at will by those using JWTs. But to avoid collisions they should be defined in the IANA JSON Web Token Registry or be defined as a URI that contains a collision resistant namespace. Example: UUID (Universally Unique Identifier), or an Object Identifier (OID)
Private Claims: These are the custom claims created to share information between parties that agree on using them and are neither registered or publicclaims. Private claims may subject to collision.
The payload is then Base64Url encoded to form the second part of the JSON Web Token.
Signature
To create the signature part, you have to take the encoded header, the encoded payload, a secret, the algorithm specified in the header, and sign that.
For example if you want to use the HMAC SHA256 algorithm, the signature will be created in the following way:
The signature is used to verify the message wasn't changed along the way, and, in the case of tokens signed with a private key, it can also verify that the sender of the JWT is who it says it is.
Python Exercise
Write a code in any language for generating the Signature mentioned above.
The provided Python script generates a cryptographic signature using the HMAC SHA256 algorithm. It consists of two main functions and a main execution block.
Why Convert to Bytes?
Base64 Encoding: The Base64 encoding process works with bytes. Therefore, any string that needs to be Base64 encoded must first be converted to bytes.
HMAC Signature Creation: The HMAC algorithm works with bytes. It requires the key (secret) and the message to be in bytes to perform the hashing operation. This is because cryptographic algorithms operate at the byte level, processing binary data directly.
Why Use 'utf-8'?:
Standard Encoding: 'utf-8' is a widely used character encoding standard that can represent every character in the Unicode character set. It ensures that any text (including special characters and symbols) is correctly converted to bytes and back to text without data loss.
Compatibility: Using 'utf-8' ensures compatibility across different systems and platforms. It is the default encoding for web protocols and many programming languages, making it a safe choice for text encoding.
Consistency: Encoding and decoding strings using 'utf-8' ensures consistent behavior and avoids potential issues with non-ASCII characters, which might be handled differently in other encodings.
Signing Algorithms
The purpose of the signature section in a JWT (JSON Web Token) is to allow intermediaries to verify the authenticity of the token, ensuring that it has not been tampered with. The process of checking the signature of a JWT is known as validation or token validation. Various signing algorithms are used to sign the header and payload sections. These algorithms are broadly divided into two types:
Symmetric Key Algorithms
In a symmetric-key algorithm, a single secret key is used to encrypt and decrypt the data. One commonly used symmetric key algorithm is HMAC (Hash-based Message Authentication Code).
HMAC: HMAC takes a hash function, a message, and a secret key as inputs and produces a hash value as output. The strength of the cryptographic hash function ensures that the message cannot be altered without the secret key. Using a weak hash function may allow malicious users to compromise the validity of the output. Therefore, strong hash functions must be used with HMAC.
HMAC + SHA256 (HS256): The SHA2 family of hash functions, including SHA256, is still considered secure by today's standards. The hashes of the header and payload sections are generated using the SHA256 hashing algorithm. These signing algorithms facilitate the easy creation and validation of tokens and should be used when all intermediary parties can be trusted to secure the secret key.
Asymmetric Algorithms
In asymmetric key algorithms, two different keys are used: one for encryption and one for decryption. The private key, which is kept secret, is used to encrypt the data, while the public key, which is publicly available, is used to decrypt the data.
RSA + SHA256 (RS256): In this algorithm, the identity provider holds the private key used to generate the signature, while the JWT token consumer uses the public key to verify the signature. RSA algorithms are commonly used in microservice architectures where you cannot trust the opposite party with your private key. Sharing the private key would allow the party to generate arbitrary tokens, posing a severe security threat. Thus, using RSA with a public/private key pair helps maintain security in such environments.
There are several other algorithms available that can be used to create a JWT.
Example of JWT
Lets take the followings as example -
Header
Payload
Secret
Construction
Step 1: Encode the Header in Base64
Step 2: Encode the Payload in Base64
Step 3: Create the Signature
Resulting JWT
Code Example for Generating JWT
Low-level Technical Architecture in Generating JWT
Header Creation: A JSON object specifying the algorithm used for the signature and the type of token is created. This object is then Base64Url encoded.
Payload Creation: A JSON object containing the claims is created. This object is then Base64Url encoded.
Signature Creation:
Concatenate the encoded header and payload with a period (
.
).Apply the cryptographic algorithm specified in the header to the concatenated string using a secret key.
The result is then Base64Url encoded to form the final part of the JWT.
Token Assembly: The JWT is constructed by concatenating the encoded header, the encoded payload, and the encoded signature with periods (.
) separating them.
By following these steps, a JWT is generated which can then be used to securely transmit information between parties. The receiving party can verify the integrity and authenticity of the token by using the same secret key (in case of symmetric algorithms) or the corresponding public key (in case of asymmetric algorithms).
How Does JWT Work?
In authentication, when a user successfully logs in using their credentials, a JSON Web Token (JWT) is returned. Because tokens function as credentials, they must be managed securely.
Whenever the user wants to access a protected route or resource, the user agent should send the JWT, typically in the Authorization header using the Bearer schema. The content of the header should look like this:
This can sometimes serve as a stateless authorization mechanism. The server’s protected routes will check for a valid JWT in the Authorization header, granting access if present. If the JWT includes necessary data, database queries may be reduced, although not always.
Sending the token in the Authorization header avoids Cross-Origin Resource Sharing (CORS) issues since it doesn't use cookies.
Let's take a broader look at the following diagram -
The process can be divided into two parts -
Obtaining a JWT
When a user logs in with their credentials, a JSON Web Token (JWT) is issued.
To access a protected route or resource, the user agent sends the JWT in the Authorization header using the Bearer schema.
The server checks for a valid JWT in the Authorization header. If valid, access is granted to the protected resource.
Accessing Resource with JWT
The application/resource server receives a request containing a JWT.
The JWT is Base64-URL decoded.
The server retrieves the algorithm type from the header section of the JWT.
Using the header and payload sections, the server generates its own hash and encrypts it using the secret key.
If the new signature matches the received signature, the server treats the JWT as valid.
Since the payload includes user identifiable information, the server can now authorize access to the required resources.
Types of Tokens
There are multiple set of tokens and each are used for their own different purposes. The most common in JWT based authentications are Access Tokens and Refresh Tokens.
Access Tokens
When a user authenticates, they are given an access token to make authorized calls to the API server. This token includes all the information the server needs to decide whether the user or device can access the requested resource. However, access tokens have a short lifespan, after which the user can no longer make authorized requests to the application server. The issuer party controls the lifespan of an access token. These tokens contain claims such as "iat" (Issued at) and "exp" (Expiration Time), which indicate when the token will expire.
Refresh Tokens
A refresh token is a special type of token used to obtain a new access token. When an access token expires, the user would typically need to re-authenticate. However, this can be avoided by using refresh tokens. As the access token nears its expiry, the refresh token makes an API call to get a new access token from the server. This significantly enhances the authentication process, allowing the user to authenticate only once, with subsequent authentications handled by the refresh token.
References
Last updated