visiosoft/username_authenticator-extension
A Laravel extension that adds username-based authentication to Visiosoft applications, letting users sign in with a username instead of email. It integrates with existing auth flows, supports configurable username fields, and simplifies login for apps that prefer usernames.
This is a PyroCMS/OpenClassify addon, not a standalone Laravel package. The -extension suffix and the visiosoft namespace confirm it plugs into the OpenClassify classifieds platform (which itself sits on PyroCMS + Laravel). You cannot meaningfully use it in a vanilla Laravel app — it relies on PyroCMS's addon discovery, the users module, and the \Anomaly\UsersModule\User\... authentication stack.
Minimal steps:
streams/, addons/, or PyroCMS-style module registration, stop here.composer require visiosoft/username_authenticator-extension (or place it under addons/ and let the addon manager pick it up).php artisan addon:install visiosoft.extension.username_authenticator (or the equivalent addon install command used by your OpenClassify version) so migrations and config get published.php artisan cache:clear && php artisan view:clear && composer dump-autoload.src/UsernameAuthenticatorExtensionServiceProvider.php or similar) and its config/ directory. That is where the "how does this hook the authenticator" answer lives — it almost certainly overrides the default EloquentUserProvider::retrieveByCredentials() behavior or registers a custom Authenticator binding.First use case: enabling users to log in with a username column instead of (or in addition to) email. That means your users table already needs a username column, and it needs to be unique — verify this before installing, because the addon will not create it for you.
Login form swap. The addon changes credential resolution, not the frontend form. You will still need to edit your login view (in OpenClassify these live under addons/ or resources/views/ in the auth module) so the email input is named to whatever the authenticator expects. A common pattern here is to accept either field and try both — inspect the authenticator class to see which key it reads (credentials['username'], credentials['email'], or both).
Email-or-username fallback. The typical integration is a retrieveByCredentials() override that does:
public function retrieveByCredentials(array $credentials)
{
if (isset($credentials['email'])) {
$user = parent::retrieveByCredentials($credentials);
if ($user) return $user;
$credentials['username'] = $credentials['email'];
unset($credentials['email']);
}
return parent::retrieveByCredentials($credentials);
}
If the package ships something like this, keep it — but be aware both columns must be indexed and ideally non-overlapping.
Service provider binding. Register the swap in boot() rather than register() so it takes effect after the users module binds its default provider:
$this->app->extend('auth', function ($auth, $app) {
$auth->provider(...custom...);
return $auth;
});
Registration flow. If registration is part of your app, ensure the username field is collected and validated (unique:users,username) and saved. The addon only solves authentication, not provisioning — this is the most commonly missed integration step.
Testing. Write a feature test immediately: one login with a valid username, one with an email, one with a duplicate string that exists as both a username and another user's email. That third test is where this class of package usually breaks.
composer.json version constraints as stale — it likely pins php: ^7.3|^7.4 and laravel/framework: ^6|^7|^8. Expect to either fork it or add "minimum-stability": "dev" and patch constraints.retrieveByCredentials override yourself in ten lines.username = NULL, the addon's lookup may silently fail or, worse, match on a shared empty value. Backfill and add a unique index before enabling.john finds John depends on your DB collation (utf8mb4_unicode_ci matches case-insensitively; utf8mb4_bin does not). Pick a policy and normalize on write and read.admin and another whose username is admin will produce ambiguous results depending on lookup order. Decide which wins and document it.retrieveByCredentials() — the extension's override often swallows mismatches rather than throwing.config/username_authenticator.php or relies on the users module config. Published config in PyroCMS often doesn't re-publish on upgrade — you may be editing a stale copy.extend() the binding further (e.g., to add a phone fallback) without forking the package. If the package only registers one authenticator, override its class via container binding in your own app-level provider rather than editing vendor code.retrieveByCredentials() implementation, copy the idea, and drop the dependency. You get the same functionality, no unmaintained constraints, and full control over the collision/case behavior.How can I help you explore Laravel packages today?