Securing APIs is crucial for protecting sensitive data and ensuring only authorized users can access certain endpoints. In this tutorial, we will walk through the process of securing Symfony APIs using JSON Web Token (JWT) authentication, a popular method for stateless authentication.

Prerequisites

  • Symfony 5 or higher installed
  • API Platform or custom API setup
  • Composer for dependency management
  • Basic knowledge of Symfony and security concepts

Step 1: Install JWT Authentication Bundle

Begin by installing the LexikJWTAuthenticationBundle, which provides JWT support for Symfony.

composer require lexik/jwt-authentication-bundle

After installation, add the bundle to your kernel if Symfony version requires manual registration.

// config/bundles.php
return [
    // ...
    Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle::class => ['all' => true],
];

Step 2: Generate SSL Keys

JWT requires a private and public key pair for signing tokens. Generate them using OpenSSL:

mkdir -p config/jwt
openssl genrsa -out config/jwt/private.pem -aes256 4096
openssl rsa -pubout -in config/jwt/private.pem -out config/jwt/public.pem

Secure the private key by setting appropriate permissions.

chmod 600 config/jwt/private.pem

Step 3: Configure the Bundle

Update your Symfony configuration to include JWT settings.

// config/packages/lexik_jwt_authentication.yaml
lexik_jwt_authentication:
    secret_key: '%kernel.project_dir%/config/jwt/private.pem'
    public_key: '%kernel.project_dir%/config/jwt/public.pem'
    pass_phrase: 'your_passphrase'
    token_ttl: 3600

Replace 'your_passphrase' with a secure passphrase used during key generation.

Step 4: Update Security Configuration

Configure your security.yaml to enable JWT authentication.

// config/packages/security.yaml
security:
    encoders:
        Symfony\Component\Security\Core\User\UserInterface:
            algorithm: auto

    providers:
        app_user_provider:
            entity:
                class: App\Entity\User
                property: email

    firewalls:
        login:
            pattern: ^/api/login
            stateless: true
            anonymous: true
            json_login:
                check_path: /api/login
                username_path: email
                password_path: password

        api:
            pattern: ^/api
            stateless: true
            jwt: ~

    access_control:
        - { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/api, roles: IS_AUTHENTICATED_FULLY }

Step 5: Create Login Endpoint

Ensure your User entity implements UserInterface and has an email and password. The login endpoint will authenticate and return a JWT token.

Example Controller

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;

class ApiController extends AbstractController
{
    /**
     * @Route("/api/login", name="api_login", methods={"POST"})
     */
    public function login()
    {
        // The JSON login is handled automatically by security.yaml
        // No code needed here unless customizing response
    }
}

Step 6: Testing the Setup

Use a tool like Postman to send a POST request to /api/login with JSON body containing email and password:

Request:

{
  "email": "[email protected]",
  "password": "your_password"
}

If successful, you will receive a JWT token in response. Use this token in the Authorization header for subsequent API requests:

Authorization: Bearer your_jwt_token

Conclusion

Implementing JWT authentication in Symfony enhances your API security by enabling stateless, token-based authentication. Follow these steps to set up and test your secured API endpoints effectively.