Laravel Echo - Allow guests to connect to presence channel - php

I am using laravel-echo-server to run Laravel Echo to broadcast events.
I have a user counter channel which shows all the users on the app. For this I am using a presence channel. This works fine for logged in users, but guests just never get connected.
I've setup the below in the BroadcastServiceProvider:
Broadcast::channel('global', function () {
return ['name' => 'guest'];
});
Which from what I can tell, should allow everyone in as 'guests'. I'm guessing there's some middleware or auth that's being checked before this that I need to disable for this channel.
Any help on getting all clients joining this presence channel would be much appreciated!

The other solutions didn't work for me for a guest presence channel, this is what I ended up with:
// routes/channels.php
<?php
use Illuminate\Auth\GenericUser;
/*
|--------------------------------------------------------------------------
| Broadcast Channels
|--------------------------------------------------------------------------
|
| Here you may register all of the event broadcasting channels that your
| application supports. The given channel authorization callbacks are
| used to check if an authenticated user can listen to the channel.
|
*/
Route::post('/custom/broadcast/auth/route', function () {
$user = new GenericUser(['id' => microtime()]);
request()->setUserResolver(function () use ($user) {
return $user;
});
return Broadcast::auth(request());
});
Broadcast::channel('online.{uuid}', function ($user, $uuid) {
return [
'id' => $user->id,
'uuid' => $uuid
];
});

You may create a temporary user with factory(User::class)->make(...) and authenticate it with a middleware to use it as a guest.
Step 1: Creating the middleware
Run: php artisan make:middleware AuthenticateGuest
In app/Http/Middleware/AuthenticateGuest.php:
public function handle($request, Closure $next)
{
Auth::login(factory(User::class)->make([
'id' => (int) str_replace('.', '', microtime(true))
]));
return $next($request);
}
Now setup the AuthenticateGuest middleware in Kernel.php.
In app\Http\Kernel.php:
protected $routeMiddleware = [
...
'authenticate-guest' => \App\Http\Middleware\AuthenticateGuest::class,
];
Step 2: Setup Broadcast::channel route
In routes/channels.php:
Broadcast::channel('chatroom', function ($user) {
return $user; // here will return the guest user object
});
More at: https://laravel.com/docs/8.x/broadcasting#authorizing-presence-channels

For anyone looking for answers to this. It is indeed possible to auth guests into presence channels you just need to override the Broadcast::routes() from the service provider with your own.
As an example my presence channel 'global' accepts guests:
Route::post('/broadcasting/auth', function(Illuminate\Http\Request $req) {
if($req->channel_name == 'presence-global'){return 'global';}
return abort(403);
});
This could be extended in various directions, or could continue to pass other presence and private channels through to the default Broadcast::auth method

