Conditional sign-in validation using Fluent Validation - asp.net-mvc-4

I am trying to use Fluent Validation and it seems easy to use at the beginning but now there is some problem. I need to validate a SignIn view model, shown below:
public SignInViewModelValidator(IMembershipService membershipService)
{
_membershipService = membershipService;
RuleFor(x => x.EMail).NotEmpty().EmailAddress();
RuleFor(x => x.Password).NotEmpty().Length(6, 20);
Custom(x =>
{
var user = _membershipService.ValidateUser(x.EMail, x.Password);
if (user == null)
return new ValidationFailure("EMail", "Your E-Mail Address or password was invalid.");
return null;
});
}
But I'm getting all the errors at once, like this:
'E Mail' should not be empty.
Your E-Mail Address or password was invalid.
'Password' should not be empty.
How can I change this behavior to not check the Custom validation rule when the other rules are invalid? In other words, it should only check the Custom validation rule when 'EMail' and 'Password' fields are valid.

I managed this in this way:
public SignInViewModelValidator(IMembershipService membershipService){
_membershipService = membershipService;
bool firstPhasePassed = true;
RuleFor(x => x.EMail)
.NotEmpty().WithMessage("")
.EmailAddress().WithMessage("")
.OnAnyFailure(x => { firstPhasePassed = false; });
RuleFor(x => x.Password)
.NotEmpty().WithMessage("")
.Length(6, 16).WithMessage("")
.OnAnyFailure(x => { firstPhasePassed = false; });
When(x => firstPhasePassed, () =>
{
Custom(x =>
{
if (_membershipService.ValidateUser(x.EMail, x.Password) == null)
return new ValidationFailure("EMail", "");
return null;
});
});
}

You can use the When method to check your custom rule only when your email/password rules are valid.
To make this easier, I suggest moving your Custom rule logic into a separate method (something like IsValidEmailAndPassword) and use the Must method to validate both email and password. Since you're passing in multiple parameters (email and password) into that method, read the documentation on the overload of Must that "accepts an instance of the parent object being validated" in order to implement this rule.
Hopefully those links point you to the right direction.

Related

cakephp3 entities relationship error when login (the error is going after refresh)

I am struggling with this issue for a few days. I've tried to debug step by step with Xdebug, but I cannot find where it is the problem.
Basically when login into the cakephp3.9 I get this error:
App\Model\Table\UsersTable association "Roles" of type "manyToMany" to "Slince\CakePermission\Model\Table\RolesTable" doesn't match the expected class "App\Model\Table\RolesTable".
You can't have an association of the same name with a different target "className" option anywhere in your app.
As I mentioned above, I am using cakephp 3.9 and the slince package ("slince/cakephp-permission": "^1.0") to manage roles/permissions. After get this error if I refresh the browser evertyhing works as normal. The error only appears once, always after login.
Relations in UsersTable.php
$this->belongsToMany('Roles', [
'foreignKey' => 'user_id',
'targetForeignKey' => 'role_id',
'joinTable' => 'users_roles'
]);
UsersController.php
public function login()
{
if ($this->request->is('post')) {
$user = $this->Auth->identify();
if ($user) {
if (Configure::read('Options.status') == 2) {
$this->Flash->error('Please confirm your account - click on the validation link emailed to you');
return $this->redirect(['action' => 'login', 'controller' => 'Users']);
}
$UsersRoles = TableRegistry::getTableLocator()->get('UsersRoles');
// Get User role_id
$AuthRole = $UsersRoles
->find()
->select(['role_id'])
->where(['user_id' => $user['id']])
->first();
// if the status of the user is false an error appears and it will be redirected back || check if is an admin role?
if ($user['status'] != 1 || $AuthRole->role_id > 3) {
$this->Flash->error('Your account is not authorized to access this area. Contact the support team or check your inbox');
return $this->redirect(['action' => 'login', 'controller' => 'Users']);
}
$Roles = TableRegistry::getTableLocator()->get('Roles');
// Get Role name
$AuthRoleName = $Roles
->find()
->select('name')
->where(['id' => $AuthRole['role_id']])
->first();
$user['role_id'] = $AuthRole['role_id'];
$user['role_name'] = $AuthRoleName['name'];
// Set the use into the session
$this->Auth->setUser($user);
// Save the previous login date to the session and enable tour vars
$session = $this->getRequest()->getSession();
if (empty($user['last_login'])) {
$session->write('Options.run', true);
$session->write('Options.player', true);
}
// Now update the actual login time
$this->Users->updateLastLogin($this->Auth->user('id'));
// Handle case where referrer is cleared/reset
$nextUrl = $this->Auth->redirectUrl();
if ($nextUrl === "/") {
return $this->redirect(['action' => 'index', 'controller' => 'Adminarea']);
} else {
return $this->redirect($nextUrl);
}
}
$this->Flash->error(__('Invalid username or password, please try again'));
}
$this->viewBuilder()->setLayout('admin_in');
}
The issue it is in the relationship "Roles", it already exists in the file "PermissionsTableTrait.php" from the slice package, and it seems that cannot be two relationships with the same name.

