Laravel Broadcast Authentication Not Using Standard User Auth - php

I've spent a couple of days on this now and can not get my head around it. I'm using this Laravel Package Ohmybrew/Laravel-Shopify which does not use the standard Laravel auth as they have their own middleware auth.shop
The issue I am facing is because it does not use the $user variable as the default Auth is not there, broadcast authentication for WebSockets on private channels is always 403 not authorised.
I've tried returning true usings another model, does not work.
Broadcast::channel('shop.{id}', function ($user, $id) {
$shop = ShopifyApp::shop();
return (int) $shop->id === (int) $id;
});
I've also tried just returning true no matter what just to see. Also no luck.
Broadcast::channel('shop.{id}', function ($user, $id) {
return true
});
I've even messed about with the PusherBroadcast.php and BroadcastManager to remove throwing the not authenticated exception. This also does not work.
I'm still getting to grips with the way Laravel works as a whole package so not sure where to look next. I've had a read up on custom auth guards but not sure that will work as the package has its own authentication.
Any steer would be greatly appreciated as I am a little out of things to try. It seems anything I do to get onto a private channel does not work.
Note: It's worth mentioning a test public channel for listening to the WebSockets works fine with the current set up. it is only the private channel authentication which is not working.
Update: Here is how I am listening to the channels
window.Echo.private('shop.{{ ShopifyApp::shop()->id }}')
.listen('ScheduleProcessed', function(e) {
console.log(e);
});
window.Echo.channel(`test`) // Broadcast channel name
.listen('BroadcastTestEvent', (e) => { // Message name
console.log(e); // The operation performed by the message, the parameter e is the data carried
alert("GOT S**T");
}
);
The second one for the test channel works completely fine. The 'shop' channel does not and it's partly down to broadcasting/auth always returns 403 when the client tries to connect to the private channel.
Update 2
Seems to have something to do with the fact the default auth needs to use the $request->user() which is empty as the custom auth does not use it at all.
I've read that binding the user might work but not 100% sure how.
$request->merge(['user' => $user]);
//add this
$request->setUserResolver(function () use ($user) {
return $user;
});

You can specify your own middleware for Broadcast routes. Something like the following should work:
Broadcast::routes(['middleware' => 'auth.shop']);
That should then use the Laravel-Shopify package to authenticate the user.
If you are using sessions, you might also need to specify that you want to use the web middleware too.
Broadcast::routes(['middleware' => ['web', 'auth.shop']]);

Related

/broadcasting/auth endpoint always fails with 403 (access denied) error when trying to authenticate with Sanctum

I haven't been able to find too many answers on specifically authenticating broadcasting using Sanctum. I'm trying to implement a basic event that broadcasts to pusher, but I keep getting a 403 error when trying to connect to /broadcasting/auth. Firstly, yes, I have uncommented the BroadcastServiceProvider in /config/app.php. Here is an extract from my Broadcast service provider:
app/Providers/BroadcastServiceProvider.php
class BroadcastServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
Broadcast::routes(['middleware' => ['auth:sanctum']]);
require base_path('routes/channels.php');
}
}
I authenticate using Sanctum and all of my endpoints live in the api.php file. I'm not sure whether I should be declaring the Broadcast::routes there, as I've seen some people allude to it, but I don't really know what difference it makes as it still gives me the same error. I also don't even know if calling auth:sanctum middleware is valid here. I mean, I know it works for all of my api routes, but I'm not sure about in this Provider file.
I'm trying to hit the /broadcasting/auth endpoint with Postman, while including my Sanctum Authorization header (Bearer [token]) in the request, but it just gives me a 403.
Can someone please point me in the right direction here?
I also had a similar issue but I was able to resolve it by adding the channel on which I'm broadcasting on in the channel.php file inside the route directory.
channel.php
Broadcast::channel('request_channel.{device_id}', function ($user, $device_id) {
return (int) $user->id === (int) \App\UserDevice::find($device_id)->user_id;
});

Laravel dingo/api - Using internal routes with Passport (L6)