You can create your own auth guard and it's also pretty simple but more complex.
Create a class which will implement Authenticable Interface.
Create UserProvider.
Create a new Guard.
Register Guard and UserProvider in AuthServiceProvider.
Add provider and guard in config/auth.php
Use your new guard.
Advantages
You don't have to modify auth endpoint
You don't have to change default guard
You base on Laravel Auth system
Keep support of multiple tabs in the browser
Can be used with web guard at the same time
Keep all the advantages of using PresenceChannel
Disadvantages
A lot to code
So,
1. Create a new class which will implement Authenticable interface.
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Contracts\Support\Jsonable;
use JsonSerializable;
/**
* #property string $id
* #property string $name
*/
class Session implements Authenticatable, Jsonable, Arrayable, JsonSerializable
{
private $id;
private $attributes = [];
public function __construct($id)
{
$this->id = $id;
$this->name = "Guest";
}
/**
* 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->{$this->getAuthIdentifierName()};
}
/**
* Get the password for the user.
*
* #return string
*/
public function getAuthPassword()
{
return "";
}
/**
* Get the token value for the "remember me" session.
*
* #return string
*/
public function getRememberToken()
{
return $this->{$this->getAuthIdentifierName()};
}
/**
* Set the token value for the "remember me" session.
*
* #param string $value
* #return void
*/
public function setRememberToken($value)
{
$this->{$this->getRememberToken()} = $value;
}
/**
* Get the column name for the "remember me" token.
*
* #return string
*/
public function getRememberTokenName()
{
return "token";
}
public function __get($name)
{
return $this->attributes[$name];
}
public function __set($name, $value)
{
$this->attributes[$name] = $value;
}
/**
* Convert the object to its JSON representation.
*
* #param int $options
* #return string
*/
public function toJson($options = 0)
{
return json_encode($this);
}
/**
* Get the instance as an array.
*
* #return array
*/
public function toArray()
{
return $this->attributes;
}
/**
* Specify data which should be serialized to JSON
* #link https://php.net/manual/en/jsonserializable.jsonserialize.php
* #return mixed data which can be serialized by <b>json_encode</b>,
* which is a value of any type other than a resource.
* #since 5.4.0
*/
public function jsonSerialize()
{
return $this->attributes;
}
}
Modify this as you wish, but you shouldn't serialize $id property
2. Create UserProvider
<?php namespace App\Extensions;
use App\Models\Session;
use Illuminate\Cache\Repository;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Support\Fluent;
use Illuminate\Support\Str;
class SessionUserProvider implements UserProvider
{
private $store;
/**
* SessionUserProvider constructor.
* #param Repository $store
*/
public function __construct(Repository $store)
{
$this->store = $store;
}
/**
* Retrieve a user by their unique identifier.
*
* #param mixed $identifier
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveById($identifier)
{
return new Session(
$this->getUniqueTokenForSession($identifier)
);
}
/**
* Retrieve a user by their unique identifier and "remember me" token.
*
* #param mixed $identifier
* #param string $token
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveByToken($identifier, $token)
{
return null;
}
/**
* Update the "remember me" token for the given user in storage.
*
* #param \Illuminate\Contracts\Auth\Authenticatable $user
* #param string $token
* #return void
*/
public function updateRememberToken(Authenticatable $user, $token)
{
return;
}
/**
* Retrieve a user by the given credentials.
*
* #param array $credentials
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveByCredentials(array $credentials)
{
return null;
}
private function unpack($data)
{
return json_decode($data);
}
private function getUniqueTokenForSession($id)
{
return $this->retrieveCacheDataForSession($id)
->get('uuid');
}
private function retrieveCacheDataForSession($id)
{
$fluent = new Fluent(
$this->unpack(
$this->store->has($id) ? $this->store->get($id) : "[]"
)
);
if(!$fluent->__isset('uuid')) {
$fluent->__set('uuid', Str::random(128));
}
$this->store->put($id, $fluent->toJson(), 60 * 60 * 60);
return $fluent;
}
/**
* Validate a user against the given credentials.
*
* #param \Illuminate\Contracts\Auth\Authenticatable $user
* #param array $credentials
* #return bool
*/
public function validateCredentials(Authenticatable $user, array $credentials)
{
return null;
}
}
Identifier property in retrieveById method is always session id if you are using broadcasting so you can also use this as a token.
3. Create new Guard
<?php namespace App\Services\Auth;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Http\Request;
class GuestGuard implements Guard
{
private $user;
protected $request;
protected $provider;
/**
* GuestGuard constructor.
* #param UserProvider $provider
* #param Request $request
*/
public function __construct(UserProvider $provider, Request $request)
{
$this->provider = $provider;
$this->request = $request;
}
/**
* Determine if the current user is authenticated.
*
* #return bool
*/
public function check()
{
return !is_null($this->user);
}
/**
* Determine if the current user is a guest.
*
* #return bool
*/
public function guest()
{
return !$this->check();
}
/**
* Get the currently authenticated user.
*
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function user()
{
if($this->check()) {
return $this->user;
}
$this->setUser(
$this->provider->retrieveById(
$this->request->session()->getId()
)
);
return $this->user;
}
/**
* Get the ID for the currently authenticated user.
*
* #return int|null
*/
public function id()
{
return !is_null($this->user) ? $this->user->id : null;
}
/**
* Validate a user's credentials.
*
* #param array $credentials
* #return bool
*/
public function validate(array $credentials = [])
{
return false;
}
/**
* Set the current user.
*
* #param \Illuminate\Contracts\Auth\Authenticatable $user
* #return void
*/
public function setUser(Authenticatable $user)
{
$this->user = $user;
}
}
Here in user method you pass session id as identifier, using broadcasting only this method is nessesary.
4. Register Guard and UserProvider in AuthServiceProvider.
// app/Providers/AuthServiceProvider.php
/**
* Register any authentication / authorization services.
*
* #return void
*/
public function boot()
{
$this->registerPolicies();
Auth::provider('sessions', function (Application $app) {
return new SessionUserProvider(
$app->make('cache.store')
);
});
Auth::extend('guest', function (Application $app, $name, array $config) {
return new GuestGuard(Auth::createUserProvider($config['provider']), $app->make('request'));
});
}
5.1 Add provider in config/auth.php
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
// New
'sessions' => [
'driver' => 'sessions',
'model' => App\Models\Session::class,
],
],
5.2 Add guard in config/auth.php
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
'hash' => false,
],
// New
'guest' => [
'driver' => 'guest',
'provider' => 'sessions'
]
],
6. Use your new guard
// routes/channels.php
Broadcast::channel('chat.{id}', function (Authenticatable $user){
return $user;
}, ['guards' => ['guest']]);
Notice that you can use 'web' as a guard at the same time ('web' should be before 'guest'). It allows you to find out who is a guest and who is a logged in user - you can just check instance of Authenticable in channel callback.
And that how it looks in the laravel-echo-server database

With the help of Renan Coelho i got it to work. The missing part for me was to override the Broadcast::routes() method with the following:
Route::post('/broadcasting/auth', function (Illuminate\Http\Request $req) {
return Broadcast::auth($req);
});
Route::post('/broadcasting/auth'... is actually a route that gets added through the "Broadcast::routes()" method. This is why we override it here. You can see the active routes by typing php artisan route:list in your terminal.
Then, Renan Coelho already said, i had to add a custom Middleware (AuthenticateGuest) that creates a random user for me. (This is the hacky part) and add it to the $middleware array in the kernel.php:
protected $middleware = [
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
\App\Http\Middleware\TrustProxies::class,
\Barryvdh\Cors\HandleCors::class,
\App\Http\Middleware\AuthenticateGuest::class
];
The AuthenticateGuest Middleware looks like the following:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
use App\User;
class AuthenticateGuest
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
Auth::login(factory(User::class)->make([
'id' => (int)str_replace('.', '', microtime(true))
]));
return $next($request);
}
}
Hope that helps someone,
Sebastian

