I want to create authentication mechanism without need for database where only one person (admin) who knows right username and password (which I would hardcode) would be able to login. I still want to use Auth::attempt(), Auth::check() and other functions.
I found out that I could create my own User driver, but it seems to me that there should be something simpler.
Maybe it is not very nice solution, but I want as simple as possible website.
It may only seem there should be something simpler, but in fact that's as simple as you can get if you want to extend the authentication system. All the methods you're using through the Auth facade (like attempt, check, etc.), are implemented within the Illuminate\Auth\Guard class. This class needs a UserProviderInterface implementation to be injected into the constructor in order to work. Which means that in order to use the Auth facade you either need to use the already implemented DatabaseUserProvider or EloquentUserProvider, or implement your own provider that handles the simple login you want.
Although the article you linked to may look lengthy, to achieve what you need you might get away with much less code in the provider than you might think. Here's what I think is what you need:
1. In your app/config/auth.php change the driver to simple and append the desired login credentials:
'driver' => 'simple',
'credentials' => array(
'email' => 'user#email.com',
'password' => 'yourpassword'
)
2. Create a file in your app directory called SimpleUserProvider.php that has this code:
use Illuminate\Auth\UserInterface;
use Illuminate\Auth\GenericUser;
use Illuminate\Auth\UserProviderInterface;
class SimpleUserProvider implements UserProviderInterface {
protected $user;
public function __construct(array $credentials)
{
$this->user = new GenericUser(array_merge($credentials, array('id' => null)));
}
// If you only need to login via credentials the following 3 methods
// don't need to be implemented, they just need to be defined
public function retrieveById($identifier) { }
public function retrieveByToken($identifier, $token) { }
public function updateRememberToken(UserInterface $user, $token) { }
public function retrieveByCredentials(array $credentials)
{
return $this->user;
}
public function validateCredentials(UserInterface $user, array $credentials)
{
return $credentials['email'] == $user->email && $credentials['password'] == $user->password;
}
}
3. Lastly you'll need to register the new provider with the authentication system. You can append this to the app/start/global.php file:
Auth::extend('simple', function($app)
{
return new SimpleUserProvider($app['config']['auth.credentials']);
});
This should give you a simple (no database) user authentication while still being able to use Laravel's facades.
The accepted answer did not work for me. Every time I logged in, the login was successful but when on the /home page, I was redirected to the login page again.
I found out that this was due to the user not being stored in the session as authenticated user. To fix this, I had to implement the getAuthIdentifier method in the User model class and also implement the retrieveById method .
I've also adjusted my solution to support multiple hard coded users (it presumes, that the email is unique, so we can also use it as id for the user):
1. In app/config/auth.php:
'providers' => [
'users' => [
'driver' => 'array',
],
],
'credentials' => [
'userA#email.com' => 'passA',
'userB#email.com' => 'passB',
]
2. The UserProvider:
use \Illuminate\Auth\GenericUser;
use \Illuminate\Contracts\Auth\UserProvider;
use \Illuminate\Contracts\Auth\Authenticatable;
class ArrayUserProvider implements UserProvider
{
private $credential_store;
public function __construct(array $credentials_array)
{
$this->credential_store = $credentials_array;
}
// IMPORTANT: Also implement this method!
public function retrieveById($identifier) {
$username = $identifier;
$password = $this->credential_store[$username];
return new User([
'email' => $username,
'password' => $password,
]);
}
public function retrieveByToken($identifier, $token) { }
public function updateRememberToken(Authenticatable $user, $token) { }
public function retrieveByCredentials(array $credentials)
{
$username = $credentials['email'];
// Check if user even exists
if (!isset($this->credential_store[$username])) {
return null;
}
$password = $this->credential_store[$username];
return new GenericUser([
'email' => $username,
'password' => $password,
'id' => null,
]);
}
public function validateCredentials(Authenticatable $user, array $credentials)
{
return $credentials['email'] == $user->email && $credentials['password'] == $user->getAuthPassword();
}
}
3. And in app/Providers/AuthServiceProvider:
use Illuminate\Support\Facades\Auth;
class AuthServiceProvider extends ServiceProvider
{
...
/**
* Register any authentication / authorization services.
*
* #return void
*/
public function boot()
{
Auth::provider('array', function($app, array $config) {
// Return an instance of Illuminate\Contracts\Auth\UserProvider...
return new ArrayUserProvider($app['config']['auth.credentials']);
});
}
}
4. In User.php (model):
class User extends Authenticatable
{
...
public function getAuthIdentifier()
{
return $this->email;
}
}
More Information:
For everyone who is interested, why there need to be the above stated additions:
When you login, the method login in Illuminate\Auth\SessionGuard is called. In this method you will find, that the identifier of the user is stored in the session with $this->updateSession($user->getAuthIdentifier()). Therefore we need to implement this method in our user model.
When you add $this->middleware('auth') in your controller, then the method authenticate() in Illuminate\Auth\Middleware\Authenticate is called. This again calls $this->auth->guard($guard)->check() to check, whether a user is authenticated. The check() method only tests, that there exists a user in the session (see Illuminate\Auth\GuardHelpers). It does this by calling the user() method of the guard, which in this case is again SessionGuard. In the user() method, the user is retrieved by taking the id stored in the session and calling retrieveById to get the user.
Related
I'm new to Laravel (5.5) and want to create an application with a custom authentication mechanism:
If the user is not logged in, he/she should be redirected to /login.
I want to use the session-guard.
I have written and registered custom classes
LdapUser extends Illuminate\Auth\GenericUser and
LdapUserProvider implements Illuminate\Contracts\Auth\UserProvider. Actually both are dummy implementations at the moment that return generic objects. Later, I want to check, if the user exists in my external ldap-directory and if the credentials are valid. Registration, internal user management or password reset are not intended. I just want to use the user's name and some properties in my blade templates.
I modified config/app.php and config/auth.php, so that this provider can be used.
Now, I wonder how to proceed in order to actually use the authentication and access the LdapUser in my blade templates. I tried {{ Auth::user()->name }}, which led to this error message: "Trying to get property of non-object."
How to I tell my app to check, if a valid session exists and otherwise redirect to the login page?
How can I access the user's properties inside the blade templates?
This is my route:
Route::group(['middleware' => 'auth'], function () {
Route::get('/helloworld', function () {return 'Hello World!';});
});
Route::get('/login', 'LdapLoginController#showLoginForm')->name('showLoginForm');
/helloworld now redirects to /login where I see the login-form, which is going to be handled by the LdapLoginController:
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
class LdapLoginController extends Controller
{
use AuthenticatesUsers;
}
I would now expect that the session guard uses the LdapUserProvider to check the credentials:
namespace App\Providers;
use App\LdapUser;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider;
class LdapUserProvider implements UserProvider
{
public function retrieveById($id)
{
return $this->dummyUser();
}
public function retrieveByCredentials(array $credentials)
{
return $this->dummyUser();
}
public function validateCredentials(Authenticatable $user, array $credentials)
{
return true;
}
public function retrieveByToken($identifier, $token)
{
return new \Exception('not implemented');
}
public function updateRememberToken(Authenticatable $user, $token)
{
return new \Exception('not implemented');
}
protected function dummyUser()
{
$attributes = array(
'id' => 123,
'username' => 'chuckles',
'password' => \Hash::make('SuperSecret'),
'name' => 'Dummy User',
);
return new LdapUser($attributes);
}
}
Unfortunately, after submitting the login form, I'm always redirected back to /login, no matter what I enter :-(
Any help is greatly appreciated!
I'm trying to allow user to view their own profile in Laravel 5.4.
UserPolicy.php
public function view(User $authUser, $user)
{
return true;
}
registered policy in AuthServiceProvider.php
protected $policies = [
App\Task::class => App\Policies\TaskPolicy::class,
App\User::class => App\Policies\UserPolicy::class
];
Routes
Route::group(['middleware' => 'auth'], function() {
Route::resource('user', 'UserController');
} );
Blade template
#can ( 'view', $user )
// yes
#else
// no
#endcan
UserController.php
public function profile()
{
return $this->show(Auth::user()->id);
}
public function show($id)
{
$user = User::find($id);
return view('user.show', array( 'user'=>$user,'data'=>$this->data ) );
}
The return is always 'false'. Same for calling policy form the controller. Where do I go wrong?
Answering my own question feels weird, but I hate it when I come across questions without followups.
So after double checking It turned out that if I remove authorizeResource from the constructor:
public function __construct()
{
$this->authorizeResource(User::class);
}
and check for authorization in the controller function:
$this->authorize('view',$user);
everything works.
I must've missed this part when I added $user as a parameter in the policy function. So the user to be viewed is never passed in the authorizeResource method.
Thanks everyone for taking your time to help me.
When you add
public function __construct()
{
$this->authorizeResource(User::class);
}
to your Controller, you have to edit all your function signatures to match it to the class e.g. your show signature has to change from public function show($id)
to public function show(User $user)
After that it should work
Just a different approach here to users viewing their own profile.
First, I will create a route for that
Route::group(['middleware' => 'auth'], function() {
Route::get('profile', 'UserController#profile');
});
Then in the profile function I do
public function profile()
{
$user = Auth::user();
return view('profile', compact('user'));
}
This way, user automatically only views their own profile.
Now, if you want to allow some users to view others' profiles, then you can use Policy. Why? Because I think user should ALWAYS be able to view their own profile. But not all users should view other users profiles.
Solution:
Change the second parameter from #can( 'view', $user ) to #can( 'view', $subject ) and it will work find.
Why:
Because you're doing it the wrong way.
public function view(User $user, $subject){
return true;
}
Just look carefully the policy view method, first parameter is authenticated user or current user and second parameter is $subject, Since policies organize authorization logic around models.
Policies are classes that organize authorization logic around a
particular model or resource. For example, if your application is a
blog, you may have a Post model and a corresponding PostPolicy to
authorize user actions such as creating or updating posts.
if you want to go further deep inside it.
https://github.com/laravel/framework/blob/5.3/src/Illuminate/Auth/Access/Gate.php#L353
/**
* Resolve the callback for a policy check.
*
* #param \Illuminate\Contracts\Auth\Authenticatable $user
* #param string $ability
* #param array $arguments
* #return callable
*/
protected function resolvePolicyCallback($user, $ability, array $arguments)
{
return function () use ($user, $ability, $arguments) {
$instance = $this->getPolicyFor($arguments[0]);
// If we receive a non-null result from the before method, we will return it
// as the final result. This will allow developers to override the checks
// in the policy to return a result for all rules defined in the class.
if (method_exists($instance, 'before')) {
if (! is_null($result = $instance->before($user, $ability, ...$arguments))) {
return $result;
}
}
if (strpos($ability, '-') !== false) {
$ability = Str::camel($ability);
}
// If the first argument is a string, that means they are passing a class name
// to the policy. We will remove the first argument from this argument list
// because the policy already knows what type of models it can authorize.
if (isset($arguments[0]) && is_string($arguments[0])) {
array_shift($arguments);
}
if (! is_callable([$instance, $ability])) {
return false;
}
return $instance->{$ability}($user, ...$arguments);
};
}
See the last line where it is calling the method with $user and $argument( in our case Model ) is passed.
Laravel Docs for Authorization/Policies
It's possible to escape one or more policies methods using options parameter at authorizeResource with except:
public function __construct()
{
$this->authorizeResource(User::class, 'user', ['except' => ['view']]);
}
This should be on Laravel's documentation, but it isn't. I discovered this just guessing. I think this way it is a better approach thus, by removing authorizeResource method in the construct, it would be necessary to implement the authorization method for each resource action in order to protect the controller.
I am not so into PHP and Laravel and I have the following problem, I came from Java.
I am following this tutorial to implement a custom user provider:
https://blog.georgebuckingham.com/laravel-52-auth-custom-user-providers-drivers/
I am using Laravel 5.3 version.
I briefly expain what I need: my Laravel application is only a front end application, all the business logic, included the user authentication, is performed by a Java back end application that exposes REST web services.
Performing a call to:
http://localhost:8080/Extranet/login
and passing username and password as basic authentication I obtain a JSON response like this that represent the logged user:
{
"userName": "Painkiller",
"email": "painkiller#gmail.com",
"enabled": true
}
So, in my Laravel application, I have to perform this call and then parse the previous returned JSON object to generate the authenticated object into the front end application session.
To do this I have implemented the previous tutorial (and it seems to works) implementing this custom user provider class named UserProvider that implements the Laravel IlluminateUserProvider interface:
<?php
namespace App\Authentication;
use Illuminate\Auth\GenericUser;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider as IlluminateUserProvider;
use GuzzleHttp\Client;
use function GuzzleHttp\json_encode;
use function GuzzleHttp\json_decode;
use Illuminate\Support\Facades\Log;
class UserProvider implements IlluminateUserProvider
{
public function retrieveById($identifier)
{
// TODO: Implement retrieveById() method.
\Log::info('retrieveById START');
// PERFORM THE CALL TO MY BACK END WB SERVICE AND CREATE A NEW GenericUser USING THESE INFORMATION:
$attributes = array(
'id' => 123,
'username' => 'nobili.andrea#gmail.com',
'password' => \Hash::make('SuperSecret'),
'name' => 'Dummy User',
);
$user = new GenericUser($attributes);
return $user;
}
public function retrieveByToken($identifier, $token)
{
// TODO: Implement retrieveByToken() method.
\Log::info('retrieveByToken START');
}
public function updateRememberToken(Authenticatable $user, $token)
{
// TODO: Implement updateRememberToken() method.
\Log::info('updateRememberToken START');
}
public function retrieveByCredentials(array $credentials) {
// TODO: Implement retrieveByCredentials() method.
\Log::info('retrieveByCredentials START');
\Log::info('INSERTED USER CREDENTIAL: '.$credentials['email'] . ' ' .$credentials['password']);
$client = new Client(); //GuzzleHttp\Client
$response = $client->get('http://localhost:8080/Extranet/login',
[
'auth' => [
'nobili.andrea#gmail.com',
'pswd'
]
]);
$dettagliLogin = json_decode($response->getBody());
\Log::info('response: '.(json_encode($dettagliLogin)));
//$user = new User('Pippo', 'pippo#google.com', true);
$attributes = array(
'id' => 123,
'username' => 'nobili.andrea#gmail.com',
'password' => \Hash::make('SuperSecret'),
'name' => 'Dummy User',
);
$user = new GenericUser($attributes);
\Log::info('USER: '.(json_encode($user)));
return $user;
}
public function validateCredentials(Authenticatable $user, array $credentials)
{
// TODO: Implement validateCredentials() method.
\Log::info('validateCredentials START');
return true;
}
}
This is only a preliminary test so the returned data are mocked.
It works in this way:
1) When the user insert his credential in the login page (http://localhost:8000/login) it is called the retrieveByCredentials() method:
public function retrieveByCredentials(array $credentials) {
// TODO: Implement retrieveByCredentials() method.
\Log::info('retrieveByCredentials START');
\Log::info('INSERTED USER CREDENTIAL: '.$credentials['email'] . ' ' .$credentials['password']);
$client = new Client(); //GuzzleHttp\Client
$response = $client->get('http://localhost:8080/Extranet/login',
[
'auth' => [
'nobili.andrea#gmail.com',
'pswd'
]
]);
$dettagliLogin = json_decode($response->getBody());
\Log::info('response: '.(json_encode($dettagliLogin)));
//$user = new User('Pippo', 'pippo#google.com', true);
$attributes = array(
'id' => 123,
'username' => 'nobili.andrea#gmail.com',
'password' => \Hash::make('SuperSecret'),
'name' => 'Dummy User',
);
$user = new GenericUser($attributes);
\Log::info('USER: '.(json_encode($user)));
return $user;
}
that performs a web service call to obtain the user information related to this user. Then these credential are verified by the validateCredentials() method (at this time it returns true every time). Finnaly it returns a GenericUser objct containing the information of the logged user (at this time are mocked because is a test and I have not yet paresed the JSON reeturned by my web service.
Then, when the user access to the next page (after the success login) it seems to me that is called the retrieveById($identifier) method, this:
public function retrieveById($identifier)
{
// TODO: Implement retrieveById() method.
\Log::info('retrieveById START');
// PERFORM THE CALL TO MY BACK END WB SERVICE AND CREATE A NEW GenericUser USING THESE INFORMATION:
$attributes = array(
'id' => 123,
'username' => 'nobili.andrea#gmail.com',
'password' => \Hash::make('SuperSecret'),
'name' => 'Dummy User',
);
$user = new GenericUser($attributes);
return $user;
}
At this time the logic is that it use the id of the previous logged user to perform a call to the back end web service, obtain again these information and create the same GenericUser object that will be returned to the next page that uses it. Now I have mock these userinformation.
Ok, this works but I can't do in this way for security reason.
So my idea is: when I retrieve the user information in the retrieveByCredentials(array $credentials), after have check that are correct, I will put this GenericUser object into session.
Then in the retrieveById() method I will retrieve these information from the session.
Can I do something like this? Could be a smart way? How can I put and retrieve an object\data into and from the session (I am not into PHP and front end).
Yes, you can.
When you log in a user using Laravel's authentication API, it is automatically stored in the application's session. So what you are doing is completely fine.
And you don't need to worry about methods like retrieveByCredentials fireing calls to your REST API when you get the user instance through Auth::user(). Every time this method is called, it checks if there is an user already loged in. If there is, it just returns the user, otherwise, it makes the necessary calls to find that user.
But I would suggest putting those calls to your REST API in another class. The UserProvider has only the responsibility of showing Laravel how to retrieve a user, so it should make use of delegation to call other objects to do the dirty work of finding something somewhere.
You can checkout more about how the session Auth class work in the source of laravel:
https://github.com/laravel/framework/blob/5.4/src/Illuminate/Auth/SessionGuard.php
This is how I solved it. It needs improvements yet but at least can be useful as a general guideline.
Notes: I'm using PHP 8 and taking advantage of constructor property promotion, so maybe you'll have to assign the instance variables. I'm also using Laravel 8.
Implement your API gateway that implements the method:
function authenticate(string $username, string $password): ?APIUser {}
Create an API user model:
<?php
namespace App\Auth;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Support\Facades\Hash;
final class APIUser implements Authenticatable
{
function __construct(
private $id,
private $username,
private $password,
private $access_token,
private $remember_token,
private $expires_in,
private $platform_id,
) {
$this->password = Hash::make($this->password);
}
function getUsername()
{
return $this->username;
}
function getAccessToken()
{
return $this->access_token;
}
function getExpiresIn()
{
return $this->expires_in;
}
function getPlatformId()
{
return $this->platform_id;
}
/**
*
* Authenticatable section
*
*/
/**
* Get the name of the unique identifier for the user.
* #return string
*/
public function getAuthIdentifierName()
{
return 'id';
}
/**
* Get the unique identifier for the user.
* #return mixed
*/
public function getAuthIdentifier()
{
return $this->id;
}
/**
* Get the password for the user.
* #return string
*/
public function getAuthPassword()
{
return $this->password;
}
/**
* Get the token value for the "remember me" session.
* #return string
*/
public function getRememberToken()
{
$this->remember_token;
}
/**
* Set the token value for the "remember me" session.
* #param string $value
* #return void
*/
public function setRememberToken($value)
{
$this->remember_token = $value;
}
/**
* Get the column name for the "remember me" token.
*
* #return string
*/
public function getRememberTokenName()
{
return 'remember_token';
}
}
Create the user provider:
<?php
namespace App\Auth;
use App\Ports\AppAPIGateway;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Contracts\Session\Session;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log;
class APIUserProvider implements UserProvider
{
function __construct(
private AppAPIGateway $api,
private Session $session
) {
}
public function retrieveById($identifier)
{
if ($this->session->has('user')) {
return $this->session->get('user');
}
return;
}
public function retrieveByToken($identifier, $token)
{
Log::info('APIUserProvider: retrieveByToken (not implemented)');
return null;
}
public function updateRememberToken(Authenticatable $user, $token)
{
$user->setRememberToken($token);
}
public function retrieveByCredentials(array $credentials)
{
if (
!array_key_exists('username', $credentials)
||
!array_key_exists('password', $credentials)
) {
throw new AuthenticationException('Could not retrieve credentials: missing parameters');
}
$user = $this->api->authenticate(
$credentials['username'],
$credentials['password']
);
$this->session->put('user', $user);
$this->session->save();
return $user;
}
public function validateCredentials(Authenticatable $user, array $credentials)
{
if (
!array_key_exists('username', $credentials)
||
!array_key_exists('password', $credentials)
) {
throw new AuthorizationException('Could not validate credentials: missing parameters');
}
return
$user->getUsername() == $credentials['username']
&& Hash::check($credentials['password'], $user->getAuthPassword());
}
}
Modify app/Providers/AuthServiceProvider.php:
public function boot()
{
$this->registerPolicies();
Auth::provider('api_driver', function ($app, array $config) {
return new APIUserProvider(
$app->make('App\Ports\AppAPIGateway'),
$app->make('Illuminate\Contracts\Session\Session')
);
});
}
Enable this new provider in config/auth.php:
'defaults' => [
'guard' => 'api_guard',
'passwords' => 'api_provider',
],
...
'guards' => [
'api_guard' => [
'driver' => 'session',
'provider' => 'api_provider',
],
],
...
'providers' => [
'api_provider' => [
'driver' => 'api_driver',
],
],
When you did do
$user = new GenericUser($attributes);
You can follow with
Auth::login($user);
Now all your User attributes are stored in session and can be retrieved by calling
$user = Auth::user();
I am new on Laravel and use Authorization. I am looking for the way to change default sql for Auth. Actually, Laravel does it using this simple sql command at below:
SELECT * FROM users WHERE login="something" AND password = "something" LIMIT 1
I am trying to change default sql like this:
SELECT
u.id, u.name, c.company
FROM
users u, companies c
WHERE
u.login="something" AND
u.password = "something" AND
u.companyId = c.id
LIMIT 1
I understood that I should create custom Authorization system: crate new user Provider and Auth Provider.
Firstly, I created Auth folder inside App and added there CustomUserProvider.php
CustomUserProvider.php
<?php namespace App\Auth;
use Illuminate\Contracts\Auth\Authenticatable as UserContract;
use Illuminate\Contracts\Auth\UserProvider as UserProviderInterface;
use App\Models\User;
class CustomUserProvider implements UserProviderInterface {
protected $model;
public function __construct(UserContract $model)
{
$this->model = $model;
}
public function retrieveById($identifier)
{
}
public function retrieveByToken($identifier, $token)
{
}
public function updateRememberToken(UserContract $user, $token)
{
}
public function retrieveByCredentials(array $credentials)
{
}
public function validateCredentials(UserContract $user, array $credentials)
{
}
}
My customAuthProvider.php file, in App/Providers:
<?php namespace App\Providers;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use App\Auth\CustomUserProvider;
use Illuminate\Support\ServiceProvider;
class CustomAuthProvider extends ServiceProvider {
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot()
{
$this->app['auth']->extend('custom',function()
{
return new CustomUserProvider(new User);
});
}
/**
* Register the application services.
*
* #return void
*/
public function register()
{
//
}
}
At the end I set driver to custom in config/Auth.php
'driver' => 'custom'
I am looking for the way using this custom classes how can I use custom sql command for Authorization (Login)?
Or maybe this way is wrong?
If all you need are additional constraints on the query that fetches user from the database during authentication, there is much simpler way to do that.
First of all, Laravel offers an AuthenticatesUsers trait that you can use in your controller to handle authentication requests. The default implementation fetches user from the database using username field and then, if matching user is found, it validates their password.
The list of attributes that is used to fetch user from the database can be customized by overriding getCredentials method in your controller. In your case the following should be enough to load user using their username and company id:
protected function getCredentials(Request $request)
{
return $request->only($this->loginUsername(), 'password', 'companyId);
}
Once you add that, user should provide their username, companyId and password in the login form and they will be authenticated only if there exists a user with given username that belongs to given company and the password provided is valid.
UPDATE: If you decide not to use the trait, but want to authenticate users manually, you can do so in a really similar manner. When calling Auth::attempt() you just need to pass all the criteria that should be used to authenticate the user, e.g.:
Auth::attempt([
'username' => Input::get('username'),
'companyId' => Input::get('companyId'),
'password' => Input::get('password')
]);
I tried this package and it helped me:
https://github.com/ollieread/multiauth/
I have a table in my database with users. Their password are generated with my own custom hashing function.
How do i override the Authentication methods in laravel 4 to use my own hash class?
This is what I have been trying to do:
class CustomUserProvider implements Illuminate\Auth\UserProviderInterface {
public function retrieveByID($identifier)
{
return $this->createModel()->newQuery()->find($identifier);
}
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.
$query = $this->createModel()->newQuery();
foreach ($credentials as $key => $value)
{
if ( ! str_contains($key, 'password')) $query->where($key, $value);
}
return $query->first();
}
public function validateCredentials(Illuminate\Auth\UserInterface $user, array $credentials)
{
$plain = $credentials['password'];
return $this->hasher->check($plain, $user->getAuthPassword());
}
}
class CodeIgniter extends Illuminate\Auth\Guard {
}
App::bind('Illuminate\Auth\UserProviderInterface', 'CustomUserProvider');
Auth::extend('codeigniter', function()
{
return new CodeIgniter( App::make('CustomUserProvider'), App::make('session'));
});
When I run the Auth::attempt method I get this error:
ErrorException: Warning: Illegal offset type in isset or empty in G:\Dropbox\Workspaces\www\video\vendor\laravel\framework\src\Illuminate\Foundation\Application.php line 352
This is how ended up solving the problem:
libraries\CustomHasherServiceProvider.php
use Illuminate\Support\ServiceProvider;
class CustomHasherServiceProvider extends ServiceProvider {
public function register()
{
$this->app->bind('hash', function()
{
return new CustomHasher;
});
}
}
libraries\CustomHasher.php
class CustomHasher implements Illuminate\Hashing\HasherInterface {
private $NUMBER_OF_ROUNDS = '$5$rounds=7331$';
public function make($value, array $options = array())
{
$salt = uniqid();
$hash = crypt($password, $this->NUMBER_OF_ROUNDS . $salt);
return substr($hash, 15);
}
public function check($value, $hashedValue, array $options = array())
{
return $this->NUMBER_OF_ROUNDS . $hashedValue === crypt($value, $this->NUMBER_OF_ROUNDS . $hashedValue);
}
}
And then I replaced 'Illuminate\Hashing\HashServiceProvider' with 'CustomHasherServiceProvider' in the providers array in app/config/app.php
and added "app/libraries" to autoload classmap in composer.json
#vFragosop was on the right path with extending Auth.
There are a couple of ways to skin the cat and here is how I would do that without replacing the default Hasher class:
Include in your app/routes.php or wherever:
use Illuminate\Auth\Guard;
Auth::extend("eloquent", function() {
return new Guard(
new \Illuminate\Auth\EloquentUserProvider(new CustomHasher(), "User"),
App::make('session.store')
);
});
Create and autoload a CustomHasher class (i.e., app/libraries/CustomHasher.php):
class CustomHasher extends Illuminate\Hashing\BcryptHasher {
public function make($value, array $options = array())
{
...
}
public function check($value, $hashedValue, array $options = array())
{
...
}
}
That's it.
Warning: I can't ensure this is works out of the box and there may be a few gotchas here and there. Keep in mind Laravel 4 is still on development. Wish I could provide a more precise answer, but codebase is still going through many changes and not everything is properly documented. Anyway, you are looking for something like this:
// on config/auth.php
'driver' => 'custom'
// on start/global.php
Auth::extend('custom', function() {
// CustomUserProvider is your custom driver and should
// implement Illuminate\Auth\UserProviderInterface;
return new Guard(new CustomUserProvider, App::make('session'));
});
If this doesn't give you enough information to start, you should be able to figure it out by taking a look at those classes below:
EloquentUserProvider and DatabaseUserProvider
These classes are the currently supported authentication drivers. They should guide you on how to create your CustomUserProvider (or any name you like really).
Manager
This is the base class for anything that accepts custom drivers (including the AuthManager). It provides the methods for registering them like you do in Laravel 3.
This was the top result on Google, but these answers are insufficient for anyone on Laravel 5. Even the documentation doesn't suffice.
I've successfully replaced the Hasher for only the UserProvider. The rest of my application continues to use the very nice BcryptHasher, while user authentication uses a custom hasher. To do this, I had to study these answers, the documentation, and Laravel's source code itself. Here's what I found. Hopefully I can save someone else's full head of hair. Feel free to crosspost this to a question about Laravel 5.
First, create your custom hasher, if you haven't already. Place it wherever you'd like.
class MyCustomHasher implements Hasher {
public function make($value, array $options = []) {
return md5( $value ); // PLEASE DON'T USE MD5!
}
public function check($value, $hashedValue, array $options = []) {
if (strlen($hashedValue) === 0) {
return false;
}
return $hashedValue === $this->make($value);
}
public function needsRehash($hashedValue, array $options = []) {
return false;
}
}
Edit any registered ServiceProvider as follows...
class AppServiceProvider extends ServiceProvider {
public function boot() {
Auth::provider('eloquentCustom', function ($app, $config) {
return new EloquentUserProvider(new MyCustomHasher(), $config['model']);
});
}
}
You can replace 'eloquentCustom' with whatever you'd prefer.
Finally, edit your config/auth.php to use your custom provider. Here are the relevant parts...
return [
// ...
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
// ...
],
// ...
'providers' => [
'users' => [
'driver' => 'eloquentCustom', // <--- This is the only change
'model' => App\User::class,
],
// ...
],
// ...
];
Here's a little explanation, because I can't believe how obscure this was.
As you may expect, authentication is configured with config/auth.php. There are two key parts: Guards and Providers. I haven't yet bothered to learn exactly what guards do, but they seem to enforce authentication requirements. Providers are responsible for providing the necessary information to the guards. Therefore, a Guard requires a Provider. You can see that, in the default configuration, guards.web.provider is mapped to providers.users.
Laravel provides two implementations of UserProvider by default: EloquentUserProvider and DatabaseUserProvider. These correspond to the two possible values for providers.users.driver: eloquent and database, respectively. Normally, the eloquent option is chosen. EloquentUserProvider needs a Hasher, so Laravel gives it whatever the standard implementation is (ie. BcryptHasher). We override this behavior by creating our own "driver" for instantiating the Provider.
Auth is our friendly neighborhood facade. It is backed by the AuthManager. The often suggested Auth::extend() method expects a Guard (contrary to what the documentation might suggest). We have no need to mess with the Guard. Instead, we can use Auth::provider() which basically does the same thing as extend(), except it expects a Provider. So we provide a function to create our own instance of a EloquentUserProvider, giving it our custom Hasher (eg. MyCustomHasher). We also include a driver "name" that can be used in the config file.
Now back to the config file. That driver name that we just created is now a valid value for providers.users.driver. Set it there and you're good to go!
I hope this all makes sense and is useful for someone!