Securing a Serverless API on Vercel using JWTs
On this page
This code example implements techniques to validate a Self Contained JWT used by a serverless API deployed on Vercel.
Vercel Serverless APIs do not have the ability to cache JWKS keys in memory. By default, this would result in a call to the JWKS endpoint of the Curity Identity Server on every API request which would impact the performance of the system. With a self-contained JWT the API has what it needs to fully validate the JWT without the need for caching or external calls to the Curity Identity Server.
The API is a simple Node.js API that uses the Jose library to perform signature validation using the EmbeddedJWK method. The API then returns a message that the JWT has been successfully validated together with a set of random data.
Prerequisites
- An installation of The Curity Identity Server is needed. Follow the Getting Started Guide to get an installation up and running on a preferred platform.
- A Vercel account and optionally an installation of the Vercel CLI
Version 6.4 or later
The Curity Identity Server 6.4 and later implements new JWT options used in this example.
Create Certificates
Next ensure that the OpenSSL Tool is installed, which is available by default on some operating systems, then run the createCerts.sh
script. This will create a number of self signed certificate files for demo purposes, to simulate a Public Key Infrastructure (PKI) on a development computer:
Certificate | Description | Lifetime |
---|---|---|
signing.p12 | A token signing key + certificate file, with the password from the createCerts.sh script | 6 months |
intermediate.pem | The public key of the intermediate key + certificate used to issue the signing certificate | 10 years |
root.pem | The public key of the root key + certificate used to issue the intermediate certificate | 10 years |
The signing certificate will be imported into the Curity Identity Server, and the intermediate and root certificates will be deployed with the API, which will use a Public Key Infrastructure library to verify the trust chain of received tokens.
Configure Token Signing
In the Curity Identity Server Admin UI, open the Facilities menu and navigate to Keys and Cryptography → Signing. Import the key by clicking on the + next to Signing Keys.
Then view the signing key details and notice that it contains a full certificate chain that will be included in JWTs issued:
Finally, navigate to Profiles
→ Token Service
→ Token Issuers
and select the imported signing certificate, and either activate the Include X509 Certificate Chain
or the Include Jwks
option:
This will result in the JWT header of access tokens being updated with the certificate chain:
{kid: '-1614245140',x5t: 'lDdNIsb3FxulMcYdAXxYJ_Z5950',x5c: ['MIIDqjCCApKgAwIBAgIESLNEvDA ...','MIICwzCCAasCCQCKVy9eKjvi+jA ...','MIIDTDCCAjSgAwIBAgIJAPlnQYH...'],alg: 'RS256'}
Include JWKS
This example code uses the Include JWKS
option and would have to be modified slightly to work with the Include X509 Certificate Chain
(x5c) option. The Securing a Serverless API with JWTs code example implements the x5c option.
Extra Token Validation
When using embedded JWKs it is also important to perform extra validation of the jwt.x5c
field against whitelisted issuers that are deployed together with the API.
The example API loads the certificates data into a truststore.
const trustStore = await this.getWhitelistedCertificateIssuers();
The truststore is then used to verify the certificate chain provided in the received JWT. With this check, the API can trust that the token has been issued by a valid Authorization Server.
Deploy to the Vercel Cloud
Start by cloning the GitHub repository. Install the dependencies, then use for example the Vercel CLI to deploy the code.
npm installvercel --env ISS=https://idsvr.example.com/oauth/v2/oauth-anonymous --env AUD=www --env ALG='RS256' --env CERT_LOCATION='../certs' deploy
The ISS
, AUD
, ALG
and CERT_LOCATION
environment variables are passed as arguments when the API is deployed to Vercel. This could optionally be handled in the Vercel Web UI instead.
Note that AUD
by default is the ID of the client that has issued the JWT that the API receives. This behavior could be changed in the Curity Identity Server.
Optionally add --prod
at the end of the command to directly deploy the API to production.
The result of the deploy command provides a preview URL that can be used to test the deployment.
🔍 Inspect: https://vercel.com/iggbom/serverless-zero-trust-vercel-api/56eiUkJfKdWZjePqLUh8UMwer8rG [1s]✅ Preview: https://serverless-zero-trust-vercel-api.vercel.app [copied to clipboard] [33s]📝 To deploy to production (serverless-zero-trust-vercel-api.vercel.app), run `vercel --prod`
Test the API
Using OAuth Tools, run a Code Flow to obtain a JWT. The Custom Token Issuer article outlies how to configure a specific client in the Curity Identity Server to issue JWTs as access tokens.
Then run an External API
flow in OAuth Tools. Point the request to the Vercel domain and add the /api
path.
The response will note that the JWT validation was successful, and some random data is returned.
{message: "JWT successfully validated",data: "correct adorable amused house"}
Conclusion
Serverless providers such as Vercel deliver convenient internet hosting for APIs, though they come with certain limitations, such as an inability to cache JWKS keys in memory. With self-contained JWTs however, it is possible to implement a performant Zero Trust architecture where the API validates JWTs on every request.
Join our Newsletter
Get the latest on identity management, API Security and authentication straight to your inbox.
Start Free Trial
Try the Curity Identity Server for Free. Get up and running in 10 minutes.
Start Free Trial