My solution to issue:
BroadcastServiceProvider.php (~/app/Providers/)
public function boot()
{
if (request()->hasHeader('V-Auth')) { /* Virtual client. */
Broadcast::routes(['middleware' => 'client_chat.broadcast.auth']);
} else {
Broadcast::routes();
}
require base_path('routes/channels.php');
}
Kernel.php (~/app/Http/)
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
...
'client_chat.broadcast.auth' => \App\Http\Middleware\ClientChatBroadcasting::class,
];
ClientChatBroadcasting.php (~/app/Http/Middleware/)
public function handle($request, Closure $next)
{
if (/** your condition **/) {
$fakeUser = new User;
$fakeUser->virtual_client = true;
$fakeUser->id = /** whatever you want **/;
$fakeUser->name = '[virtual_client]';
$fakeUser->asdasdasdasdasd = 'asdasdasdasdasd';
$request->merge(['user' => $fakeUser]);
$request->setUserResolver(function () use ($fakeUser) {
return $fakeUser;
});
}
return $next($request);
}
ChatChannel.php (~/app/Broadcasting/Chat/)
Broadcast::channel('chat.{chatId}', ChatChannel::class); Channel Classes
public function join($member/**($fakeUser)**/, $chatId)
{
$memberData = [/** your data **/];
/* If there is no member data (null), then there will be an authentication error. */
return $memberData;
}
[place in your js file, where you want connect to broadcasting]
this.Echo = new Echo({
broadcaster: 'socket.io',
host: /** your host **/,
reconnectionAttempts: 60,
encrypted: true,
auth: {
headers: {
'V-Auth': true,
'Access-Token': accessToken,
'Virtual-Id': virtualId,
'Chat-Id': chatId
}
}
});

I made it here reading this blog, getting the /broadcasting/auth error. The answers were very helpful but I didn't find the complete answer I needed.
I did this in Laravel 9.x so FYI it will work in that version too.
The major missing piece of code I needed was in the routes/web.php:
use Illuminate\Support\Facades\Route;
Broadcast::routes([
'middleware' => ['web', 'authenticate-guest'],
]);
Route::get('/', function () {
return view('welcome');
});
However I will add the rest down here in case anyone needs the whole thing:
Remove comment in app.config provider:
App\Providers\BroadcastServiceProvider::class,
Make middleware: php artisan make:middleware AuthenticateGuest.php
Then update it, in app/Http/Middleware
public function handle(Request $request, Closure $next)
{
Auth::login(User::factory()->make([
'id' => (int) str_replace('.', '', microtime(true))
]));
return $next($request);
}
app/Http/Kernel.php File..
protected $routeMiddleware = [
...
'authenticate-guest' => \App\Http\Middleware\AuthenticateGuest::class
];
app/Providers/BroadcastServiceProvider.php file should be this, but check:
public function boot()
{
Broadcast::routes();
require base_path('routes/channels.php');
}
Lastly, the routes/channel.php file:
use Illuminate\Support\Facades\Broadcast;
Broadcast::channel('common_room', function ($user) {
return true;
});
To finish everything off, do a npm run dev, followed by php artisan route:clear and maybe php artisan cache:clear/php artisan config:clear as well. Hope that helps!

Related

Unable to get login using laravel 5.4

