One popular method for securing cloud applications is through the use of JSON Web Tokens (JWTs). JWTs are used to authenticate and authorize users, and cloud providers widely use them to ensure secure access to cloud resources. 

Let’s explore custom JWT generation for cloud solutions.

What are JWTs?

JSON Web Token (JWT) is an open standard that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted since it is digitally signed. JWTs can be signed using a secret key or a public/private key pair.

Signed tokens consist of three parts separated by dots: header, payload, and signature. They can verify the integrity of the claims contained within it, while encrypted tokens hide those claims from other parties. When tokens are signed using public/private key pairs, the signature also proves that only the intended party accessed the contents.

JWTs are useful in several scenarios. Authorization is a common use case for JWTs. Once the user is logged in, each following request will include the JWT, allowing the user to access routes, services, and resources that are permitted with that token. Single Sign-On is a feature that widely uses JWT because of its small overhead and its ability to be easily used across different domains.

Secure information exchange is another scenario where JWTs are useful. Since JWTs can be signed, the party who has signed it can be verified. Additionally, as the signature is calculated using the header and the payload, it can be understood if the data in it has been changed or not.

Identity Providers that Generate JWTs

Several identity providers use JWTs as part of their schemes. Some of the examples are Cognito, Auth0, and Okta:

Cognito lets us add user sign-up, sign-in, and access control to web and mobile apps quickly and easily. Amazon Cognito scales to millions of users and supports sign-in with social identity providers, such as Apple, Facebook, Google, and Amazon, and enterprise identity providers via SAML 2.0 and OpenID Connect. Cognito also supports the use of other providers.

Auth0 issues JWTs as a result of the authentication process. When the user logs in using Auth0, a JWT is created, signed, and sent to the user. Auth0 supports signing JWT with both HMAC and RSA algorithms. This token will be then used to authenticate and authorize with APIs which will grant access to their protected routes and resources. JWTs are also used to perform authentication and authorization in Auth0’s API v2, replacing the traditional usage of regular opaque API keys.

Okta is a customizable, secure, and drop-in solution to add authentication and authorization services to applications. It gets rid of the development overhead, security risks, and maintenance that come from building it in-house.

Also see: The Ultimate Guide for Cloud Migration

Why Custom JWTs are Needed?

Custom JWT generation may be needed and useful for several purposes.

  • During development, they help us understand JWT basics.
  • They may be needed if the functionalities provided by identity providers are not compliant with our system.
  • They allow users to authenticate with an authentication system that is independent of any provider.
  • They allow us to avoid third parties being involved with our applications.

Important Note: One downside of opting for custom JWTs is the overhead. Using custom JWTs requires extra layers of implementation, maintenance, and cost,  so we should decide whether this process is mandatory for architectural design. If it’s not, we should always think about using identity providers for JWT. The following architectural diagram shows the necessary steps to implement custom JWTs efficiently.

Why Custom JWTs are Needed?

To efficiently use custom JWTs, they should be connected to authorizers and services associated with their usage.  In this blog post, we explain how to perform the first step on AWS Cloud.

JWT Generation on AWS Cloud

Before all, assess which claims are needed in the custom JWT. Some options are User ID, username, roles, and expiration time. These claims will be used in the JWT script. Then, generate the necessary keys using Ubuntu.

Install OpenSSL and cryptography libraries. After installing the libraries, generate keys using the following commands:

ssh-keygen -t rsa -b 4096 -m PEM -f jwtRS256.key 
openssl rsa -in jwtRS256.key -pubout -outform PEM -out jwtRS256.key.pub 

These commands output two keys in the form jwt.io require. Then, copy the keys into the local machine. Save the private key into a file named “jwt-key” and copy the public key into a file named “jwt-key-public”. After generating the keys, the private key needs to be recorded in AWS Secrets Manager. To do this, log in to your AWS console, then open the AWS Secrets Manager dashboard. Select ‘Store a new secret’.

AWS Secrets Manager

Then, choose ‘Other type of secret’, and ‘Plaintext’ as the storage option. Paste the generated private key in the box, and do not forget to clear the existing text.

JWS cloud migration

Lastly, give a name to the stored secret. Adding a description or tags is optional but may be preferred for better identification. After finalizing, note down the ARN of the secret. We need this ARN to update our code later.

