Framework: Authorizing Channels with custom guard

Created on 1 Apr 2017  路  6Comments  路  Source: laravel/framework

  • Laravel Version: 5.3.30
  • PHP Version: PHP 5.6.30-0+deb8u1 (cli)
  • Database Driver & Version: mysql

I try connect to authorize channel with custom guard and have error 'Client can not be authenticated, got HTTP status 403' in laravel echo server error log. And I have 403 exception here

(/vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/Broadcaster.php)

/**
 * Authenticate the incoming request for a given channel.
*
* @param  \Illuminate\Http\Request  $request
* @param  string  $channel
* @return mixed
*/
   protected function verifyUserCanAccessChannel($request, $channel)
   {
       foreach ($this->channels as $pattern => $callback) {
           if (! Str::is($pattern, $channel)) {
               continue;
           }

        $parameters = $this->extractAuthParameters($pattern, $channel);

        if ($result = $callback($request->user(), ...$parameters)) {
            return $this->validAuthenticationResponse($request, $result);
        }
    }

    throw new HttpException(403);
}

Because $request->user() return null with custom guard.

I change it to $request->user('my_custom_guard') and now it work. But it don't work with standard users.

Any ideas?

How can i redefine it for my case?

UPD:
I suggest add guard parameter to

    /**
     * Register a channel authenticator.
     *
     * @param  string  $channel
     * @param  callable  $callback
     * @return $this
     */
    public function channel($channel, callable $callback)
    {
        $this->channels[$channel] = $callback;

        return $this;
    }

with default empty value.

UPD2: I did Temporary change

if ($result = $callback($request->user(), ...$parameters)) {
            return $this->validAuthenticationResponse($request, $result);
        }

to

if ($result = $callback($request, ...$parameters)) {
            return $this->validAuthenticationResponse($request, $result);
        }

and check it in BroadcastServiceProvider

  Broadcast::channel('Dashboard.User.*', function ($request, $userId) {
        return (int) $request->user('my-custom-guard')->id === (int) $userId;
    });

Most helpful comment

maybe it will be useful for anyone
RedisBroadcaster always check default guard at line $request->user() but you can use setUserResolver to use required guard for example at middleware

$request->setUserResolver(function(){
                // check user here according you business logic and return it 
                // for example $user = Auth::guard('admin')->user();
                if ($user) {
                    return $user;
                }
                return null;
            });

not the best solution but better then edit code at vendor

All 6 comments

Now i have one more problem in /vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/RedisBroadcaster.php on line 49

    /**
     * Authenticate the incoming request for a given channel.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return mixed
     */
    public function auth($request)
    {
        if (Str::startsWith($request->channel_name, ['private-', 'presence-']) &&
            ! $request->user()) {
        throw new HttpException(403);
        }

        $channelName = Str::startsWith($request->channel_name, 'private-')
                            ? Str::replaceFirst('private-', '', $request->channel_name)
                            : Str::replaceFirst('presence-', '', $request->channel_name);

        return parent::verifyUserCanAccessChannel(
            $request, $channelName
        );
    }

How i can pass my custom auth guard name to it? (((

UPD: I did temporary comment this lines

   if (Str::startsWith($request->channel_name, ['private-', 'presence-']) &&
            ! $request->user()) {
        throw new HttpException(403);
        }

It is duplicate a parent functionally

oK, now i think the best way to fix it add guard name param to channel pattern.

@mediaceh How did you solve it ?

Globally or temporary? Temporary solution i describe up there. If you have any questions - welcome.

@mediaceh you modified in the core I mean vendor files which will not work on other copies of the projects cause it comes from vendor which is ignored via VCS. So, it will only on your working copy.
Can it be passed via the routes or in any other way
It always checks with the default guard defined in auth.php.

@taylorotwell can you give some light on this it will be very helpfull 馃憤

I have also mentioned the same issue in question on SO

maybe it will be useful for anyone
RedisBroadcaster always check default guard at line $request->user() but you can use setUserResolver to use required guard for example at middleware

$request->setUserResolver(function(){
                // check user here according you business logic and return it 
                // for example $user = Auth::guard('admin')->user();
                if ($user) {
                    return $user;
                }
                return null;
            });

not the best solution but better then edit code at vendor

Was this page helpful?
0 / 5 - 0 ratings

Related issues

gabriellimo picture gabriellimo  路  3Comments

Anahkiasen picture Anahkiasen  路  3Comments

SachinAgarwal1337 picture SachinAgarwal1337  路  3Comments

klimentLambevski picture klimentLambevski  路  3Comments

ghost picture ghost  路  3Comments