Hi I'm developing user authentication on basis of SOAP API response using Laravel 5.4, for that I've implemented UserProvider and AuthServiceProvider but still unsuccessful.
Here is the implementation of UserProvider
namespace App\Authentication;
use App\Http\Controllers\ApiCallsController;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider as IlluminateUserProvider;
use Config;
use Illuminate\Foundation\Auth\User;
class UserProvider implements IlluminateUserProvider
{
/**
* #param mixed $identifier
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveById($identifier)
{
// Get and return a user by their unique identifier
$http_client = new ApiCallsController();
$check_user = $http_client->HttpClient("GET", Config::get('constants.c2mCloud') . '/user/UserTypeOrName/' . $identifier);
$users = json_decode($check_user);
if ($users->responseCode == "00") {
if (count($users->users) > 0) {
$user = new User();
$users = $users->users[0];
$user->id = $users->userId;
$user->c2MUsername = $users->c2MUsername;
$user->createdAt = $users->createdAt;
$user->lastModifiedAt = $users->lastModifiedAt;
$user->password = $users->password;
$user->userType = $users->userType;
$user->active = $users->active;
return new \App\User([
'id' => $users->userId
]);
}
}
}
/**
* #param mixed $identifier
* #param string $token
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveByToken($identifier, $token)
{
// Get and return a user by their unique identifier and "remember me" token
}
/**
* #param \Illuminate\Contracts\Auth\Authenticatable $user
* #param string $token
* #return void
*/
public function updateRememberToken(Authenticatable $user, $token)
{
// Save the given "remember me" token for the given user
}
/**
* Retrieve a user by the given credentials.
*
* #param array $credentials
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveByCredentials(array $credentials)
{
// Get and return a user by looking up the given credentials
$http_client = new ApiCallsController();
$check_user = $http_client->HttpClient("GET", Config::get('constants.c2mCloud') . '/user/UserTypeOrName/' . $credentials['c2MUsername']);
$users = json_decode($check_user);
if ($users->responseCode == "00") {
if (count($users->users) > 0) {
$users = $users->users[0];
// return $users->users[0];
return new \App\User([
'id' => $users->userId,
// 'c2MUsername' => $users->c2MUsername,
// 'createdAt' => $users->createdAt,
// ''
]);
}
}
}
/**
* Validate a user against the given credentials.
*
* #param \Illuminate\Contracts\Auth\Authenticatable $user
* #param array $credentials
* #return bool
*/
public function validateCredentials(Authenticatable $user, array $credentials)
{
// Check that given credentials belong to the given user
}
}
And implementation of AuthServiceProvider
<?php
namespace App\Authentication;
use Auth;
use App\Authentication\UserProvider;
use Illuminate\Support\ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
/**
* Perform post-registration booting of services.
*
* #return void
*/
public function boot()
{
Auth::provider('c2m_provider', function($app, array $config) {
return new UserProvider();
});
}
/**
* Register bindings in the container.
*
* #return void
*/
public function register()
{
//
}
}
and my User class is
<?php
namespace App;
use Illuminate\Contracts\Auth\Authenticatable;
class User implements Authenticatable
{
/**
* #return string
*/
public function getAuthIdentifierName()
{
// Return the name of unique identifier for the user (e.g. "id")
}
/**
* #return mixed
*/
public function getAuthIdentifier()
{
// Return the unique identifier for the user (e.g. their ID, 123)
}
/**
* #return string
*/
public function getAuthPassword()
{
// Returns the (hashed) password for the user
}
/**
* #return string
*/
public function getRememberToken()
{
// Return the token used for the "remember me" functionality
}
/**
* #param string $value
* #return void
*/
public function setRememberToken($value)
{
// Store a new token user for the "remember me" functionality
}
/**
* #return string
*/
public function getRememberTokenName()
{
// Return the name of the column / attribute used to store the "remember me" token
}
}
And in my controller after using Auth::login($user); when I check is user logged in by Auth::check() it alwys return false.
Anyone who can help me?
Try using Auth::attempt($username,$password)
if (Auth::attempt($username,$password)) {
//user is logged in
} else {
//user is not logged in
}
Auth::check() simply checks if the user is logged in. It doesn't actually process the login.
For custom Authentication, we need in to implement these 4 classes in laravel 5.4. And this implementation is working for me in a live project.
namespace App\Auth;
use Illuminate\Contracts\Auth\Authenticatable as User_Authenticatable;
class Authenticatable implements User_Authenticatable
{
/**
* The column name of the "remember me" token.
*
* #var string
*/
protected $rememberTokenName = 'remember_token';
/**
* 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->{$this->getAuthIdentifierName()};
}
/**
* 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()
{
if (! empty($this->getRememberTokenName())) {
return $this->{$this->getRememberTokenName()};
}
}
/**
* Set the token value for the "remember me" session.
*
* #param string $value
* #return void
*/
public function setRememberToken($value)
{
if (! empty($this->getRememberTokenName())) {
$this->{$this->getRememberTokenName()} = $value;
}
}
/**
* Get the column name for the "remember me" token.
*
* #return string
*/
public function getRememberTokenName()
{
return $this->rememberTokenName;
}
}
2nd
namespace App\Auth;
use Illuminate\Contracts\Auth\Access\Authorizable as User_Authorizable;
use Illuminate\Contracts\Auth\Access\Gate;
Class Authorizable Implements User_Authorizable
{
/**
* Determine if the entity has a given ability.
*
* #param string $ability
* #param array|mixed $arguments
* #return bool
*/
public function can($ability, $arguments = [])
{
return app(Gate::class)->forUser($this)->check($ability, $arguments);
}
/**
* Determine if the entity does not have a given ability.
*
* #param string $ability
* #param array|mixed $arguments
* #return bool
*/
public function cant($ability, $arguments = [])
{
return ! $this->can($ability, $arguments);
}
/**
* Determine if the entity does not have a given ability.
*
* #param string $ability
* #param array|mixed $arguments
* #return bool
*/
public function cannot($ability, $arguments = [])
{
return $this->cant($ability, $arguments);
}
}
3rd
namespace App\Auth;
use Illuminate\Auth\Notifications\ResetPassword as ResetPasswordNotification;
class CanResetPassword extends ResetPasswordNotification
{
}
4th
<?php
namespace App\Auth;
use App\Http\Controllers\ApiCallsController;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider as IlluminateUserProvider;
use Config;
use App\Models\ApiUser;
class UserProvider implements IlluminateUserProvider
{
/**
* #param mixed $identifier
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveById($identifier)
{
// Get and return a user by their unique identifier
return ApiUser::getUser(null, null, $identifier);
}
/**
* #param mixed $identifier
* #param string $token
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveByToken($identifier, $token)
{
// Get and return a user by their unique identifier and "remember me" token
}
/**
* #param \Illuminate\Contracts\Auth\Authenticatable $user
* #param string $token
* #return void
*/
public function updateRememberToken(Authenticatable $user, $token)
{
// Save the given "remember me" token for the given user
}
/**
* Retrieve a user by the given credentials.
*
* #param array $credentials
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveByCredentials(array $credentials)
{
// Get and return a user by looking up the given credentials
return ApiUser::getUser($credentials['c2MUsername']);
}
/**
* Validate a user against the given credentials.
*
* #param \Illuminate\Contracts\Auth\Authenticatable $user
* #param array $credentials
* #return bool
*/
public function validateCredentials(Authenticatable $user, array $credentials)
{
// Check that given credentials belong to the given user
$api_user = \App\Models\ApiUser::getUser($credentials['c2MUsername']);
if(!empty($api_user)){
return true;
}
return false;
}
}
My Custom Auth Provider
<?php
/**
* Created by PhpStorm.
* User: CresenTech
* Date: 23-Oct-17
* Time: 7:19 PM
*/
namespace App\Providers;
use Illuminate\Support\Facades\Auth;
use App\Auth\UserProvider;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class CustomAuthProvider extends ServiceProvider {
/**
* The policy mappings for the application.
*
* #var array
*/
// protected $policies = [
//// 'App\Model' => 'App\Policies\ModelPolicy',
// ];
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot()
{
/**
* Register any authentication / authorization services.
*
* #return void
*/
// $this->registerPolicies();
Auth::provider('custom',function()
{
return new UserProvider();
});
}
/**
* Register the application services.
*
* #return void
*/
public function register()
{
//
}
}
And my auth.php in config directory
<?php
return [
/*
|--------------------------------------------------------------------------
| Authentication Defaults
|--------------------------------------------------------------------------
|
| This option controls the default authentication "guard" and password
| reset options for your application. You may change these defaults
| as required, but they're a perfect start for most applications.
|
*/
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
/*
|--------------------------------------------------------------------------
| Authentication Guards
|--------------------------------------------------------------------------
|
| Next, you may define every authentication guard for your application.
| Of course, a great default configuration has been defined for you
| here which uses session storage and the Eloquent user provider.
|
| All authentication drivers have a user provider. This defines how the
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data.
|
| Supported: "session", "token"
|
*/
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
],
//
// 'c2m' => [
// 'driver' => 'session',
// 'provider' => 'users',
// ],
],
/*
|--------------------------------------------------------------------------
| User Providers
|--------------------------------------------------------------------------
|
| All authentication drivers have a user provider. This defines how the
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data.
|
| If you have multiple user tables or models you may configure multiple
| sources which represent each model / table. These sources may then
| be assigned to any extra authentication guards you have defined.
|
| Supported: "database", "eloquent"
|
*/
'providers' => [
// 'users' => [
// 'driver' => 'eloquent',
// 'model' => App\User::class,
// ],
'users' => [
'driver' => 'custom',
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
/*
|--------------------------------------------------------------------------
| Resetting Passwords
|--------------------------------------------------------------------------
|
| You may specify multiple password reset configurations if you have more
| than one user table or model in the application and you want to have
| separate password reset settings based on the specific user types.
|
| The expire time is the number of minutes that the reset token should be
| considered valid. This security feature keeps tokens short-lived so
| they have less time to be guessed. You may change this as needed.
|
*/
'passwords' => [
'users' => [
'provider' => 'users',
// 'table' => 'password_resets',
'expire' => 60,
],
],
];

Changing Laravel 5.4 password encryption and table column names

I am trying to integrate the auth in laravel 5.4 within an existing database where the user and password fields have other names (memberid, passwordnew_enc). With the bellow changes and forcing the create function in RegisterController to use MD5 I managed to make the registration work. It also logins fine after registration. However the actual login form returns:
These credentials do not match our records.
So far I have changed the User.php
public function getAuthPassword()
{
return $this->passwordnew_enc;
}
and
public function setPasswordAttribute($value)
{
$this->attributes['password'] = md5($value);
}
Also on LoginController.php
public function username()
{
return 'memberid';
}
Did I miss something ?
I only need to change the two column names to fit and the password encryption from bcrypt to md5
I would make custom user provider php artisan make:provider CustomUserProvider:
<?php
namespace App\Providers;
use Illuminate\Auth\EloquentUserProvider;
use Illuminate\Contracts\Auth\Authenticatable as UserContract;
class CustomUserProvider extends EloquentUserProvider {
/**
* Validate a user against the given credentials.
*
* #param \Illuminate\Contracts\Auth\Authenticatable $user
* #param array $credentials
* #return bool
*/
public function validateCredentials(UserContract $user, array $credentials)
{
$plain = $credentials['password']; // will depend on the name of the input on the login form
$hashedValue = $user->getAuthPassword();
if ($this->hasher->needsRehash($hashedValue) && $hashedValue === md5($plain)) {
$user->passwordnew_enc = bcrypt($plain);
$user->save();
}
return $this->hasher->check($plain, $user->getAuthPassword());
}
}
This way if the password exists using md5 it will allow it to work once and then rehash it.
You will register the CustomUserProvider in App\Providers\AuthServiceProvider boot() as follows:
$this->app['auth']->provider('custom', function ($app, array $config) {
$model = $app['config']['auth.providers.users.model'];
return new CustomUserProvider($app['hash'], $model);
});
Edit your config/auth.php
'providers' => [
'users' => [
'driver' => 'custom',
'model' => App\User::class,
],
],
You will also need to add the following as mentioned previously...
app\Http\Controllers\Auth\LoginController.php
public function username()
{
return 'memberid';
}
app\User.php
public function getAuthIdentifierName()
{
return 'memberid';
}
public function getAuthIdentifier()
{
return $this->memberid;
}
public function getAuthPassword()
{
return $this->passwordnew_enc;
}
Alright I got it
app\User.php
public function setPasswordAttribute($value)
{
$this->attributes['password'] = md5($value);
}
public function getAuthPassword()
{
return $this->passwordnew_enc;
}
public function getAuthIdentifierName()
{
return 'memberid';
}
app\Http\Controllers\Auth\LoginController.php
public function username()
{
return 'memb___id';
}
config\app.php
// Illuminate\Hashing\HashServiceProvider::class,
App\Providers\MD5HashServiceProvider::class,
app\Providers\MD5HashServiceProvider.php
<?php namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class MD5HashServiceProvider extends ServiceProvider
{
/**
* Indicates if loading of the provider is deferred.
*
* #var bool
*/
protected $defer = true;
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
$this->app->singleton('hash', function () {
return new \MD5Hasher;
});
}
/**
* Get the services provided by the provider.
*
* #return array
*/
public function provides()
{
return ['hash'];
}
}
lib\MD5Hasher\MD5Hasher.php
<?php
class MD5Hasher implements Illuminate\Contracts\Hashing\Hasher
{
/**
* Hash the given value.
*
* #param string $value
* #return array $options
* #return string
*/
public function make($value, array $options = array())
{
return md5($value); //hash('md5', $value);
}
/**
* Check the given plain value against a hash.
*
* #param string $value
* #param string $hashedValue
* #param array $options
* #return bool
*/
public function check($value, $hashedValue, array $options = array())
{
return $this->make($value) === $hashedValue;
}
/**
* Check if the given hash has been hashed using the given options.
*
* #param string $hashedValue
* #param array $options
* #return bool
*/
public function needsRehash($hashedValue, array $options = array())
{
return false;
}
}
composer.json
...
"autoload": {
"classmap": [
...
"app/Lib"
],
...
upful's code worked for me (in Laravel 5.4)
But I needed to add:
use Illuminate\Contracts\Auth\Authenticatable as UserContract;
in the CustomUserProvider class.

Laravel 5 Auth:attempt() always returns false

I am trying to make a custom login with multi auth. For the meantime, I am trying to do the login for admin. When an admin logs in, the login function handles it (it also just refreshes without the login function) Auth:attempt() seems to be always returning false, however (I have a different table name and fields). Aside from that, I can freely access the dashboard by just changing the url even if the user is not really logged in.
AuthController
/*
|--------------------------------------------------------------------------
| Registration & Login Controller
|--------------------------------------------------------------------------
|
| This controller handles the registration of new users, as well as the
| authentication of existing users. By default, this controller uses
| a simple trait to add these behaviors. Why don't you explore it?
|
*/
use AuthenticatesAndRegistersUsers, ThrottlesLogins;
/**
* Where to redirect users after login / registration.
*
* #var string
*/
protected $redirectTo = 'admin/dashboard';
/**
* Where to redirect users after logout.
*
* #var string
*/
protected $redirectAfterLogout = 'admin/login';
/**
* Guard for admin
*
*
*/
protected $guard = 'admin';
/**
* Create a new authentication controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware($this->guestMiddleware(), ['except' => 'logout']);
}
/**
* Get a validator for an incoming registration request.
*
* #param array $data
* #return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
{
return Validator::make($data, [
'OUsername' => 'required|max:255|unique:users',
'OPassword' => 'required|min:6|confirmed',
]);
}
/**
* Create a new user instance after a valid registration.
*
* #param array $data
* #return User
*/
protected function create(array $data)
{
return Admin::create([
'OUsername' => $data['OUsername'],
'OPassword' => bcrypt($data['OPassword']),
]);
}
/**
* Show login form.
*
*
*
*/
public function showLoginForm()
{
if (view()->exists('auth.authenticate')) {
return view('auth.authenticate');
}
return view('pages.admin.login');
}
/**
* Show registration form.
*
*
*
*/
public function showRegistrationForm()
{
return view('pages.admin.register');
}
public function login(Request $request)
{
//Get inputs
$username = $request->input('username');
$password = $request->input('password');
//Redirect accordingly
if (Auth::guard('admin')->attempt(array('OUsername' => $username, 'OPassword' => $password)))
{
return redirect()->intended('admin/dashboard');
}
else
{
//when echoing something here it is always displayed thus admin login is just refreshed.
return redirect('admin/login')->withInput()->with('message', 'Login Failed');
}
}
Admin Provider Model
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'account_officer_t';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'OUsername', 'OPassword',
];
public $timestamps = false;
/**
* Set primary key
*
* #var int
*/
protected $primaryKey = 'AccountOfficerID';
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'OPassword', 'remember_token',
];
public function getAuthPassword()
{
return $this->OPassword;
}
Routes
/*
|--------------------------------------------------------------------------
| Application Routes
|--------------------------------------------------------------------------
|
| Here is where you can register all of the routes for an application.
| It's a breeze. Simply tell Laravel the URIs it should respond to
| and give it the controller to call when that URI is requested.
|
*/
Route::group(['namespace' => 'Admin', 'middleware' => 'guest'], function(){
//This uses the guest middleware with the class name RedirectIfAuthenticated
Route::auth();
//Route for admin dashboard view
Route::get('admin/dashboard', array('as' => 'dashboard', 'uses' => 'AdminController#showDashboard'));
});
Route::group(['middleware' => ['web']], function () {
//Route for login
Route::get('admin/login','AdminAuth\AuthController#showLoginForm');
Route::post('admin/login','AdminAuth\AuthController#login');
Route::get('admin/logout','AdminAuth\AuthController#logout');
//Route for registration
Route::get('admin/ims-register', 'AdminAuth\AuthController#showRegistrationForm');
Route::post('admin/ims-register', 'AdminAuth\AuthController#register');
});
RedirectIfAuthenticated (guest middleware)
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #param string|null $guard
* #return mixed
*/
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard('admin')->check()) {
return redirect('admin/dashboard');
}
if (Auth::guard($guard)->check()) {
return redirect('/');
}
return $next($request);
}
I have just started learning the MVC framework and started using Laravel. Thank you for the help.
Notes
My passwords are stored using bcrypt() with column length of 255
I have tried checking if the hash from the table matches my input using Hash::check. It returns true. But when I do this:
dd( Auth::guard('admin')->attempt(array('OUsername' => $username, 'OPassword' => $password)));
It is false.
Tried checking the results based on the answer from this question especially # 7. Still the same.
The problem seems to be with this line
'OPassword' => $password
I changed it to
'password' => $password
It has to be password not OPassword. And then in my Admin model I specified
public function getAuthPassword()
{
return $this->OPassword;
}