About the implementation of Remember Me using AuthenticationPlugin's Cookie Authenticator

I use CakePHP's AuthenticationPlugin. I was trying to implement RememberMe functionality into this.
I found the following article when I was reading the Cakephp documentation.
Cookie Authenticator aka “Remember Me”
However, the documentation here is difficult for me to understand. I have no idea what to do with it.
I've successfully implemented EncryptedCookieMiddleware. I have no idea what to do after that.
I don't know how to use rememberMeField, how to use fields and how to use cookies.
$this->Authentication->rememberMeField
$this->Authentication->fields
I tried to see if I could use it like these, but it was still no good.
Please let me know how to use these.
Also, do you know of any RememberMe tutorials?
How do I implement it?
Sorry. Please help me...
// in config/app.php
'Security' => [
.....
'cookieKey' => env('SECURITY_COOKIE_KEY', 'AnyString'), // <- add
],
// in src/Application.php
use Cake\Http\Middleware\EncryptedCookieMiddleware; // <- add
// in middleware()
public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue
{
$cookies = new EncryptedCookieMiddleware( // <- add
['mail', 'password'],
Configure::read('Security.cookieKey')
);
$middlewareQueue
// . . .
->add($cookies) // <-add
->add(new AuthenticationMiddleware($this));
So far I've been able to implement it myself. I'm confident.
The problem is after this. We have no idea what to do with it...
A Remember me checkbox was implemented in the template Form.
$this->request->getData('rememberMe'); to get it.
If this is 1, the checkbox was pressed.
// in src/Controller/UsersController
public function login()
{
$this->request->allowMethod(['get', 'post']);
if ($this->request->is('post')) {
$result = $this->Authentication->getResult();
// If the user is logged in, whether POST or GET, we will redirect
$requestGetData = $this->request->getData('rememberMe');
if ($requestGetData['rememberMe'] == 1){
$this->Authentication->cookie['name'] = $requestGetData['mail'];
$this->Authentication->cookie['name'] = $requestGetData['password']
}
if ($result->isValid()) {
$redirect = $this->request->getQuery('redirect', [
'controller' => 'Stores',
'action' => 'index',
]);
return $this->redirect($redirect);
}
// If the user fails to authenticate after submitting, an error is displayed.
if (!$result->isValid()) {
$this->Flash->error(__('Your email address or password is incorrect.'));
}
}
$title = $this->config('Users.title.login');
$message = $this->config('Users.message.login');
$this->set(compact('login_now', 'title', 'message'));
}
I know that's not true. But I tried to implement something like this just in case.
Please help me!
Changed around the login.
public function login()
{
$this->request->allowMethod(['get', 'post']);
if ($this->request->is('post')) {
$result = $this->Authentication->getResult();
$requestData = $this->request->getData();
if ($result->isValid()) {
$redirect = $this->request->getQuery('redirect', [
'controller' => 'Stores',
'action' => 'index',
]);
$this->Authentication->getAuthenticationService()->loadAuthenticator( 'Authentication.Cookie', [
'fields' => ['mail', 'password']
]
);
return $this->redirect($redirect);
}
if ($this->request->is('post') && !$result->isValid()) {
$this->Flash->error(__('Your email address or password is incorrect.'));
}
}
$title = $this->config('Users.title.login');
$message = $this->config('Users.message.login');
$this->set(compact('title', 'message'));
}
You're not supposed to load authenticators in your controllers, authentication happens at middleware level, before any of your controllers are being invoked.
The cookie authenticator is ment to be loaded and configured just like any other authenticator, that is where you create the authentication service, usually in Application::getAuthenticationService() in src/Application.php.
By default the field in the form must be remember_me, not rememberMe, that is unless you would configure the cookie authenticator's rememberMeField option otherwise.
Furthermore the default cookie name of the cookie authenticator is CookieAuth, so if you wanted to encrypt it, you'd have to use that name in the EncryptedCookieMiddleware config accordingly.
tl;dr
Remove all cookie related code from your controller, and load the authenticator in your Application::getAuthenticationService() method:
use Authentication\Identifier\IdentifierInterface;
// ...
public function getAuthenticationService(ServerRequestInterface $request): AuthenticationServiceInterface
{
$service = new AuthenticationService();
// ...
// The cookie authenticator should be loaded _after_ the session authenticator,
// and _before_ other authenticators like the form authenticator
$service->loadAuthenticator('Authentication.Cookie', [
// 'rememberMeField' => 'custom_form_field_name', // if you want to change the default
'fields' => [
IdentifierInterface::CREDENTIAL_USERNAME => 'mail',
IdentifierInterface::CREDENTIAL_PASSWORD => 'password',
],
]);
// ...
return $service;
}
set the authentication cookie name in the EncryptedCookieMiddleware config:
$cookies = new EncryptedCookieMiddleware(
['CookieAuth'],
Configure::read('Security.cookieKey')
);
and change the field name in your form to remember_me if you're using the cookie authenticator's defaults:
echo $this->Form->control('remember_me', [
'type' => 'checkbox'
]);
That's all that should be required, if you tick the checkbox in your login form, then the authentication middleware will set a cookie after successful authentication accordingly, and it will pick up the cookie if it's present on a request and no other authenticator successfully authenticates the request first (like the session authenticator for example).

Custom is Obsolete

I updated a project to the latest version of Fluent Validation and I get a warning:
'AbstractValidator<AccountSignInModel>.Custom(Func<AccountSignInModel, ValidationFailure>)'
is obsolete: 'Use model-level RuleFor(x => x) instead'
When I am using the following code:
When(x => !String.IsNullOrEmpty(x.Password) && !String.IsNullOrEmpty(x.Username), () => {
Custom(x => {
Boolean valid = service.ValidateCredentials(x.Username, x.Password));
if (!valid)
return new ValidationFailure("Credentials", "Authentication failed");
return null;
});
});
I don't know how to convert this into a RuleFor(x => x).
Or is there another alternative to custom?
We decided to use Fluent Validation recently on our application. So I am fairly new to this, figuring stuff as we go forward.
Stumbled upon your issue while searching for a different issue. There are not many resources on this. Thought I would share my thoughts, if it can help you.
Here is what I would do.
public NewCustomValidator(Interface int)
{
CascadeMode = CascadeMode.Continue; // you could use StopOnFirstFailure or Continue
RuleFor(x=> x.UserName).NotEmpty(); // Will return an error if null or empty
RuleFor(x=> x.Password).NotEmpty();
RuleSet("SomeNameHere", () =>
{
CascadeMode = CascadeMode.StopOnFirstFailure;
var isValid = false;
RuleFor(x=> new { x.UserName, x.Password })
.Must((u , p) =>
{
valid = ValidateCredentials(u, p);
return valid;
})
.WithMessage("You can show any message if you want");
RuleFor(x => x.UserName).Must((s) => isValid);
RuleFor(x => x.Password).Must((s) => isValid);
});
}
So, I am basically using your method to define in a Rule Set. You may have to add some code to validate the ruleSet.
var result = validator.Validate(NewCustom, ruleSet: "SomeNameHere");
Disclaimer: This code may not compile. But it will give you an idea on how to approach the problem. If you have better ideas or If you could make the code work, please post the answer. That will help me gain some more knowledge. Thanks.

