It All Started with Metadata
Milk Admin natively relies on two separate databases — one for internal configuration, the other for managing external data. This architecture made it a natural candidate for integration with external systems, such as a WordPress database.
Once connected, I was able to start managing a WordPress site's tables directly from my panel. From there, an immediate need emerged: handling metadata relationships natively. I looked at how the most well-known and mature systems similar to mine handled this, and I found that there usually isn't a native system for it. So I wrote hasMeta, a method that handles native metadata relationships.
How it looks in a model definition
$rule->table('#__users')
->id()
->hasMeta('nickname', UserMetaModel::class, 'user_id')
->hasMeta('city', UserMetaModel::class, 'user_id')
->string('name', 100)->required();
Then there are a number of other small conventions I inherited from WordPress: the dynamic table prefix (#__) and a functions.php file for more advanced customizations.
A Small Personal Win
Every now and then I try to promote my work — I'm terrible at it, but I try. When I showed the metadata functionality to a client of mine, his reaction was enthusiastic. He asked me for a demo… that had nothing to do with metadata. He wanted to verify whether my admin panel could handle WordPress authentication.
A different request than expected, but an exciting one, so I got to work.
The WordPress Plugin
I developed a plugin that hooks into WordPress's authenticate filter, with priority 30, to intervene after the standard checks:
add_filter('authenticate', [$this, 'authenticate'], 30, 3);
The method receives three parameters: $user (the result of previous checks), $username, and $password. If conditions require it, the plugin makes a POST call to my admin panel's endpoint via wp_remote_post:
$args = [
'headers' => ['Content-Type' => 'application/json'],
'timeout' => (int)$options['request_timeout'],
'body' => wp_json_encode([
'username' => $username,
'password' => $password,
]),
];
$response = wp_remote_post($endpoint, $args);
The Authentication Endpoint
On the Milk Admin side, I created a dedicated module that exposes a public endpoint for credential verification:
#[ApiEndpoint('public-auth/check-credentials', 'POST')]
Reachable with a simple call:
POST /public_html/api.php?page=public-auth/check-credentials
Content-Type: application/json
{
"username": "user@example.com",
"password": "password"
}
For this demo the table only contains username and password, but nothing prevents extending the response with additional data such as roles, permissions, or profile information.
Key design choices
The plugin is designed not to interfere with administrators and not to block the native flow when username or password are empty: in those cases, WordPress handles everything as usual.
Final Thoughts
What started as an experiment with metadata turned into a concrete demonstration of interoperability. My admin panel doesn't replace WordPress — it works alongside it: it makes it easy to create a management panel that extends WordPress data, but I discovered it can also integrate well via API.
Top comments (0)