Unable to get Policies working in Laravel 5.3

I've been following the Laravel Authorization docs trying to build "is the user allowed to do this" functionality by using Policies, but I can't get it to work. I keep getting This action is unauthorized and I've tried with route middleware too.
PagePolicy.php:
namespace App\Policies;
use App\Models\User;
use App\Models\Page;
use Illuminate\Auth\Access\HandlesAuthorization;
class PagePolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can view the page.
*
* #param App\Models\User $user
* #param App\Models\Page $page
* #return mixed
*/
public function view(User $user, Page $page)
{
return $user->id === $page->user_id;
}
/**
* Determine whether the user can create pages.
*
* #param App\Models\User $user
* #return mixed
*/
public function create(User $user)
{
}
/**
* Determine whether the user can update the page.
*
* #param App\Models\User $user
* #param App\Models\Page $page
* #return mixed
*/
public function update(User $user, Page $page)
{
//
}
/**
* Determine whether the user can delete the page.
*
* #param App\Models\User $user
* #param App\Models\Page $page
* #return mixed
*/
public function delete(User $user, Page $page)
{
//
}
}
PageController.php:
namespace App\Http\Controllers;
use Auth;
use Carbon\Carbon;
use App\Models\Page;
use App\Http\Requests\PageRequest;
class PageController extends ApiController
{
public function createNewPage(PageRequest $request)
{
$this->authorize('create', Page::class);
$request->merge([
'user_id' => Auth::id(),
'published_at' => Carbon::now(),
]);
if (Page::create($request->all())) {
return response()->json('success', 201);
}
return response()->json('error', 500);
}
}
AuthServiceProvidor.php:
namespace App\Providers;
use App\Models\Page;
use App\Policies\PagePolicy;
use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* #var array
*/
protected $policies = [
Page::class => PagePolicy::class,
];
/**
* Register any authentication / authorization services.
*
* #return void
*/
public function boot()
{
$this->registerPolicies();
//
}
}
I managed to figure it out. I wasn't using Route Model Binding. So I added authorize() after the page call and used the $page variable instead of Page::class.
public function update(PageUpdateRequest $request, $pageSlug)
{
$page = Page::where(['user_id' => Auth::id(), 'slug' => $pageSlug])->first();
$this->authorize('update', $page);
$page->update($request->all());
return fractal()->item($page, new PageTransformer())->toArray();
}
It's not totally clear to me which action you're attempting to authorize since you've provided the call to create in the controller but only provided a policy check in place for viewing a page. Having said that, I would be sure to var_dump/dd the values you're attempting to do a type comparison of to verify they're of the same type. If anything's been explicitly cast, it may cause issues with certain database drivers that return integers as strings.
I think the problem is not in your policies, rather in your PageRequest class. Make sure the authorize() method in your App\Http\Requests\PageRequest class returns true .
class PageRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true; // you can also check the authorization using PagePolicy here
}
}
Current code:
protected $policies = [
Task::class => TaskPolicy::class,
];
Solution code:
protected $policies = [
'App\Task' => 'App\Policies\TaskPolicy',
];
I experienced the same problem, while following the Intermediate Task List Tutorial on the Laravel website.
The solution is actually present in the Github code for this tutorial.