I see quite a few people having a similar issue with this, but no final resolved solutions. I have been trying to get this working for about 24 hours now and still no luck!
Goals
Build and API using Laravel 6 and Dingo API
Be able to consume the API externally, authenticating with Passport oAuth.
Be able to consume the API internally, via ajax, using passports self-authenticating feature.
Be able to consume the API internally, with PHP, using dingo's self-consuming methods.
What I have found out so far
Auth provider order
Most solutions I have seen suggest setting up both the passport auth and dingo alongside one another. This is auth:api (passport) and api.auth (dingo).
// API route middleware
$api->group(['middleware' => 'auth:api', 'api.auth'], function (Router $api) {
...
The api.auth here is actually a custom auth provider setup in laravel and configured to dingo, which bridges the passport logic into dingo.
// Auth provider
class DingoPassportAuthProvider extends Authorization
{
protected $guard;
public function __construct(AuthManager $auth)
{
dump('DingoPassportAuthProvider Instantiated');
$this->guard = $auth->guard('api');
}
public function authenticate(Request $request, Route $route)
{
if ($this->guard->check()) {
return $this->guard->user();
}
throw new UnauthorizedHttpException('Not authenticated via Passport.');
}
public function getAuthorizationMethod()
{
return 'Bearer';
}
}
// Configured in dingo (Api.php)
'auth' => [
'passport' => \App\Providers\DingoPassportAuthProvider::class,
],
If we put the dingo API provider first in the middleware stack we get:
Internal API requests work IF you specify the user for the call with the be() method: $this->api->be($request->user())->get('/api/profile')
External API requests and internal AJAX requests authenticate correctly and the user is returned from the custom dingo auth provider, however, for some reason you cannot then access this user from within the API controllers: $user = $request->user(); // null
If we put the Passport API provider first in the middleware stack we get:
Internal API requests do not work at all (401 always returned)
External API requests and internal AJAX requests work as intended.
The authenticate method on the dingo passport provider is no longer called. I think this may have something to do with the 401 returned on internal calls.
I believe the correct way around, is to put the passport authentication first. This way, we authenticate the user before calling the dingo authentication, resulting in 2 things:
Passport works natively as expected.
Dingo internal API calls should now just be able to be called with $this->api->get('/api/profile') (omit defining the user with be()), however this does not work.
At the moment I have the previous configuration. Passport works as intended for external and ajax calls, but the internal dingo calls always return 401.
There are a few boilerplate templates I have checked out and they do not seem to do anything different. I wonder if something changed in L6 to explain why the internal requests do not work.
I have found one work around for now, which gets most of the way there...
Within the custom dingo auth provider:
class DingoPassportAuthProvider extends Authorization
{
public function authenticate(Request $request, Route $route)
{
if (Auth::guard('web')->check()) {
return Auth::guard('web')->user();
}
if (Auth::guard('api')->check()) {
$user = Auth::guard('api')->user();
Passport::actingAs($user);
return $user;
}
throw new UnauthorizedHttpException('Not authenticated via Passport.');
}
public function getAuthorizationMethod()
{
return 'Bearer';
}
}
This now checks to see if the request is coming from either the web guard (internal request) or the api guard (external or ajax request) and returns the correct user.
For the api guard, there seems to be an issue that the user is authenticated but not actually available within the controllers. To get around this I added the Passport::actingAs($user). It is probably not best practice, but the guards are now acting as they should and as are all my different scenarios.
Then in the API route middleware, we only specify the custom dingo provider.
// API route middleware
$api->group(['middleware' => 'api.auth'], function (Router $api) {
...
One thing to note with this, is dingos be() method does not work quite as expected. Instead you need to switch the user as you would in a general laravel app.
\Auth::loginUsingId(2);
$user = $this->api->get('/api/profile');

Laravel REST API - infinite loop

I am building a REST user-microservice using Laravel 5.5 + Passport.
I am using the standard Passport::routes(), but I have had to modify the Auth::routes in order to make them return JSON responses, and to make them work with Passport.
I have added the following lines to my routes/web.php file:
Route::group(['middleware' => 'auth:api'], function () {
$this->post('logout', 'Auth\LoginController#logout')->name('logout');
});
This allows me to POST https://myapi/logout
If I make the call with the header "Authorization => Bearer TOKEN", I get a successful logout response.
If I provide no header at all, I get a "not authenticated" message (which is good)
However, if I provide the header with a revoked token, I get a recursive deadloop of the function: Illuminate\Auth\RequestGuard->user() (it keeps calling itself recursively until stack-overflow)
This is all done in the auth:api middleware, my logout code is not reached, but my LoginController constructor is called. Constructor code:
public function __construct(Application $app)
{
$this->apiConsumer = $app->make('apiconsumer');
$this->middleware('guest')
->except('logout');
}
I'm struggling to understand if it's my code causing this issue, or some combination of Laravel + passport + auth.
My first thought was that the auth:api middleware fails to authenticate the user, and as a result redirects the user to /home, where for some reason it's triggered again, recursively. But if that was the case, why would it work correctly with no header?
My current thinking is that the token in question does exist in the database, but Laravel is failing to figure out that it's revoked.
Any suggestions appreciated,
I found an answer (if not the answer) after a lot of research. It appears this is a Laravel bug (https://github.com/laravel/passport/issues/440). The solution is to add OAuthServerException to the $dontReport array in app/Exceptions/Handler.php:
class Handler extends ExceptionHandler
{
protected $dontReport = [
...
\League\OAuth2\Server\Exception\OAuthServerException::class,
];
}
This will avoid trying to log user information, thereby avoid the deadloop.
I have faced this in localhost. in my case, I have used xampp server and facing this issue
after creating a virtual host like "testlarave.test" then solve the error

Laravel 5.4 null csrf_token() when posting to route

I'm sending an ajax post request, and with Laravel it seems that is done by creating a post route for it. I've set it up so a csrf token is put in the header automaticaly for every ajax request using ajaxSetup. I'm attempting to then catch that header on the backend and verify the tokens match.
In my web routes (which automatically use the web middleware), this returns as expected:
Route::get('/test', function() {
return csrf_token();
});
However, when I post to a route via AJAX, like either of the below ways:
Attempt 1:
Route::post('/test', 'AjaxController#test');
In the AjaxController construct, followed by an alert in the view:
var_dump(csrf_token().',hi'); die;
Response: ',hi' (csrf_token was null).
Attempt 2:
Route::post('/test', ['test' => csrf_token().',hi', 'uses' => 'AjaxController#test']);
$test = $request->route()->getAction()['test'];
var_dump($test); die;
Response: ',hi' (csrf_token was null).
What I seem to be running into is, with get requests csrf_token() is populated, on my post request, it is not.
Any ideas?
check your route group it must apply the web middleware as
Route::group(['middleware' => 'web'], function () {
Route::get('/test', function() {
return csrf_token();
//or return $request->session()->token();
});
});
Finally figured this out.
CSRF can indeed be checked on an ajax post request. I wanted to make sure someone on their own site isn't hitting my ajax endpoint with any success of doing anything, especially for another user.
However, I ran into a Laravel order of operations issue, with the way Laravel sets up the session. I was trying to call a validation method (within in the same class) in the constructor, where I validated for CSRF and verified the requesting user all in one place. I wanted to do this so that any time someone hits this class, I didn't have to call the verification in each public method in the class, I'd only have to call it once.
However, csrf_token(), and the request session in general, is not available to me yet in my construct. It is, however, available to me in the method within the controller class that is called in the route.
For example, given the following route:
Route::post('/test', 'AjaxController#test');
If I injected Request into the construct and then tried to reference anything in the session (in the construct), or get the value of csrf_token(), it will throw an error, because Laravel hasn't set that stuff up yet. But if I reference either of those things in the test method, it'll be there and available just fine.
A bit of a weird Laravel order of operations issue.
csrf protections are managed by Laravel Forms. It won't be available when dealing with APIs.
You should have a look at how middlewares are used in Laravel
https://laravel.com/docs/5.4/middleware
Think using API middleware for your APIs ;)
If you run this command php artisan make:auth documented here https://laravel.com/docs/5.4/authentication#authentication-quickstart when going to resources/views/layouts/app.blade.php you'll see this:
<meta name="csrf-token" content="{{ csrf_token() }}">
And in app.js
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN':$('meta[name="csrf-token"]').attr('content')
}
});
In 5.3 there was this cool feature which looks as though it has since been removed in 5.4.
<script>
window.Laravel = <?php echo json_encode([
'csrfToken' => csrf_token(),
]); ?>
</script>
So what you need to do is add the csrf field to every request. Do the first 2 code snippets and you'll be fine. The 3rd I believe is probably for Vue.
Answer to your question: no, no, no and no. CSRF tokens I wouldn't believe are generated in POST requests, it's a Cross site Reference token, not an authentication token. If you're looking for something like authentication token refreshing then checkout JWT although the packages for JWT for laravel are a bit unfinished at the moment; with a little work you can get them working.
https://github.com/tymondesigns/jwt-auth 1.0.*#dev is pretty good. You can then use their refresh middleware to generate new tokens on request but this is quite advanced and unless it's for authentication then I wouldn't bother really.
I believe Dingo (another work in progress I believe) https://github.com/dingo/api uses the above package
Anything else let me know!

Laravel automatically logged out after few seconds?

I am developing web application using Laravel 5 and angularJs with RESTFUL apis.
Using middleware to authentication purpose. My problem is after sending few request simultaneously,system automatically logged out and sending 401 exception from laravel side.
API base controller:
class ApiController extends BaseController {
use DispatchesCommands, ValidatesRequests;
function __construct() {
$this->middleware('api.auth');
}
}
Middleware:
class APIMiddleware {
/**
* Handle an incoming request.
*
* #param Request $request
* #param Closure $next
* #return mixed
*/
public function handle($request, Closure $next) {
if (!Auth::check()) {
abort(401, "Unauthorized");
}
return $next($request);
}
}
Log in controller
public function login(LoginRequest $request) {
if (Auth::check()) {
Auth::logout();
}
if (Auth::attempt(['email' => $request->input('email'), 'password' => $request->input('password')], $request->input('is_remember'))) {
return array(true);
} else {
abort(401, "Invalid email & password");
}
}
After few request gone, Server log out and sends 401 exception. I am stuck with this issue.
Now I'm not 100% sure (and depending on your set-up I can't even say I'm 90% sure) But after changing my session_driver from file to database I seem to have fixed this issue - that is if it's the same issue.
I think do the samething as you with my app - that is on a start up of a page, I'm making 6 request (this is development and I will be changing it to one so please don't cry). If I load this page, it works with about 3 or 4 request, then the other 2-3 come back with a unauthorised response. It also only happens on request that require middleware => auth.
So here's my theory to why this is happening: Because, by default, sessions are saved in a file - making multiple requests at once means that file is being opened 6 times at once - probably messing it up (depending on your machine). Therefore changing the session to a database, which is designed to have thousands of requests at once, works!
SOLUTION:
Go to your .env file and change SESSION_DRIVER=file to SESSION_DRIVER=database.
Next you will need to create a session migration: php artisan session:table.
Now composer dump-autoload for good practice.
Finally migrate (php artisan migrate).
NOTE: I'm not 100% sure though if this is the case, but for me this solution worked. I am also aware that this question is really old, but both the developers I work with and myself have had this issue and there doesn't seem to be a solution, so Just though I'd post this.
Managed to figure it out.. Since i use laravel for pretty much all my projects, I forgot to change the session name, as a result, one session was overwriting the other, causing the auto-loggout.. So if you have multiple laravel projects running, make sure they all have different session names. Hope this helps someone in future !
Here is a Laracast thread on this issue.
For me this was the process to solve the problem:
Cleared my browser's cookies for localhost.
Changed value of cookie key in app/session.php.
Ran php artisan config:clear.
It may be a problem that you are accessing the user variable illegally. Please use Auth::check() before accessing Auth::user() This seems to work for my project. Optionally you can try for changing the session driver from .env file.
Might be useful for someone: Had the very same problem. I've changed the cookie name in session settings. By default it is laravel_session, so try setting it to something else
I solved the same issue by clearing cache using php artisan cache:clear and also running composer dump-autoload. Hope this works for you.
I had a similar problem this week. I have a server with multiple Laravel applications. One application was logging the other out.
The problem had to do with session management. The session name was the same for all the applications. Changing it would be enough to avoid different applications conflict. However, I can have different instances of the same application in the server (for testing purposes, for example). So, changing only the session name would not be enough.
To solve my problem properly, I used the session path to make the configuration unique per instance. In the config/session.php, I defined something like this:
'cookie' => 'systemx_session',
'path' => parse_url(env('APP_URL', 'http://localhost'), PHP_URL_PATH),
I use the parse_url function with the environment variable APP_URL because my server has the instances deployed under something like http://example.com/systemx.
I hope this helps someone who might end up having the same kind of problem.
I think you copied an old project for a new application, so you need to change the config/session.php
'cookie' => 'new_session',
I had a similar problem that the users didn't login at all & I found Its because of my authenticatable eloquent model, specified in my auth guard in config/auth.php (User in my case).
I was applying a global scope (in my case verified) so that users were filtered by a specific column & auth guard couldn't find the user so it logged out everytime ...
I solved my problem by this post https://laracasts.com/discuss/channels/laravel/ignore-global-scopes-for-auth

Categories