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

# Add JWT Authentication to Your Dart Frog API

Secure your Dart Frog API using JWT authentication with JWKS.

## How Zuplo Handles It

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

## Dart Frog Backend Code

```dart
import 'dart:convert';
import 'package:dart_frog/dart_frog.dart';
import 'package:jose/jose.dart';
import 'package:http/http.dart' as http;

const String issuer = 'https://my-api-a32f34.zuplo.api/__zuplo/issuer';
const String jwksUrl = '$issuer/.well-known/jwks.json';

Future<Jwks> fetchJwks() async {
  final response = await http.get(Uri.parse(jwksUrl));
  if (response.statusCode != 200) {
    throw Exception('Failed to load JWKS');
  }
  return Jwks.fromJson(jsonDecode(response.body));
}

Handler validateJwt(Handler handler) {
  return (context) async {
    final authorization = context.request.headers['Authorization'];
    if (authorization == null || !authorization.startsWith('Bearer ')) {
      return Response(statusCode: 401, body: 'No token provided');
    }

    final token = authorization.substring(7);
    try {
      final jwks = await fetchJwks();
      final jwt = JsonWebToken.unverified(token);

      final keyStore = JsonWebKeyStore()
        ..addKeySetUrl(Uri.parse(jwksUrl));

      final isValid = await jwt.verify(keyStore, issuer: issuer, algorithms: ['RS256']);

      if (!isValid) {
        return Response(statusCode: 401, body: 'Invalid token');
      }

      context = context.provide<JsonWebToken>(() => jwt);
      return handler(context);
    } catch (e) {
      return Response(statusCode: 401, body: 'Invalid token');
    }
  };
}

final Handler protectedRoute = (context) async {
  final jwt = context.read<JsonWebToken>();
  final claims = jwt.claims;

  return Response.json(body: {
    'message': 'Access granted',
    'user': claims.toJson(),
  });
};

void main() {
  final app = Router()
    ..use(validateJwt)
    ..get('/protected', protectedRoute);

  serve(app);
}
```

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