For AWS Lambda, a ‘.zip’ file containing the necessary libraries and main code needs to be uploaded. An alternative way is to directly upload the compressed file into an S3 bucket via the console. To prepare the necessary file, download authlib and cryptography libraries into a folder.

Next, we will prepare the main code file and add it to this folder before compressing it. First, copy the following code and update the issuer field.

from authlib.jose import jwt
from time import time
import base64 

//Secrets manager code goes here.

def lambda_handler(event, context):
    device_id = event['device_id']
    user_id = event['user_id']
    header = {'alg': 'RS256'}
    payload = {'iss': '<your-name>', 'sub': user_id, 'user_id': user_id, 'device_id': device_id, 'exp': time() + 3600}
    private_key = get_secret()
    s = jwt.encode(header, payload, private_key)
    data = s.decode("utf-8")
    print("success")
    return data

To fill in the secrets manager part of this code, head to the secrets manager console and click on the private key we previously stored. Head to the bottom of the page, then copy the Python code generated by AWS. Update the generated code to fit your needs, then paste it into the corresponding part of the code.

Do not forget to name the code file ‘lambda_function’! After adding the generated code, we are ready to upload our compressed file to lambda.

Carry the ‘.zip’ file into your main machine, then check if the compressed file has extra parent directories. If so, all files which need to be included in the function need to be selected and compressed. Lambda will raise an error if there are extra parent directories, so only compress the necessary files.

After preparing the ‘.zip’ file, head into the lambda console. Select ‘Create function’.

JWT web token

Name your function, select ‘Python 3.9’ from the runtime options, leave the rest as default then hit ‘Create function’. Now, for our function to work correctly, we need to give it enough permissions to access Secrets Manager. Find the newly created function from the lambda dashboard, then click on it. From here, select ‘Configuration’, then ‘Permissions’.

Python JWT

Click on the assigned execution role, this will lead you into the IAM console. From here, select ‘Attach policies’.

JWT console

In this menu, search for ‘SecretsManagerReadWrite’, then check the box next to it and click ‘Attach policy’. Then, head back to the Lambda console, and click ‘Test’.

JWT guide

Set up the test event with device_id and user_id fields. To avoid errors, check if the handler name is lambda_function.lambda_handler. Then, hit the test to obtain the generated JWT. To test the obtained JWT, paste it into jwt.io.

JWT test

The values are correct, but the signature is not yet verified. For verification, paste the generated public key into the corresponding box.

JWT verification

Supplying the JWT and the public key into jwt.io, it can be observed that the signature is now valid.

This article may interest you: Introduction to AWS Cloud Development Kit

Attacking JWTs

We introduce two sample tools to attack our generated JWT: jwt_tool and jwt_hack.

1. jwt_tool can be used to validate, forge, scan, and tamper JWTs. Its functionality includes:

  • Checking the validity of a token,
  • Testing for known exploits,
  • Scanning for misconfigurations or known weaknesses,
  • Fuzzing claim values to provoke unexpected behaviors,
  • Testing the validity of a secret/key file/Public Key/JWKS key,
  • Identifying weak keys via a High-speed Dictionary Attack,
  • Forging new token header and payload contents and creating a new signature with the key or via another attack method,
  • Timestamp tampering,
  • RSA and ECDSA key generation, and reconstruction (from JWKS files).

The sample screenshot from the tool shows the functionalities it features.

2. jwt_hack can be used to crack, decode, and encode JWTs, and create attack payloads. For example, using the ‘payload’ command and supplying a JWT outputs several different JWTs tampered with known exploits. These JWTs can be used to check the correctness and validity of the original via jwt.io or be used in some tests to see if a system is running correctly.

Conclusion

Custom JWT generation is a crucial aspect of cloud solutions that involves authentication and authorization. It provides a flexible and scalable approach to authentication and authorization in cloud solutions. 

In summary, custom JWT generation is a valuable tool for securing cloud solutions by ensuring that only authorized users can access the application. It is an essential consideration for developers who want to build secure and reliable cloud solutions.

In this blog post, we’ve talked about JWT (JSON Web Token) from different points of view. We provided information about what JWT is, JWT use cases, and why we need custom JWT. We give you details about custom JWT generation for your AWS Cloud solutions. Also, we’ve introduced different JWT attacking tools that could be useful for your pentest processes. We hope you enjoyed it!

Check out our Cloud Security services to stay secure!