/images/resources/code-examples/code-examples-vercel.jpg

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:

CertificateDescriptionLifetime
signing.p12A token signing key + certificate file, with the password from the createCerts.sh script6 months
intermediate.pemThe public key of the intermediate key + certificate used to issue the signing certificate10 years
root.pemThe public key of the root key + certificate used to issue the intermediate certificate10 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 CryptographySigning. Import the key by clicking on the + next to Signing Keys.

Cert Import

Then view the signing key details and notice that it contains a full certificate chain that will be included in JWTs issued:

Cert Chain

Finally, navigate to ProfilesToken ServiceToken Issuers and select the imported signing certificate, and either activate the Include X509 Certificate Chain or the Include Jwks option:

x5c Settings

This will result in the JWT header of access tokens being updated with the certificate chain:

json
12345678910
{
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.

js
1
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.

shell
12
npm install
vercel --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.

shell
123
🔍 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.

OAuth Tools External API

The response will note that the JWT validation was successful, and some random data is returned.

json
1234
{
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