When building custom Joomla 5 Web Services plugins, developers often want to protect API endpoints using Joomla’s built-in authentication, especially the X-Joomla-Token header. Unfortunately, it’s common to run into issues where the custom route doesn’t recognize the authenticated user, even when a valid token is sent.
In this guide, we’ll show you exactly how to secure your custom Joomla API endpoints properly—without using 'public' => true
, which would bypass authentication.
We’ll walk through:
- Creating a secure custom API plugin in Joomla 5
- Enforcing token-based authentication
- Ensuring
$app->getIdentity()
returns the authenticated user - Avoiding common pitfalls
Why Not Use 'public' => true
?
Setting 'public' => true
in your custom API route makes it publicly accessible — which means no authentication is required. This defeats the purpose of API token validation, especially for endpoints that deal with user data, internal logic, or write operations.
Instead, we want to enforce Joomla’s built-in token auth system using X-Joomla-Token (or session/cookie-based auth if needed).
Step-by-Step Guide to Secure Custom API Endpoints
Plugin Structure Example
Your custom plugin should be placed in:
/plugins/webservices/helloworldapi/
Structure:
/helloworldapi
├── helloworldapi.php
├── helloworldapi.xml
└── services
└── provider.php
1. Register Routes in the Right Event
Instead of onBeforeApiRoute
, use the onAfterApiAuthenticate
event to register your custom routes.
Why? Because Joomla processes authentication before this event — which means the user will be available via getIdentity()
.
helloworldapi.php
defined('_JEXEC') or die;
use Joomla\CMS\Plugin\CMSPlugin;
class PlgWebservicesHelloworldapi extends CMSPlugin
{
protected $autoloadLanguage = true;
public function onAfterApiAuthenticate($app, $router)
{
$route = new \Joomla\Router\Route(
['GET'],
'v1/helloworldapi/sayhello',
'api.onApiHelloworld',
[],
[
'component' => 'com_helloworldapi',
'format' => ['application/json'],
// DO NOT SET 'public' => true
]
);
$router->addRoute($route);
}
public function onApiHelloworld()
{
$app = \Joomla\CMS\Factory::getApplication();
$user = $app->getIdentity();
if ($user->guest) {
http_response_code(401);
echo new \Joomla\CMS\Response\JsonResponse(['error' => 'Unauthorized'], 401);
} else {
echo new \Joomla\CMS\Response\JsonResponse([
'id' => $user->id,
'name' => $user->name
]);
}
$app->close();
}
}
2. Enable the Plugin
- Go to System → Plugins
- Enable your
Webservices - Helloworldapi
plugin
3. Test the Endpoint
Use a REST client like Postman or cURL:
Test with Token
GET /api/index.php/v1/helloworldapi/sayhello
X-Joomla-Token: your_valid_api_token
Accept: application/json
Expected response:
{
"id": 123,
"name": "John Doe"
}
Test Without Token
Returns:
{
"error": "Unauthorized"
}
Common Pitfall: Registering the Route Too Early
Don’t do this:
public function onBeforeApiRoute($app, $router)
At this point, authentication hasn’t been processed, so:
$app->getIdentity()
will return a guest- Even valid tokens will be ignored
Use Cases Where This Matters
This approach is useful for:
- Admin-only API access
- Restricted mobile app endpoints
- Authenticated user data access
- Custom business logic inside your own extensions
More Resources
Looking to integrate Joomla APIs into full-stack applications? Check out:
Summary
Step | What to Do |
---|---|
1. | Use onAfterApiAuthenticate to add your routes |
2. | Never set 'public' => true unless you want no auth |
3. | Use $app->getIdentity() to get the user |
4. | Return 401 Unauthorized if guest |
With this setup, your custom Joomla 5 API plugins will be secure, token-authenticated, and ready for production use.