---
title: "Add JWT Authentication to Your Vapor API"
description: "Secure your Vapor API using JWT authentication with JWKS."
canonicalUrl: "https://zuplo.com/use-cases/api-key-auth/swift/vapor/jwt-backend"
framework: "Vapor"
language: "Swift"
authStrategy: "JWT with JWKS"
pageType: use-case
---

# Add JWT Authentication to Your Vapor API

Secure your Vapor API using JWT authentication with JWKS.

## How Zuplo Handles It

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

## Vapor Backend Code

```swift
import Vapor
import JWT

struct JWTPayload: JWTPayload {
    var sub: SubjectClaim
    var iss: IssuerClaim
    var exp: ExpirationClaim

    func verify(using signer: JWTSigner) throws {
        try exp.verifyNotExpired()
    }
}

func getKey(from jwksURI: String, kid: String) -> EventLoopFuture<JWK> {
    let client = HTTPClient(eventLoopGroupProvider: .createNew)
    defer { try? client.syncShutdown() }

    return client.get(url: "\(jwksURI)/.well-known/jwks.json").flatMapThrowing { response in
        guard let body = response.body else { throw Abort(.unauthorized, reason: "No JWKS data") }
        let jwks = try JSONDecoder().decode(JWKSet.self, from: body)
        guard let key = jwks.keys.filter({ $0.keyIdentifier == kid }).first else {
            throw Abort(.unauthorized, reason: "Key ID not found")
        }
        return key
    }
}

func validateJwtMiddleware(jwksURI: String) -> Middleware {
    return JWTMiddleware<JWTPayload> { jwt, request in
        getClient(for: request).flatMap { key in
            let signer = JWTSigner.rs256(key: key)
            return request.application.jwt.verify(jwt, as: JWTPayload.self, using: signer)
        }
    }
}

func getClient(for request: Request) -> EventLoopFuture<JWK> {
    guard let kid = request.headers.bearerAuthorization?.token else {
        return request.eventLoop.makeFailedFuture(Abort(.unauthorized, reason: "No token provided"))
    }
    return getKey(from: "https://my-api-a32f34.zuplo.api/__zuplo/issuer", kid: kid)
}

public func configure(_ app: Application) throws {
    app.middleware.use(validateJwtMiddleware(jwksURI: "https://my-api-a32f34.zuplo.api/__zuplo/issuer"))

    app.get("protected") { req -> String in
        // Access granted if this point is reached
        return "Access granted"
    }
}
```

## 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)
