---
title: "Add JWT Authentication to Your undefined API"
description: undefined
canonicalUrl: "https://zuplo.com/use-cases/api-key-auth/perl/mojolicious/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

```perl
use Mojolicious::Lite;
use Mojo::JWT;
use Mojo::URL;
use Mojo::JSON 'decode_json';
use Mojo::UserAgent;

# Define the issuer and JWKS URL
my $ISSUER = 'https://my-api-a32f34.zuplo.api/__zuplo/issuer';
my $JWKS_URL = Mojo::URL->new("$ISSUER/.well-known/jwks.json");

# Configure user agent for JWKS fetching with caching
my $ua = Mojo::UserAgent->new;
$ua->max_redirects(3);
$ua->app->secrets(['some_secret']);
my $keys = {};

sub fetch_jwks {
  my $tx = $ua->get($JWKS_URL);
  if (my $res = $tx->success) {
    $keys = { map { $_->{kid} => $_ } @{$res->json->{keys}} };
  } else {
    die "Failed to fetch JWKS: " . $tx->error->{message};
  }
}

# Fetch JWKS when the app starts
fetch_jwks();

# Middleware to validate JWT
under sub {
  my $c = shift;

  # Extract token from Authorization header
  my $auth_header = $c->req->headers->authorization;
  return $c->render(json => {error => 'No token provided'}, status => 401)
    unless $auth_header && $auth_header =~ s/^Bearer\s+//;

  my $token = $auth_header;

  # Decode and verify JWT
  my $jwt;
  eval {
    my $decoded = Mojo::JWT->new(secret => sub {
      my $headers = shift;
      my $kid = $headers->{kid};
      return $keys->{$kid}{n} if $keys->{$kid};
      die "Key not found";
    });
    $jwt = $decoded->decode($token);
  };

  if ($@) {
    return $c->render(json => {error => 'Invalid token', details => $@}, status => 401);
  }

  # Check the issuer
  if ($jwt->{iss} ne $ISSUER) {
    return $c->render(json => {error => 'Invalid issuer'}, status => 401);
  }

  # Attach user information to the stash
  $c->stash(user => $jwt);

  return 1;
};

# Protected route
get '/protected' => sub {
  my $c = shift;
  $c->render(json => {message => 'Access granted', user => $c->stash('user')});
};

app->start;
```

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