Laravel 5.2 Error In Custom Authentication

I am getting error while making custom authentication for my laravel 5.2 however this code works fine on my laravel 5.1 My config/auth.php file
'providers' => [
'users' => [
'driver' => 'custom',
'model' => App\User::class,
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
My CustomUserProvider.php (Auth/CustomUserProvider) file
<?php namespace App\Auth;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Contracts\Hashing\Hasher as HasherContract;
use Illuminate\Contracts\Auth\Authenticatable as UserContract;
class CustomUserProvider implements UserProvider {
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
<?php namespace App\Providers;
use App\User;
use 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();
});
}
/**
* Register the application services.
*
* #return void
*/
public function register()
{
//
}
}
Now this works fine in laravel 5.1 in 5.2 i am getting error like
InvalidArgumentException in CreatesUserProviders.php line 40:
Authentication user provider [custom] is not defined.
The only point is to use
$this->app['auth']->provider(...
instead of
$this->app['auth']->extend(...
The last one is used in 5.1, the first one should be used in 5.2.
app/Models/User.php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable {
protected $connection='conn';
protected $table='users-custom';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'login', 'passwd'
];
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = [
'passwd',
];
public function getAuthPassword(){
//your passwor field name
return $this->passwd;
}
public $timestamps = false;
}
create app/Auth/CustomUserProvider.php
namespace App\Auth;
use Illuminate\Support\Str;
use Illuminate\Contracts\Auth\Authenticatable as UserContract;
use Illuminate\Contracts\Auth\UserProvider;
/**
* Description of CustomUserProvider
*
*/
class CustomUserProvider implements UserProvider {
/**
* The hasher implementation.
*
* #var \Illuminate\Contracts\Hashing\Hasher
*/
protected $hasher;
/**
* The Eloquent user model.
*
* #var string
*/
protected $model;
/**
* Create a new database user provider.
*
* #param \Illuminate\Contracts\Hashing\Hasher $hasher
* #param string $model class name of model
* #return void
*/
public function __construct($model) {
$this->model = $model;
}
/**
* Retrieve a user by their unique identifier.
*
* #param mixed $identifier
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveById($identifier) {
return $this->createModel()->newQuery()->find($identifier);
}
/**
* Retrieve a user by their unique identifier and "remember me" token.
*
* #param mixed $identifier
* #param string $token
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveByToken($identifier, $token) {
$model = $this->createModel();
return $model->newQuery()
->where($model->getAuthIdentifierName(), $identifier)
->where($model->getRememberTokenName(), $token)
->first();
}
/**
* Update the "remember me" token for the given user in storage.
*
* #param \Illuminate\Contracts\Auth\Authenticatable $user
* #param string $token
* #return void
*/
public function updateRememberToken(UserContract $user, $token) {
$user->setRememberToken($token);
$user->save();
}
/**
* Retrieve a user by the given credentials.
*
* #param array $credentials
* #return \Illuminate\Contracts\Auth\Authenticatable|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.
$query = $this->createModel()->newQuery();
foreach ($credentials as $key => $value) {
if (!Str::contains($key, 'password')) {
$query->where($key, $value);
}
}
return $query->first();
}
/**
* Validate a user against the given credentials.
*
* #param \Illuminate\Contracts\Auth\Authenticatable $user
* #param array $credentials
* #return bool
*/
public function validateCredentials(UserContract $user, array $credentials) {
//your method auth
$plain = $credentials['password'];
return md5($plain)==md5($user->getAuthPassword());
}
/**
* Create a new instance of the model.
*
* #return \Illuminate\Database\Eloquent\Model
*/
public function createModel() {
$class = '\\' . ltrim($this->model, '\\');
return new $class;
}
/**
* Gets the hasher implementation.
*
* #return \Illuminate\Contracts\Hashing\Hasher
*/
public function getHasher() {
return $this->hasher;
}
/**
* Sets the hasher implementation.
*
* #param \Illuminate\Contracts\Hashing\Hasher $hasher
* #return $this
*/
public function setHasher(HasherContract $hasher) {
$this->hasher = $hasher;
return $this;
}
/**
* Gets the name of the Eloquent user model.
*
* #return string
*/
public function getModel() {
return $this->model;
}
/**
* Sets the name of the Eloquent user model.
*
* #param string $model
* #return $this
*/
public function setModel($model) {
$this->model = $model;
return $this;
}
}
in config/auth.php
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users_office',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
'users_office' => [
'driver' => 'customUser',
'model' => App\Models\User::class,
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
in \vendor\laravel\framework\src\Illuminate\AuthCreatesUserProviders.php
public function createUserProvider($provider)
{
$config = $this->app['config']['auth.providers.'.$provider];
if (isset($this->customProviderCreators[$config['driver']])) {
return call_user_func(
$this->customProviderCreators[$config['driver']], $this->app, $config
);
}
switch ($config['driver']) {
case 'database':
return $this->createDatabaseProvider($config);
case 'eloquent':
return $this->createEloquentProvider($config);
case 'customUser':
return $this->createCustomUserProvider($config);
default:
throw new InvalidArgumentException("Authentication user provider [{$config['driver']}] is not defined.");
}
}
protected function createCustomUserProvider($config){
return new \App\Auth\CustomUserProvider($config['model']);
}
add App\Providers\CustomUserAuthProvider.php
namespace App\Providers;
use Auth;
use App\Models\User;
use App\Auth\CustomUserProvider;
use Illuminate\Support\ServiceProvider;
/**
* Description of CustomAuthProvider
*
*/
class CustomUserAuthProvider extends ServiceProvider {
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot()
{
Auth::extend('customUser', function($app) {
// Return an instance of Illuminate\Contracts\Auth\UserProvider...
return new CustomUserProvider(new User);
});
}
/**
* Register the application services.
*
* #return void
*/
public function register()
{
//
}
}
Try by replacing the boot function as below:
public function boot()
{
Auth::provider('custom', function($app, array $config) {
// Return an instance of Illuminate\Contracts\Auth\UserProvider...
return new CustomUserProvider($app['custom.connection']);
});
}
Replace the boot function as below:
public function boot()
{
Auth::provider('customUser', function($app, array $config) {
return new CustomUserProvider($config['model']);
});
}

Categories