They solve different problems
API keys answer: “was this caller provisioned to use this gateway?” They’re opaque, fast to verify, easy to revoke, and carry no embedded claims.
JWTs answer: “who is this caller, what are they allowed to do, and when does this assertion expire?” They’re self-describing, claims-bearing, and time-limited.
The common pattern of choosing one or the other misses the opportunity to use each for what it’s actually good at. In a well-structured gateway configuration, you want both:
- An API key (or proxy credential) that authenticates the integration itself to the gateway.
- A JWT that carries the identity and permissions of the human or service behind that integration.
The RequestRocket model supports this naturally
RequestRocket’s credential model has two types: proxy credentials (what callers use to authenticate to the gateway) and target credentials (what the gateway uses to authenticate to the upstream API).
The proxy credential type jwtVerify validates an incoming JWT. But you can also require a proxy-level API key alongside JWT validation by layering rules that check for the presence of both.
Pattern 1: API key on the proxy credential, JWT in a header check
The proxy credential authenticates the integration (API key); a rule checks that a caller-supplied X-User-Auth header — a JWT from your own identity provider — is present for user-context operations:
POST /clients/{clientId}/credentials
{
"credentialType": "proxy",
"credentialAuthType": "key",
"credentialName": "mobile-app-backend",
"credentialRegion": "us-east-1",
"credentialSecret": {
"key": "rr_live_xxxxxxxxxxxxxx"
}
}Then add a rule requiring the JWT header for user-specific endpoints:
POST /clients/{clientId}/proxies/{proxyId}/rules
{
"effect": "allow",
"methods": ["GET", "POST"],
"path": {
"path": { "pattern": "^/v1/user/" },
"presence": "must_exist"
},
"headers": [
{
"name": { "pattern": "^X-User-Auth$" },
"value": { "pattern": ".*" },
"presence": "must_exist"
}
],
"priority": 10,
"notes": "User-scoped endpoints: require X-User-Auth header"
}Pattern 2: jwtVerify proxy credential with additional API key header check
For integrations where the caller should present both a valid JWT (verified against JWKS) and a known API key (as an additional proof of integration identity):
POST /clients/{clientId}/credentials
{
"credentialType": "proxy",
"credentialAuthType": "jwtVerify",
"credentialName": "partner-api-jwt-verify",
"credentialRegion": "us-east-1",
"credentialSecret": {
"jwksUri": "https://partner-auth.example.com/.well-known/jwks.json",
"audience": "https://api.myapp.com",
"issuer": "https://partner-auth.example.com/"
}
}Then add a rule that also requires the partner’s static API key in a custom header:
POST /clients/{clientId}/proxies/{proxyId}/rules
{
"effect": "allow",
"methods": ["GET", "POST"],
"path": {
"path": { "pattern": ".*" },
"presence": "must_exist"
},
"headers": [
{
"name": { "pattern": "^X-Partner-Key$" },
"value": { "pattern": "^partner_key_xxxxxxxxxxxx$" },
"presence": "must_exist"
}
],
"priority": 5,
"notes": "Partner integration: require static partner key alongside JWT"
}Now a request must: (a) carry a valid JWT verified against the partner’s JWKS, AND (b) include the expected static partner key. Compromising one factor alone doesn’t grant access.
Using JWT claims to augment API key access
Even when your proxy credential is a simple key type (no JWT verification), you can pass a JWT in a header and use the filter token system to make decisions based on its claims — provided you’ve also attached a jwtVerify credential somewhere in the flow, or you’re passing the token as additional context.
A more common pattern: use the JWT claims to vary what the API key-authenticated caller can access:
POST /clients/{clientId}/proxies/{proxyId}/filters
{
"token": [
{
"claim": { "pattern": "^tier$" },
"value": { "pattern": "^(premium|enterprise)$" },
"presence": "must_absent"
}
],
"requestPath": {
"path": { "pattern": "^/v1/export" },
"presence": "must_exist"
},
"operations": [
{
"effect": "destroy",
"jsonPath": { "pattern": ".*" },
"notes": "Block export endpoint — premium or enterprise tier required in JWT"
}
],
"notes": "Tier gate: export requires premium/enterprise claim in JWT"
}The upstream side: key to the vendor, JWT to your backend
A common full-stack configuration looks like this:
| Layer | Auth mechanism | RequestRocket role |
|---|---|---|
| Caller → Gateway | jwtVerify proxy credential | Validates JWT, checks claims |
| Gateway → Upstream vendor API | bearer or key target credential | Injects vendor token |
| Gateway → Your own backend | basic or key target credential | Injects service account key |
The caller authenticates with a JWT (issued by your identity provider). The upstream APIs receive their own credentials, injected by the gateway. Your backend receives the forwarded request with the caller’s JWT claims available in headers — without needing to trust the caller or re-verify the token.
When layering isn’t necessary
Not every proxy needs both mechanisms. Internal service-to-service calls between trusted services on your own infrastructure usually don’t need JWT claim checking — a proxy API key is sufficient. Save the JWT claim layer for:
- Public-facing API endpoints where caller identity matters.
- Partner integrations where you need to verify both integration identity (key) and user identity (JWT).
- Multi-tenant APIs where JWT tenant claims drive access control.
Next steps
Both credential types and the full filter rule system are available on every proxy. Read the credential and filter documentation to see how to configure layered auth for your specific integration pattern, or start for free.