Laravel 5 customizing authentication

In Laravel 5 how can you customize the default authentication which comes out of the box? For example I have multiple types of users to authenticate against. Each type of user is defined by a role i.e Jobseeker, Recruiter etc. Each type of user will have a different type of registration form to capture some of the profile details as well. So I have the following tables:
users
roles
role_user
jobseeker_profile
recruiter_profile
The default authcontroller and passwordcontroller in Laravel 5 uses traits for all the authentication methods. How would you go about customizing it - do you guys edit the existing trait files? So for example the getRegister method returns the register view but I would want it to check the route before deciding which view to show.
// default method
public function getRegister()
{
return view('auth.register');
}
// custom method
public function getRegister()
{
if (Request::is('jobseeker/register'))
{
return view('auth.jobseeker_register');
}
elseif (Request::is('recruiter/register'))
{
return view('auth.recruiter_register');
}
}
Similarly the default postLogin method is as follows:
public function postLogin(Request $request)
{
$this->validate($request, [
'email' => 'required|email', 'password' => 'required',
]);
$credentials = $request->only('email', 'password');
if ($this->auth->attempt($credentials, $request->has('remember')))
{
return redirect()->intended($this->redirectPath());
}
return redirect($this->loginPath())
->withInput($request->only('email', 'remember'))
->withErrors([
'email' => 'These credentials do not match our records.',
]);
}
But I would want the method to also check the user roles as follows:
public function postLogin(Request $request)
{
$this->validate($request, [
'email' => 'required|email', 'password' => 'required',
]);
$credentials = $request->only('email', 'password');
if ($this->auth->attempt($credentials, $request->has('remember')))
{
if(Auth::user()->role->name == 'recruiter')
{
return redirect()->to('/recruiter/dashboard');
}
elseif(Auth::user()->role->name == 'jobseeker')
{
return redirect()->to('jobseeker/dashboard');
}
}
return redirect($this->loginPath())
->withInput($request->only('email', 'remember'))
->withErrors([
'email' => 'These credentials do not match our records.',
]);
}
So my question is how do you go about customizing the existing authentication? Do you guys create a new controller perhaps CustomAuthController, CustomPasswordController and copy all the traits from the default auth controllers into these custom controllers and edit them as appropriate? I'm unable to find any Laravel 5 tutorials on how to acheive this - they all simply talk about the default out of the box authentication. If anyone has done something similar before I would love to hear about how you went about it and which files were edited to wire this custom auth up.
You have a couple of options:
Override the methods in the existing auth controller.
Just don’t implement the AuthenticatesAndRegistersUsers trait at all, and implement authentication logic entirely yourself.
With regards to redirect, I’d listen on the auth.login event, check your user’s type there, and then redirect to the specific dashboard there and then.

