diff --git a/.gitignore b/.gitignore
index 997ca2f..f345d3d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
-.vagrant
\ No newline at end of file
+.vagrant
+tmp
\ No newline at end of file
diff --git a/README.md b/README.md
index d5229e6..37ff974 100644
--- a/README.md
+++ b/README.md
@@ -50,6 +50,19 @@ with a space and surround it with double quotes:
 setenv OAUTH_AUDIENCE "https://api.mywebsite.com https://api2.mywebsite.com"
 ```
 
+## Support for multiple keys
+
+This library support specifying multiple keys values in the JWT token. They should be specified as a JSON array of strings.
+You can also accept multiple audience values in the `OAUTH_KID` and `OAUTH_PUBKEY_PATH` environment variables in the **haproxy.cfg** file. Separate each value
+with a space and surround it with double quotes:
+
+```
+setenv OAUTH_PUBKEY_PATH "/etc/haproxy/pem/pubkey.pem /etc/haproxy/pem/pubkey2.pem"
+setenv OAUTH_KID "key1 key2"
+```
+
+Make sure that the order of the paths to the keys matches the index of the relevant identifier.
+
 ## Output variables
 
 After calling `http-request lua.jwtverify`, you get access to variables for each of the claims in the token.
@@ -74,16 +87,16 @@ Try it out using the Docker Compose.
    | permission  | description           |
    |-------------|-----------------------|
    | read:myapp  | Read access to my app |
-   | write:myapp | Write access to myapp | 
+   | write:myapp | Write access to myapp |
 
 1. Now that you have an API defined in Auth0, add an application that is allowed to authenticate to it. Go to the "Applications" tab and add a new "Machine to Machine Application" and select the API you just created. Give it the "read:myapp" and "write:myapp"permissions (or only one or the other).
 1. On the Settings page for the new application, go to **Advanced Settings > Certificates** and download the certificate in PEM format. HAProxy will validate the access tokens against this certificate, which was signed by the OAuth provider, Auth0.
 
 1. Convert it first using `openssl x509 -pubkey -noout -in ./mycert.pem > pubkey.pem` and save **pubkey.pem** to **/example/haproxy/pem/pubkey.pem**.
-1. Edit **example/haproxy/haproxy.cfg**: 
+1. Edit **example/haproxy/haproxy.cfg**:
 
-   * replace the `OAUTH_ISSUER` variable in the global section with the Auth0 domain URL with your own, such as https://myaccount.auth0.com/. 
-   * replace the `OAUTH_AUDIENCE` variable with your API name in Auth0, such as "https://api.mywebsite.com". 
+   * replace the `OAUTH_ISSUER` variable in the global section with the Auth0 domain URL with your own, such as https://myaccount.auth0.com/.
+   * replace the `OAUTH_AUDIENCE` variable with your API name in Auth0, such as "https://api.mywebsite.com".
    * replace the `OAUTH_PUBKEY_PATH` variable with the path to your PEM certificate. (also update the docker-compose file)
 
 1. Create the environment with Docker Compose:
diff --git a/docker-compose.ubuntu.example.yml b/docker-compose.ubuntu.example.yml
index 6ce31ca..919073d 100644
--- a/docker-compose.ubuntu.example.yml
+++ b/docker-compose.ubuntu.example.yml
@@ -17,7 +17,9 @@ services:
     volumes:
        - ./example/haproxy/haproxy.cfg:/etc/haproxy/haproxy.cfg
        - ./example/haproxy/pem/pubkey.pem:/etc/haproxy/pem/pubkey.pem
+       - ./example/haproxy/pem/pubkey2.pem:/etc/haproxy/pem/pubkey2.pem
        - ./example/haproxy/pem/test.com.pem:/etc/haproxy/pem/test.com.pem
+       - ./lib/jwtverify.lua:/usr/local/share/lua/5.4/jwtverify.lua
     ports:
     - "80:80"
     - "443:443"
diff --git a/example/haproxy/haproxy.cfg b/example/haproxy/haproxy.cfg
index 2415762..9aba837 100644
--- a/example/haproxy/haproxy.cfg
+++ b/example/haproxy/haproxy.cfg
@@ -10,7 +10,9 @@ global
     # Replace the Auth0 URL with your own:
     setenv OAUTH_ISSUER https://youraccount.auth0.com/
     setenv OAUTH_AUDIENCE https://api.mywebsite.com
-    setenv OAUTH_PUBKEY_PATH /etc/haproxy/pem/pubkey.pem
+    # Note that that you can use multiple keys, just make sure that kid length matches the number of keys
+    setenv OAUTH_PUBKEY_PATH "/etc/haproxy/pem/pubkey.pem /etc/haproxy/pem/pubkey2.pem"
+    setenv OAUTH_KID "key1 key2"
 
 defaults
     log global
@@ -29,7 +31,7 @@ frontend api_gateway
     http-request deny unless { var(txn.authorized) -m bool }
     http-request deny if { path_beg /api/myapp } { method GET }             ! { var(txn.oauth.scope) -m sub read:myapp }
     http-request deny if { path_beg /api/myapp } { method POST PUT DELETE } ! { var(txn.oauth.scope) -m sub write:myapp }
-    
+
 backend apiservers
     balance roundrobin
     server server1 server1:80
diff --git a/example/haproxy/pem/pubkey.pem b/example/haproxy/pem/pubkey.pem
index 77c09c8..6bc6e8d 100644
--- a/example/haproxy/pem/pubkey.pem
+++ b/example/haproxy/pem/pubkey.pem
@@ -1,9 +1,14 @@
 -----BEGIN PUBLIC KEY-----
-MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvIL8bebCh+pi68Rt0CCu
-104VqR10kuD0E1yzwaywvaEiyhfUeDDKAyKC8yS5ilu9xyWK/pg/84RiWq7WoqhU
-m8L06jtknn/ZCOuyUdkn1QcdOG10lbbrUF1AOduTIvFYyT4zHrIcKt6MyeQUO0kH
-cXQU7lvM2C62BboAasZFupDts1m1kPZMWaiSjLrE1eruhl8NrfipiPWMZJSJoYCQ
-cmtN3REXk9z8X7ZPgcMJ9hNN+Kv0fTYLZI4wS4TpHscVfbK18cL4uLrTCcip7jNe
-y2KZ/YdbeHgmmcQAdiB4veH4I2dAyqIdsy8Jk+KTs3Ae8qp+S3XtC8z/uXMbN7lR
-AwIDAQAB
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2Z1w2ZZ0oHnpxYX3Nnee
+zVwI4fHtOQTdNz9vek6QZlkl4JY9yxcVfG8ssJq0k55po4MelioNyybpR4+9AE/Q
+PwGUm7xEsAjlP/BAmpTz5PAeBAMd1sn+frHgKaICfQO6feob9/JaLo2ixRN0zzPd
+5dhUrRaW/Az5a4mcXLSEGUAtIgtCg+ZnXra5Bn503xSOqOJkp8R2qnozmsVidAeL
+/bOMe/Yb7QpDjJgv565G3gbbzcLn6+IG8IWnXNxLD7C6mcPA0yS3MrJUpRFRzWrW
+MMPWOvBHWCm/2NnNQGHGSpImQ/BZ1DOoFqXO6DRuZpYiBzE/H74ojaatfoxSzpJz
+618j4u3CcTKwYafkdXXbUoSMK9FWXdCsSVppXqBLIOtaS2MZMtnuUP23EucoK65l
+BSUiuQ7gztzuDTKSLjlm3oMS2Z6Z3j0e0dHXnNZQTZKvhjjTyi65n3Xq7EBhNsbp
+r6zN4RGbimoliRvyuNQpY9ottbB5Md2PkNRx2WwMtOEMCNCDvmSMwJ7SeUEcs0n+
+J7v4WyEA5TH2OYdwRPOfyAbZbxyP8ZEICCe6Xhn8QKVZO5nTIHzuQuHW5z8Q9gA/
+3waj/ksN9CGP211lRCxDf2iINw1EkPYjeWM5MAr1N1BwyhItqbP3DAIsajFb6CtY
+dZKcoUh45cl6d6DaXgWq4L8CAwEAAQ==
 -----END PUBLIC KEY-----
diff --git a/example/haproxy/pem/pubkey2.pem b/example/haproxy/pem/pubkey2.pem
new file mode 100644
index 0000000..6922cba
--- /dev/null
+++ b/example/haproxy/pem/pubkey2.pem
@@ -0,0 +1,14 @@
+-----BEGIN PUBLIC KEY-----
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAu9TovLhy2PFHJ6U3+lDj
+8uiqtigAJ+cdBTbe4NotSJiNKikZlUSzJElIwAYXeHpRsDuCml76e0axB22XWGZ1
+QPII/2Etd6ZjWu5mp27i9EqpJHnd5xpdeNnBUf1KWH2uVUJjPVEZWAU1rqVl/FhI
+owRyjqq3KtZo36u4ZD3264SOMXzIZIn4+dDuwNavGUen0mug+r3istTa5fQy8DVu
+DhSU24MBLKQwAlNOWfUUf/h9SqpE25w2mehJwhJ+qXP0OwlzCw0tJIjcSkycrB02
++xF9ucALZzZgX72et242gIak0p7NkRcjuYWPMhhmvrkVXgz4XSfszHIlgnvD+hG9
+EZOTgq2hL7O1BHB4FIyQrZBtCT/Q7pcjlHb0VMiRW2tv4GYW1tiSnf2Tww3nJOwf
+YetCSuT0zhamEC7LEQFlju9ZZvQThTtXrEYhryp+Tw2UxEsUtiiVFcncX0St4lw8
+XBzQkIUOsUDdVHenpdfPqTLsFZ1CwydX7DmQX1tVDHY2J2jQGK2ZBJrNWPD6SlWA
+7hDMaTZykTiVdQXGczVqHJi8YUB8YwBffLOHxF+TISF6Nj+6eB9DXmXmAyVsHaYu
+5TEZccPOGvSRKzUJo/vwusZ8pmSZigaaD42M+xqWKxYlvx1Mli8lBtA9Q3jMotSl
+YEAQiWgiAK/Vev1vo3i2sf8CAwEAAQ==
+-----END PUBLIC KEY-----
diff --git a/lib/jwtverify.lua b/lib/jwtverify.lua
index c3086d0..6bcb0dc 100644
--- a/lib/jwtverify.lua
+++ b/lib/jwtverify.lua
@@ -25,6 +25,7 @@ if not config then
   config = {
       debug = true,
       publicKey = nil,
+      kid = nil,
       issuer = nil,
       audience = nil,
       hmacSecret = nil
@@ -130,7 +131,23 @@ local function algorithmIsValid(token)
   return true
 end
 
-local function rs256SignatureIsValid(token, publicKey)
+local function rs256SignatureIsValid(token, keys, kids)
+  -- Check if a kid if provided, if so verify it exists in the kids array
+  local token_kid = token.headerdecoded.kid
+  local publicKey
+  if kids ~= nil then
+    if not contains(kids, token_kid) then
+      log("The kid provided in the token (" .. token_kid .. ") does not match the kid provided in the configuration.")
+      return false
+    end
+
+    -- get the key from the keys list at the index of the correct kid
+    publicKey = keys[token_kid]
+  else
+    -- if no kid is provided, use the first key in the list
+    publicKey = keys[next(keys)]
+  end
+
   local digest = openssl.digest.new('SHA256')
   digest:update(token.header .. '.' .. token.payload)
   local vkey = openssl.pkey.new(publicKey)
@@ -160,10 +177,10 @@ end
 
 -- Checks if the audience in the token is listed in the
 -- OAUTH_AUDIENCE environment variable. Both the token audience
--- and the environment variable can contain multiple audience values, 
+-- and the environment variable can contain multiple audience values,
 -- separated by commas. Each value will be checked.
 local function audienceIsValid(token, expectedAudienceParam)
-  
+
   -- Convert OAUTH_AUDIENCE environment variable to a table,
   -- even if it contains only one value
   local expectedAudiences = expectedAudienceParam
@@ -172,8 +189,14 @@ local function audienceIsValid(token, expectedAudienceParam)
     expectedAudiences = core.tokenize(expectedAudienceParam, " ")
   end
 
-  -- Convert 'aud' claim to a table, even if it contains only one value
   local receivedAudiences = token.payloaddecoded.aud
+
+  -- Check if 'aud' exists and handle cases where it's missing
+  if receivedAudiences == nil then
+    return false
+  end
+
+  -- Convert 'aud' claim to a table, even if it contains only one value
   if type(token.payloaddecoded.aud) == "string" then
     receivedAudiences ={}
     receivedAudiences[1] = token.payloaddecoded.aud
@@ -195,7 +218,8 @@ local function setVariablesFromPayload(txn, decodedPayload)
 end
 
 local function jwtverify(txn)
-  local pem = config.publicKey
+  local keys = config.publicKeys
+  local kid = config.kid
   local issuer = config.issuer
   local audience = config.audience
   local hmacSecret = config.hmacSecret
@@ -219,7 +243,7 @@ local function jwtverify(txn)
 
   -- 3. Verify the signature with the certificate
   if token.headerdecoded.alg == 'RS256' then
-    if rs256SignatureIsValid(token, pem) == false then
+    if rs256SignatureIsValid(token, keys, kid) == false then
       log("Signature not valid.")
       goto out
     end
@@ -271,18 +295,44 @@ end
 core.register_init(function()
   config.issuer = os.getenv("OAUTH_ISSUER")
   config.audience = os.getenv("OAUTH_AUDIENCE")
-  
+
+  -- when using multiple keys, parse the kid list
+  local kid = os.getenv("OAUTH_KID")
+  if kid ~= nil then
+    config.kid = core.tokenize(kid, " ")
+  end
+
   -- when using an RS256 signature
-  local publicKeyPath = os.getenv("OAUTH_PUBKEY_PATH") 
+  local publicKeyPath = os.getenv("OAUTH_PUBKEY_PATH")
   if publicKeyPath ~= nil then
-    local pem = readAll(publicKeyPath)
-    config.publicKey = pem
+    -- tokenize the path in case multiple keys are provided
+    keyPaths = core.tokenize(publicKeyPath, " ")
+
+    -- Check if there is more than one file path then we must have kid identifiers
+    if #keyPaths > 1 and config.kid == nil then
+      log("Multiple public keys provided but no key identifiers.")
+      return
+    end
+
+    -- Make sure that the kid size matches the keyPaths size
+    if config.kid ~= nil and #config.kid ~= #keyPaths then
+      log("The number of keys does not match the number of key identifiers.")
+      return
+    end
+
+    -- Read all the keys and store them in the config
+    config.publicKeys = {}
+    for i, keyPath in ipairs(keyPaths) do
+      local pem = readAll(keyPath)
+      config.publicKeys[config.kid[i]] = pem
+    end
   end
-  
+
   -- when using an HS256 or HS512 signature
   config.hmacSecret = os.getenv("OAUTH_HMAC_SECRET")
-  
+
   log("PublicKeyPath: " .. (publicKeyPath or "<none>"))
+  log("KeyIdentifiers: " .. (kid or "<none>"))
   log("Issuer: " .. (config.issuer or "<none>"))
   log("Audience: " .. (config.audience or "<none>"))
 end)
diff --git a/test.sh b/test.sh
new file mode 100755
index 0000000..972aa52
--- /dev/null
+++ b/test.sh
@@ -0,0 +1,316 @@
+#!/bin/bash
+
+# Function to wait for containers to be up
+wait_for_containers() {
+    # Wait for at least two containers to be up and running
+    echo "Waiting for at least 2 containers to be running..."
+    while true; do
+        container_count=$(docker compose -f "$COMPOSE_FILE" ps -q | wc -l)
+        if [ "$container_count" -ge 2 ]; then
+            echo "At least 2 containers are running."
+            break
+        fi
+        echo "Currently $container_count containers are running. Waiting..."
+        sleep 2  # Wait for 2 seconds before checking again
+    done
+
+    # Now check each container's status
+    local service_names=$(docker compose -f "$COMPOSE_FILE" ps --services)
+    for service in $service_names; do
+        echo "Waiting for $service to be up..."
+        while true; do
+            status=$(docker compose -f "$COMPOSE_FILE" ps "$service" | grep "$service" | grep "Up")
+            if [ -n "$status" ]; then
+                echo "$service is up!"
+                break
+            fi
+            sleep 2  # Wait for 2 seconds before checking again
+        done
+    done
+}
+
+# Log function with color and timestamp
+log() {
+    local GREEN="\033[0;32m"
+    local YELLOW="\033[1;33m"
+    local RED="\033[0;31m"
+    local NC="\033[0m"  # No Color
+
+    # Get log level and message
+    local log_level=$1
+    shift
+    local log_message="$@"
+
+    # Get current date and time
+    local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
+
+    # Set color based on log level
+    case $log_level in
+        DEBUG)
+            echo -e "${NC}[$timestamp] [INFO] ${log_message}${NC}"
+            ;;
+        INFO)
+            echo -e "${GREEN}[$timestamp] [INFO] ${log_message}${NC}"
+            ;;
+        WARNING)
+            echo -e "${YELLOW}[$timestamp] [WARNING] ${log_message}${NC}"
+            ;;
+        ERROR)
+            echo -e "${RED}[$timestamp] [ERROR] ${log_message}${NC}"
+            ;;
+        *)
+            echo -e "[$timestamp] [UNKNOWN] ${log_message}"
+            ;;
+    esac
+}
+
+base64_url_encode() {
+    openssl base64 -e -A | tr '+/' '-_' | tr -d '='
+}
+
+create_jwt() {
+    local header=$1
+    local payload=$2
+    local private_key=$3
+
+    local encoded_header=$(echo -n "$header" | base64_url_encode)
+    local encoded_payload=$(echo -n "$payload" | base64_url_encode)
+
+    local unsigned_token="${encoded_header}.${encoded_payload}"
+    local signature=$(echo -n "$unsigned_token" | openssl dgst -sha256 -sign "$private_key" | base64_url_encode)
+
+    echo "${unsigned_token}.${signature}"
+}
+
+# Constants
+WORKDIR="$(pwd)"
+TMP_DIR="${WORKDIR}/tmp"
+PRIVATE_KEY="${TMP_DIR}/private.pem"
+PUBLIC_KEY="${TMP_DIR}/public.pem"
+CERT_FILE="${TMP_DIR}/cert.pem"
+PRIVATE2_KEY="${TMP_DIR}/private2.pem"
+PUBLIC2_KEY="${TMP_DIR}/public2.pem"
+CERT2_FILE="${TMP_DIR}/cert2.pem"
+COMPOSE_FILE="${WORKDIR}/docker-compose.ubuntu.example.yml"
+
+mkdir -p ${TMP_DIR}
+
+## Setup
+generate_keys() {
+    log INFO "Generating keys..."
+    openssl req -x509 \
+        -newkey rsa:4096 \
+        -keyout "${PRIVATE_KEY}" \
+        -out "${CERT_FILE}" \
+        -days 365 \
+        -nodes \
+        -subj "/CN=youraccount.auth0.com"
+    openssl x509 -pubkey -noout -in "${CERT_FILE}" > "${PUBLIC_KEY}"
+    openssl req -x509 \
+        -newkey rsa:4096 \
+        -keyout "${PRIVATE2_KEY}" \
+        -out "${CERT2_FILE}" \
+        -days 365 \
+        -nodes \
+        -subj "/CN=youraccount.auth0.com"
+    openssl x509 -pubkey -noout -in "${CERT2_FILE}" > "${PUBLIC2_KEY}"
+}
+
+generate_keys
+
+
+log INFO "Overriding the public key used to verify the signature"
+cp "${PUBLIC_KEY}" "${WORKDIR}/example/haproxy/pem/pubkey.pem"
+cp "${PUBLIC2_KEY}" "${WORKDIR}/example/haproxy/pem/pubkey2.pem"
+
+log info "Take down any old project"
+docker compose -f ${COMPOSE_FILE} down || true
+
+log INFO "Start the compose project"
+docker compose -f ${COMPOSE_FILE} up -d
+docker compose -f ${COMPOSE_FILE} logs -f &
+
+log INFO "Waiting for containers to go up"
+wait_for_containers
+
+###### Tests ######
+test_sanity() {
+    # Header for RS256 JWT
+    header='{
+    "alg": "RS256",
+    "typ": "JWT",
+    "kid": "key1"
+    }'
+
+    log DEBUG Payload \(modify as needed\)
+    payload='{
+    "iss": "https://youraccount.auth0.com/",
+    "aud": "https://api.mywebsite.com",
+    "sub": "user@example.com",
+    "exp": '$(($(date +%s) + 3600))',
+    "scope": "read:myapp"
+    }'
+
+    log DEBUG Create jwt
+    jwt=$(create_jwt "$header" "$payload" "$PRIVATE_KEY")
+
+    log DEBUG "Testing the API"
+    curl --request GET \
+        -k --fail \
+        --url https://localhost/api/myapp \
+        --header "authorization: Bearer ${jwt}"
+
+    # We expect the API to pass
+    [[ $? -eq 0 ]]
+}
+
+test_no_audience() {
+    # Header for RS256 JWT
+    header='{
+    "alg": "RS256",
+    "typ": "JWT",
+    "kid": "key1"
+    }'
+
+    log DEBUG Payload \(modify as needed\)
+    payload='{
+    "iss": "https://youraccount.auth0.com/",
+    "sub": "user@example.com",
+    "exp": '$(($(date +%s) + 3600))',
+    "scope": "read:myapp"
+    }'
+
+    log DEBUG Create jwt
+    jwt=$(create_jwt "$header" "$payload" "$PRIVATE_KEY")
+
+    log DEBUG "Testing the API"
+    curl --request GET \
+        -k --fail \
+        --url https://localhost/api/myapp \
+        --header "authorization: Bearer ${jwt}"
+
+    # We expect the API to return an error
+    [[ $? -ne 0 ]]
+}
+
+test_incorrect_key_id() {
+    # Header for RS256 JWT
+    header='{
+    "alg": "RS256",
+    "typ": "JWT",
+    "kid": "key195"
+    }'
+
+    log DEBUG Payload \(modify as needed\)
+    payload='{
+    "iss": "https://youraccount.auth0.com/",
+    "aud": "https://api.mywebsite.com",
+    "sub": "user@example.com",
+    "exp": '$(($(date +%s) + 3600))',
+    "scope": "read:myapp"
+    }'
+
+    log DEBUG Create jwt
+    jwt=$(create_jwt "$header" "$payload" "$PRIVATE_KEY")
+
+    log DEBUG "Testing the API"
+    curl --request GET \
+        -k --fail \
+        --url https://localhost/api/myapp \
+        --header "authorization: Bearer ${jwt}"
+
+    # We expect the API to return an error
+    [[ $? -ne 0 ]]
+}
+
+test_second_key_usage_pass() {
+    # Header for RS256 JWT
+    header='{
+    "alg": "RS256",
+    "typ": "JWT",
+    "kid": "key2"
+    }'
+
+    log DEBUG Payload \(modify as needed\)
+    payload='{
+    "iss": "https://youraccount.auth0.com/",
+    "aud": "https://api.mywebsite.com",
+    "sub": "user@example.com",
+    "exp": '$(($(date +%s) + 3600))',
+    "scope": "read:myapp"
+    }'
+
+    log DEBUG Create jwt
+    jwt=$(create_jwt "$header" "$payload" "$PRIVATE2_KEY")
+
+    log DEBUG "Testing the API"
+    curl --request GET \
+        -k --fail \
+        --url https://localhost/api/myapp \
+        --header "authorization: Bearer ${jwt}"
+
+    # We expect the API to pass
+    [[ $? -eq 0 ]]
+}
+
+test_second_key_usage_fail() {
+    # Header for RS256 JWT
+    header='{
+    "alg": "RS256",
+    "typ": "JWT",
+    "kid": "key"
+    }'
+
+    log DEBUG Payload \(modify as needed\)
+    payload='{
+    "iss": "https://youraccount.auth0.com/",
+    "aud": "https://api.mywebsite.com",
+    "sub": "user@example.com",
+    "exp": '$(($(date +%s) + 3600))',
+    "scope": "read:myapp"
+    }'
+
+    log DEBUG Create jwt
+    jwt=$(create_jwt "$header" "$payload" "$PRIVATE2_KEY")
+
+    log DEBUG "Testing the API"
+    curl --request GET \
+        -k --fail \
+        --url https://localhost/api/myapp \
+        --header "authorization: Bearer ${jwt}"
+
+    # We expect the API to return an error
+    [[ $? -ne 0 ]]
+}
+
+###### Run tests ######
+# Define the array of test functions
+tests=("test_sanity" "test_no_audience" "test_incorrect_key_id" "test_second_key_usage_pass" "test_second_key_usage_fail")
+all_tests_passed=true
+
+# Loop through the array of test functions and run them
+for test in "${tests[@]}"; do
+    log INFO "Running $test..."
+    $test  # Call the test function
+
+    # Check the result of the test
+    if [ $? -eq 0 ]; then
+        log INFO "$test passed"
+    else
+        log ERROR "$test failed"
+        all_tests_passed=false
+    fi
+done
+
+# Terminate the compose project
+log INFO "Take down any old project"
+docker compose -f ${COMPOSE_FILE} down || true
+
+# Check if all tests passed
+if [ "$all_tests_passed" = true ]; then
+    log INFO "All tests passed. The run was successful."
+    exit 0
+else
+    log ERROR "Some tests failed. The run failed."
+    exit 1
+fi
\ No newline at end of file