---
title: "Add JWT Authentication to Your undefined API"
description: undefined
canonicalUrl: "https://zuplo.com/use-cases/api-key-auth/swift/kitura/jwt-backend"
framework: undefined
language: undefined
authStrategy: "JWT with JWKS"
pageType: use-case
---

# Add JWT Authentication to Your undefined API



## How Zuplo Handles It

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

## undefined Backend Code

```swift
import Kitura
import KituraJWT
import LoggerAPI
import SwiftJWT
import Foundation

let router = Router()

// Constants
let issuer = "https://my-api-a32f34.zuplo.api/__zuplo/issuer"
let jwksUri = "\(issuer)/.well-known/jwks.json"

// JWKS Manager
class JWKSManager {
    private var keys: [String: JWK] = [:]
    private let url: URL
    private let cacheMaxAge: TimeInterval = 600 // 10 minutes in seconds

    init(jwksUri: String) {
        self.url = URL(string: jwksUri)!
        refreshKeys()
        Timer.scheduledTimer(withTimeInterval: cacheMaxAge, repeats: true) { _ in
            self.refreshKeys()
        }
    }

    private func refreshKeys() {
        let task = URLSession.shared.dataTask(with: self.url) { data, response, error in
            guard let data = data, error == nil else {
                Log.error("Failed to fetch JWKS: \(String(describing: error))")
                return
            }

            do {
                let jwks = try JSONDecoder().decode(JWKSet.self, from: data)
                self.keys = jwks.keys.reduce(into: [:]) { $0[$1.kid!] = $1 }
            } catch {
                Log.error("Failed to decode JWKS: \(error)")
            }
        }
        task.resume()
    }

    func getKey(kid: String) -> JWK? {
        return keys[kid]
    }
}

let jwksManager = JWKSManager(jwksUri: jwksUri)

// Middleware to validate JWT
func validateJwtMiddleware(request: RouterRequest, response: RouterResponse, next: @escaping () -> Void) {
    guard let authorizationHeader = request.headers["Authorization"], authorizationHeader.hasPrefix("Bearer ") else {
        response.status(.unauthorized).send(json: ["error": "No token provided"])
        return
    }

    let token = String(authorizationHeader.dropFirst("Bearer ".count))
    let jwtVerifier = JWTVerifier

    do {
        let decoded = try JWT<ClaimsJWT>(jwtString: token)
        guard let header = decoded.header as? JSONWebHeader, let keyId = header.kid, let jwk = jwksManager.getKey(kid: keyId) else {
            response.status(.unauthorized).send(json: ["error": "Invalid token"])
            return
        }

        let jwtVerifier = JWTVerifier.rs256(publicKey: jwk.rsaPublicKey!)

        if decoded.verify(using: jwtVerifier) {
            // Attach user info to request
            request.userInfo["user"] = decoded.claims
            next()
        } else {
            response.status(.unauthorized).send(json: ["error": "Invalid token"])
        }
    } catch {
        response.status(.unauthorized).send(json: ["error": "Invalid token", "details": error.localizedDescription])
    }
}

// Example usage
router.get("/protected", middleware: BodyParser())
router.get("/protected", handler: validateJwtMiddleware) { request, response, next in
    let user = request.userInfo["user"]
    try response.send(json: ["message": "Access granted", "user": user]).end()
}

Kitura.addHTTPServer(onPort: 8080, with: router)
Kitura.run()
```

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