---
title: "Add JWT Authentication to Your Apache HTTP Server API"
description: ""
canonicalUrl: "https://zuplo.com/use-cases/api-key-auth/c/apachehttpserver/jwt-backend"
framework: "Apache HTTP Server"
language: "C"
authStrategy: "JWT with JWKS"
pageType: use-case
---

# Add JWT Authentication to Your Apache HTTP Server API



## How Zuplo Handles It

Let Zuplo issue short-lived JWTs signed with a JWKS your Apache HTTP Server backend can verify — no long-lived API keys touch your origin.

## Apache HTTP Server Backend Code

```c
#include <httpd.h>
#include <http_config.h>
#include <http_protocol.h>
#include <http_request.h>
#include <apr_json.h>
#include <ap_log.h>
#include <jwt.h>
#include <curl/curl.h>

#define ISSUER "https://my-api-a32f34.zuplo.api/__zuplo/issuer"
#define JWKS_URI ISSUER "/.well-known/jwks.json"

// Function to fetch JWKS
static size_t jwks_write_callback(void *contents, size_t size, size_t nmemb, void *userp) {
    ((char *)userp)[nmemb] = '\0';
    strcat(userp, contents);
    return size * nmemb;
}

static char *fetch_jwks(apr_pool_t *pool, const char *url) {
    CURL *curl;
    CURLcode res;
    char *jwks = apr_pcalloc(pool, CURL_MAX_WRITE_SIZE);

    curl_global_init(CURL_GLOBAL_DEFAULT);
    curl = curl_easy_init();
    if(curl) {
        curl_easy_setopt(curl, CURLOPT_URL, url);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, jwks_write_callback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, jwks);
        res = curl_easy_perform(curl);
        curl_easy_cleanup(curl);
    }
    curl_global_cleanup();
    return jwks;
}

// Middleware to validate JWT
static int validate_jwt(request_rec *r) {
    const char *auth_header = apr_table_get(r->headers_in, "Authorization");
    if (!auth_header) {
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "No token provided");
        return HTTP_UNAUTHORIZED;
    }

    const char *token = auth_header + 7; // Skip "Bearer "
    char *jwks = fetch_jwks(r->pool, JWKS_URI);

    jwt_t *jwt = NULL;
    jwt_alg_t alg = JWT_ALG_RS256;
    if (jwt_decode(&jwt, token, (unsigned char *)jwks, strlen(jwks)) != 0) {
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Invalid token");
        return HTTP_UNAUTHORIZED;
    }

    const char *jwt_issuer = jwt_get_grant(jwt, "iss");
    if (!jwt_issuer || strcmp(jwt_issuer, ISSUER) != 0) {
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Issuer mismatch");
        jwt_free(jwt);
        return HTTP_UNAUTHORIZED;
    }

    // Token is valid, attach user info
    r->user = apr_pstrdup(r->pool, jwt_issuer);
    jwt_free(jwt);
    return OK;
}

// Example protected handler
static int protected_handler(request_rec *r) {
    if (strcmp(r->handler, "protected-handler")) {
        return DECLINED;
    }

    int check = validate_jwt(r);
    if (check != OK) {
        return check;
    }

    ap_set_content_type(r, "application/json");
    ap_rprintf(r, "{\"message\":\"Access granted\",\"user\":\"%s\"}", r->user);
    return OK;
}

// Register hooks
static void register_hooks(apr_pool_t *pool) {
    ap_hook_handler(protected_handler, NULL, NULL, APR_HOOK_MIDDLE);
}

module AP_MODULE_DECLARE_DATA jwt_auth_module = {
    STANDARD20_MODULE_STUFF,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    register_hooks
};
```

## Example Request

```bash
curl -X GET \
  'https://your-api.zuplo.dev/your-route' \
  -H 'Authorization: Bearer YOUR_API_KEY'
```

## Learn More

- [API Key Authentication on Zuplo](https://zuplo.com/docs/policies/api-key-auth-inbound)
- [JWT Authentication on Zuplo](https://zuplo.com/docs/policies/open-id-jwt-auth-inbound)
- [All use cases](https://zuplo.com/use-cases)
