JWT Authentication
As of OXID eShop version 7.5, you can use the JWT Authenticator component to add token-based authentication to API endpoints exposed via the API entrypoint.
The component provides a complete authentication solution including user validation, token generation, role-based access control, and ready-to-use API controllers.
Overview
The JWT Authenticator component provides:
JWT token generation and validation
Integration with OXID user system
Role-based access control with Symfony Security
#[IsGranted]and#[CurrentUser]attributes for protecting endpointsReady-to-use login and profile endpoints
Key features:
Stateless authentication suitable for APIs and headless commerce
Automatic user role resolution from OXID user data
Configurable role hierarchy
Symfony Security integration for consistent authorization
Installation
Install the component via Composer:
composer require oxid-esales/jwt-authentication-component
The component is automatically registered as an OXID eShop component and its services are immediately available.
Configuration
JWT Secret Key
Set the JWT secret key in your .env file:
API_JWT_SECRET=your-secret-key-here
Generate a secure secret key using OpenSSL:
openssl rand -base64 64
Warning
Never commit your JWT secret to version control. Always use environment variables or secure configuration management.
Role Hierarchy
Configure role inheritance in your project’s services.yaml:
Example
parameters:
oxid_jwt_authenticator.role_hierarchy:
ROLE_ADMIN_MALL:
- ROLE_ADMIN
With this configuration, users with ROLE_ADMIN_MALL automatically inherit ROLE_ADMIN
permissions.
For more complex role hierarchies, you can reimplement RoleResolverInterface to define
custom resolution logic. See Custom Roles below.
Available Endpoints
The component provides these built-in API endpoints:
Public Endpoints
These endpoints are accessible without authentication:
POST /api/login
Authenticates a user and returns a JWT token.
Example
curl -X POST https://your-shop.com/api/login \
-H "Content-Type: application/json" \
-d '{"username": "[email protected]", "password": "password"}'
Response:
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
"user": {
"username": "[email protected]",
"roles": ["ROLE_USER"]
}
}
Protected Endpoints
These endpoints require authentication via JWT token in the Authorization header:
GET /api/profile
Returns the authenticated user’s profile. Requires IS_AUTHENTICATED.
Example
curl -X GET https://your-shop.com/api/profile \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGc..."
Response:
{
"username": "[email protected]",
"roles": ["ROLE_USER"]
}
Protecting Your Endpoints
Use Symfony’s #[IsGranted] attribute to protect your API endpoints:
Basic Authentication
Require any authenticated user:
Example
<?php
declare(strict_types=1);
namespace MyVendor\MyModule\Controller\Api;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
final readonly class DataController
{
#[Route('/api/data', methods: ['GET'])]
#[IsGranted('IS_AUTHENTICATED')]
public function getData(): Response
{
// Only authenticated users can access this
}
}
Accessing Authenticated User
Using #[CurrentUser] Attribute
The recommended approach is to use Symfony’s #[CurrentUser] attribute:
Example
use OxidEsales\AuthComponent\Security\User\ApiUser;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\CurrentUser;
use Symfony\Component\Security\Http\Attribute\IsGranted;
final readonly class ProfileController
{
#[Route('/api/profile', methods: ['GET'])]
#[IsGranted('IS_AUTHENTICATED')]
public function getProfile(#[CurrentUser] ApiUser $user): Response
{
return new JsonResponse([
'id' => $user->getOxid(),
'username' => $user->getUserIdentifier(),
'roles' => $user->getRoles()
]);
}
}
Optional Authentication
To make authentication optional, use nullable type:
Example
#[Route('/api/info', methods: ['GET'])]
public function getInfo(#[CurrentUser] ?ApiUser $user): Response
{
return new JsonResponse([
'authenticated' => $user !== null,
'user' => $user ? [
'id' => $user->getOxid(),
'username' => $user->getUserIdentifier(),
] : null,
]);
}
Available Roles
The component resolves user roles from OXID’s user data:
ROLE_USERAll authenticated users automatically receive this role.
ROLE_ADMINUsers with OXID admin rights (
OXRIGHTSfield).ROLE_ADMIN_MALLUsers with mall admin rights (
OXRIGHTS = 'malladmin').
Custom Roles
You can extend role resolution by decorating the RoleResolverInterface:
Example
<?php
declare(strict_types=1);
namespace MyVendor\MyModule\Security;
use OxidEsales\AuthComponent\Security\User\RoleResolverInterface;
readonly class CustomRoleResolver implements RoleResolverInterface
{
public function __construct(
private RoleResolverInterface $inner
) {
}
public function resolveRoles(string $rights): array
{
$roles = $this->inner->resolveRoles($rights);
// Add custom role logic
$roles[] = 'ROLE_CUSTOM';
return $roles;
}
}
Register the decorator in services.yaml:
MyVendor\MyModule\Security\CustomRoleResolver:
decorates: OxidEsales\AuthComponent\Security\User\RoleResolverInterface
arguments:
$inner: '@.inner'