Authentication with 2 different tables

I need to create a new "auth" config with another table and users. I have one table for the "admin" users and another table for the normal users.
But how can I create another instance of Auth with a different configuration?
While trying to solve this problem myself, I found a much simpler way. I basically created a custom ServiceProvider to replace the default Auth one, which serves as a factory class for Auth, and allows you to have multiple instances for multiple login types. I also stuck it all in a package which can be found here: https://github.com/ollieread/multiauth
It's pretty easy to use really, just replace the AuthServiceProvider in app/config/app.php with Ollieread\Multiauth\MultiauthServiceProvider, then change app/config/auth.php to look something like this:
return array(
'multi' => array(
'account' => array(
'driver' => 'eloquent',
'model' => 'Account'
),
'user' => array(
'driver' => 'database',
'table' => 'users'
)
),
'reminder' => array(
'email' => 'emails.auth.reminder',
'table' => 'password_reminders',
'expire' => 60,
),
);
Now you can just use Auth the same way as before, but with one slight difference:
Auth::account()->attempt(array(
'email' => $attributes['email'],
'password' => $attributes['password'],
));
Auth::user()->attempt(array(
'email' => $attributes['email'],
'password' => $attributes['password'],
));
Auth::account()->check();
Auth::user()->check();
It also allows you to be logged in as multiple user types simultaneously which was a requirement for a project I was working on. Hope it helps someone other than me.
UPDATE - 27/02/2014
For those of you that are just coming across this answer, I've just recently added support for reminders, which can be accessed in the same factory style way.
You can "emulate" a new Auth class.
Laravel Auth component is basically the Illuminate\Auth\Guard class, and this class have some dependencies.
So, basically you have to create a new Guard class and some facades...
<?php
use Illuminate\Auth\Guard as AuthGuard;
class CilentGuard extends AuthGuard
{
public function getName()
{
return 'login_' . md5('ClientAuth');
}
public function getRecallerName()
{
return 'remember_' . md5('ClientAuth');
}
}
... add a ServiceProvider to initialize this class, passing it's dependencies.
<?php
use Illuminate\Support\ServiceProvider;
use Illuminate\Auth\EloquentUserProvider;
use Illuminate\Hashing\BcryptHasher;
use Illuminate\Auth\Reminders\PasswordBroker;
use Illuminate\Auth\Reminders\DatabaseReminderRepository;
use ClientGuard;
use ClientAuth;
class ClientServiceProvider extends ServiceProvider
{
public function register()
{
$this->registerAuth();
$this->registerReminders();
}
protected function registerAuth()
{
$this->registerClientCrypt();
$this->registerClientProvider();
$this->registerClientGuard();
}
protected function registerClientCrypt()
{
$this->app['client.auth.crypt'] = $this->app->share(function($app)
{
return new BcryptHasher;
});
}
protected function registerClientProvider()
{
$this->app['client.auth.provider'] = $this->app->share(function($app)
{
return new EloquentUserProvider(
$app['client.auth.crypt'],
'Client'
);
});
}
protected function registerClientGuard()
{
$this->app['client.auth'] = $this->app->share(function($app)
{
$guard = new Guard(
$app['client.auth.provider'],
$app['session.store']
);
$guard->setCookieJar($app['cookie']);
return $guard;
});
}
protected function registerReminders()
{
# DatabaseReminderRepository
$this->registerReminderDatabaseRepository();
# PasswordBroker
$this->app['client.reminder'] = $this->app->share(function($app)
{
return new PasswordBroker(
$app['client.reminder.repository'],
$app['client.auth.provider'],
$app['redirect'],
$app['mailer'],
'emails.client.reminder' // email template for the reminder
);
});
}
protected function registerReminderDatabaseRepository()
{
$this->app['client.reminder.repository'] = $this->app->share(function($app)
{
$connection = $app['db']->connection();
$table = 'client_reminders';
$key = $app['config']['app.key'];
return new DatabaseReminderRepository($connection, $table, $key);
});
}
public function provides()
{
return array(
'client.auth',
'client.auth.provider',
'client.auth.crypt',
'client.reminder.repository',
'client.reminder',
);
}
}
In this Service Provider, I put some example of how to create a 'new' password reminder component to.
Now you need to create two new facades, one for authentication and one for password reminders.
<?php
use Illuminate\Support\Facades\Facade;
class ClientAuth extends Facade
{
protected static function getFacadeAccessor()
{
return 'client.auth';
}
}
and...
<?php
use Illuminate\Support\Facades\Facade;
class ClientPassword extends Facade
{
protected static function getFacadeAccessor()
{
return 'client.reminder';
}
}
Of course, for password reminders, you need to create the table in database, in order to work. In this example, the table name should be client_reminders, as you can see in the registerReminderDatabaseRepository method in the Service Provider. The table structure is the same as the original reminders table.
After that, you can use your ClientAuth the same way you use the Auth class. And the same thing for ClientPassword with the Password class.
ClientAuth::gust();
ClientAuth::attempt(array('email' => $email, 'password' => $password));
ClientPassword::remind($credentials);
Don't forget to add your service provider to the service providers list in the app/config/app.php file.
UPDATE:
If you are using Laravel 4.1, the PasswordBroker doesn't need the Redirect class anymore.
return new PasswordBroker(
$app['client.reminder.repository'],
$app['client.auth.provider'],
$app['mailer'],
'emails.client.reminder' // email template for the reminder
);
UPDATE 2
Laravel 5.2 just introduced multi auth, so this is no longer needed in this version.
Ok, I had the same problem and here is how I solved it:
actually in laravel 4 you can simply change the auth configs at runtime so to do the trick you can simply do the following in your App::before filter:
if ($request->is('admin*'))
{
Config::set('auth.model', 'Admin');
}
this will make the Auth component to use th Admin model when in admin urls. but this will lead to a new problem, because the login session key is the same if you have two users in your admins and users table with the same id you will be able to login to the admin site if you have logged in before as a regular user! so to make the two different authetications completely independent I did this trick:
class AdminGuard extends Guard
{
public function getName()
{
return 'admin_login_'.md5(get_class($this));
}
public function getRecallerName()
{
return 'admin_remember_'.md5(get_class($this));
}
}
Auth::extend('eloquent.admin', function()
{
return new AdminGuard(new EloquentUserProvider(new BcryptHasher, 'Admin'), App::make('session.store'));
});
and change the App::before code to:
if ($request->is('admin*'))
{
Config::set('auth.driver', 'eloquent.admin');
Config::set('auth.model', 'Admin');
}
you can see that I made a new auth driver and rewrote some methods on the Guard class so it will generate different session keys for admin site. then I changed the driver for the admin site. good luck.
I had the same problem yesterday, and I ended up creating a much simpler solution.
My requirements where 2 different tables in two different databases. One table was for admins, the other was for normal users. Also, each table had its own way of hashing. I ended up with the following (Code also available as a gist on Github: https://gist.github.com/Xethron/6790029)
Create a new UserProvider. I called mine MultiUserProvider.php
<?php
// app/libraries/MultiUserProvider.php
use Illuminate\Auth\UserProviderInterface,
Illuminate\Auth\UserInterface,
Illuminate\Auth\GenericUser;
class MultiUserProvider implements UserProviderInterface {
protected $providers;
public function __construct() {
// This should be moved to the config later...
// This is a list of providers that can be used, including
// their user model, hasher class, and hasher options...
$this->providers = array(
'joomla' => array(
'model' => 'JoomlaUser',
'hasher' => 'JoomlaHasher',
)
'another' => array(
'model' => 'AnotherUser',
'hasher' => 'AnotherHasher',
'options' => array(
'username' => 'empolyee_number',
'salt' => 'salt',
)
),
);
}
/**
* Retrieve a user by their unique identifier.
*
* #param mixed $identifier
* #return \Illuminate\Auth\UserInterface|null
*/
public function retrieveById($identifier)
{
// Returns the current provider from the session.
// Should throw an error if there is none...
$provider = Session::get('user.provider');
$user = $this->createModel($this->providers[$provider]['model'])->newQuery()->find($identifier);
if ($user){
$user->provider = $provider;
}
return $user;
}
/**
* Retrieve a user by the given credentials.
*
* #param array $credentials
* #return \Illuminate\Auth\UserInterface|null
*/
public function retrieveByCredentials(array $credentials)
{
// First we will add each credential element to the query as a where clause.
// Then we can execute the query and, if we found a user, return it in a
// Eloquent User "model" that will be utilized by the Guard instances.
// Retrieve the provider from the $credentials array.
// Should throw an error if there is none...
$provider = $credentials['provider'];
$query = $this->createModel($this->providers[$provider]['model'])->newQuery();
foreach ($credentials as $key => $value)
{
if ( ! str_contains($key, 'password') && ! str_contains($key, 'provider'))
$query->where($key, $value);
}
$user = $query->first();
if ($user){
Session::put('user.provider', $provider);
$user->provider = $provider;
}
return $user;
}
/**
* Validate a user against the given credentials.
*
* #param \Illuminate\Auth\UserInterface $user
* #param array $credentials
* #return bool
*/
public function validateCredentials(UserInterface $user, array $credentials)
{
$plain = $credentials['password'];
// Retrieve the provider from the $credentials array.
// Should throw an error if there is none...
$provider = $credentials['provider'];
$options = array();
if (isset($this->providers[$provider]['options'])){
foreach ($this->providers[$provider]['options'] as $key => $value) {
$options[$key] = $user->$value;
}
}
return $this->createModel($this->providers[$provider]['hasher'])
->check($plain, $user->getAuthPassword(), $options);
}
/**
* Create a new instance of a class.
*
* #param string $name Name of the class
* #return Class
*/
public function createModel($name)
{
$class = '\\'.ltrim($name, '\\');
return new $class;
}
}
Then, I told Laravel about my UserProvider by adding the following lines to the top of my app/start/global.php file.
// app/start/global.php
// Add the following few lines to your global.php file
Auth::extend('multi', function($app) {
$provider = new \MultiUserProvider();
return new \Illuminate\Auth\Guard($provider, $app['session']);
});
And then, I told Laravel to use my user provider instead of EloquentUserProvider in app/config/auth.php
'driver' => 'multi',
Now, when I authenticate, I do it like so:
Auth::attempt(array(
'email' => $email,
'password' => $password,
'provider'=>'joomla'
)
)
The class would then use the joomlaUser model, with the joomlaHasher, and no options for the hasher... If using 'another' provider, it will include options for the hasher.
This class was built for what I required but can easily be changed to suite your needs.
PS: Make sure the autoloader can find MultiUserProvider, else it won't work.
I'm using Laravel 5 native auth to handle multiple user tables...
It's not difficult, please check this Gist:
https://gist.github.com/danielcoimbra/64b779b4d9e522bc3373
UPDATE: For Laravel 5, if you need a more robust solution, try this package:
https://github.com/sboo/multiauth
Daniel