How do I authenticate an user to a channel with a custom auth method?
For example, in my app I use a token auth (stored in the db for each user) for my API, passed via header and read by a custom middleware.
How do I control the access with these custom auth methods for channels? How would be the client and server side? I am using socket.io and the Laravel docs are quite unclear of how auth methods works for sockets.
USE CASE:
I have users stored in DB with default Laravel migration. However, how my app is just the API, it is stateless, then, rather than using remember_token to store session tokens, I use it to store an unique authentication token, generated in the login endpoint.
Every endpoint of the API, the request pass through a middleware, where the user is retrieved by the token present in the headers
$token = Request::header('X-token');
$request->user = User::findByToken($token);
return $next($request);
Now the question comes in. Laravel docs isn't much clear on how private channels works.
How would a broadcast client pass the mentioned token and get authorized to join a given channel?
The Laravel documentation on broadcasting is pretty comprehensive about this, although it doesn't go into minute detail on client libraries that it doesn't support out of the box.
In this case, you're wanting the section on authorizing channels. From the docs:
In the BroadcastServiceProvider included with your Laravel application, you will see a call to the Broadcast::routes method. This method will register the /broadcasting/auth route to handle authorization requests
When defining broadcast routes, the currently authenticated user is passed automatically as a parameter. Since you're wanting to change the authentication mechanism ahead of the broadcast route definition so that the correct user is passed along, your broadcast client would pass along the token (however you like - HTTP header, URL parameter, POST data, etc.) and then you would customise the /broadcasting/auth route to parse that data and use it to authenticate the user. From there you would use the standard Broadcast::channel() definitions to ensure that the authenticated user is also an authorized user on that channel.
As per the documentation, Broadcast::routes() takes an optional array of $attributes for it to use the web middleware. From the source:
$attributes = $attributes ?: ['middleware' => ['web']];
So if you wish, you may change the middleware authentication to 'api' by passing it to the Broadcast::routes() call (or any other standard route parameters).
Since the route is already created for you, if you want to customise it at a lower level that route functions/middleware then you would need to change the auth() method on whichever BroadcastProvider you're using. More specifically, when the route is set up, it calls the authenticate() method on the BroadcastController, which in turn simply calls the auth() method on the Broadcast facade. The facade would then call whichever provider you're using - there are a few providers defined out of the box which should give you a good starting point if you want to write your own. That being said, as long as you don't have a problem with the provider itself, it's probably easier to just write your own middleware and pass that to Broadcast::routes() as an attribute if you need something particular with your authentication (if it differs from the api authentication middleware).
As an extra, since you've tagged this with socket.io, you may also want to read up on Laravel Echo. There's also a section on talking to socket.io specifically in the broadcasting introduction.
On top of Leith's answer, those scratching head and wondering why creating a custom middleware and using it in BroadcastServiceProvider.php would throw back an error.
public function boot()
{
Broadcast::routes(['middleware' => ['custom.middleware']]);
require base_path('routes/channels.php');
}
Inside Broadcaster.php there is a method called retrieveUser() which should return $request->user() as a result of successful authentication from within your custom middleware.
In my case, I was trying to pass access token to API's custom middleware; and once authenticating the user, I simply passed on my original request return $next($request)
For the above code to work, I had to fetch the user model by access token and then merge it to the original request like so
$request->merge(['user' => $user ]);
$request->setUserResolver(function () use ($user) {
return $user;
});
return $next($request);
So now retrieveUser() method from Broadcaster.php is able to retrieve the authenticated user with return $request->user() command, and it gets passed to Broadcast::channel method as a first parameter
Broadcast::channel('private-channel-name', function ($user) {
// if you get here, you've been authenticated (within a custom middleware)
return true;
});
Related
I am a bit confused, I have a web application having a login, Register, Logout. Some dashboard views etc(CRUD), I want to make an api for this application too.
Like an api which third party will use, Can update records, Can delete records etc.
Actually there should be some way which can be use by mobile app for CRUD.
I know we have that routes/api.php, But i am pretty confused that when to use it. Please explain the scenario, I am blank.
Update:
Scenario
Application having views, authentication system etc, How an android app will be able to perform CRUD operations on the same application ?
1.web routing uses session state, CSRF protection. does it mean api routing not using session state, CSRF protection?
All it possible but not required. You still can using sessions etc, but this is a REST principles violation.
2.laravel 5.3 uses seperate web and api routing, is there any advantages ?
It's just for your convenience. In Laravel 5.2 you need specify middleware for routes like ['web'] or ['api'] but it doesn't required anymore. In 5.3 routes stored in separated files and specify routes middleware not required.
If you are specifying routes in api.php, you will need to use the auth:api middleware. For example:
Route::group(['middleware' => ['auth:api']], function () {
Route::get('/test', function (Request $request) {
return response()->json(['name' => 'test']);
});
});
Notes about Token auth and Laravel 5.3:
If you've setup laravel's default auth system, you will also need to add a column for api_token to the user table. If you are using DB seeders, you might want to add something like:
$table->char('api_token', 60)->nullable();
to your users table seeder. Alternatively just add the column manually and fill that column with a random 60-char key.
When making the request, you can add the api_token as a URL/Querystring parameter like so:
domain.com/api/test?api_token=[your 60 char key].
You can also send the key as a header (if using Postman or similar), i.e: Header: Authorization, Value: Bearer [your 60 char key].
I order to get a useful error if the token is incorrect, also send the following header with all requests:
Header: Accept, Value: application/json. This allows the expectsJson() check in the unauthenticated() function inside App/Exceptions/Handler.php to work correctly.
I have a laravel project, which is about Students and their courses.
This project has CRUD functions, where the user(mainly the school's admin) can create users and assign their courses. I have successfully created this project.
But now how do I make this an API?. I need to share the students courses to another website, therefore I need to create an API right?
I have never created an API in laravel, when I checked the documentation of Laravel, it has this topic called API authentication using passport, is this talking about requiring users to login first before accessing the API
?.
Would be very helpful if someone can explain a little bit about creating an API and where to start from.
You are trying to give another website access to view the students courses. Is the data publicly available ? If yes, you don't need Authentication. But that means literally everybody with the API URL can access the data. Using Authentication on your API is entirely up to you.
Now, to create an API, it's very easy with Laravel. There are tons of free resources online like this one: https://tutorials.kode-blog.com/laravel-5-rest-api. Just google the topic for more.
Basically, the API works exactly like what you did in terms of controller and routing. In laravel 5.4, there is a separate route file for api located in routes/api.php. That's where you define your routes.
Route::get('courses', 'Api\CourseController#index');
This route will respond to https://www.website.com/api/courses and will link to the function index() located in app/Http/Controllers/Api/CourseController.php. You will have to create that controller yourself.
Looking at that route file, you can see something out of the box
Route::middleware('auth:api')->get('/user', function (Request $request) {
return $request->user();
});
It means the route /api/user will require authentication because of the auth:api middleware.
In your controllers so far, you return views. In api controller you will return response
public function index()
{
$courses = Course::all();
return response()->json($courses);
}
The application calling the website will receive a JSON object containing an array of courses. More about responses here: https://laravel.com/docs/5.4/responses
About api_token
The default API authentication offered by laravel works by checking api_token attribute from the users table. That api_token has to been present in the headers of the request sent to the api. Laravel will then use that api_token to identify the user making the request. In default application, to get a user, you do
$user = Auth::user();
Within your API controllers, you do
$user = Auth::guard('api')->user();
//Auth will look for api_token to get the user making the request
Firstly I'm a real beginner with Laravel so I will try to describe my problem as best as I can.
I am building a website using Laravel however the information on users will not be stored on my server but rather externally on another server.
The only way to access the user's data is through an external API; I am not allowed access to their database. The API request returns a token and I use this token to check with their server to see if the user is logged in.
My question is: how do I authenticate the user so that I can still use Laravel's out of the box guards.
It's really handy to use methods like Auth::check() to determine if the user is still logged in.
You'll either need to modify Laravel's default authentication middleware in app/Http/middleware/Authenticate.php or you'll need to create your own middleware class that runs the authentication that you need. Create a class in the app/Http/middleware folder and register that middleware. https://laravel.com/docs/master/middleware
in my user table I have a active_id column to track this user account is still active or not.
Is it possible when a logged in user visit our website, check if it's not a active user then logout the logged in user.
Or when the logged in inactive user perform a post request , log out the user immediately .
Is it possible ?
You can accomplish this very easily with Middleware
Do the console command php artisan make:middleware CheckUserIsActive
Go to the generated file at app/Http/Middlewares/CheckUserIsActive
In the handle method
public function handle($request, Closure $next)
{
if (! $user = auth()->user()->is_active) {
auth()->user()->logout();
}
return $next($request);
}
Edit the app/Http/Kernel.php file: find web key on middlewareGroups property and append your new middleware \App\Http\Middlewares\CheckUserIsActive::class,
With this approach the check will be fired off on each request on your app, assuming that in your routes.php file you have web group middleware applied to all your routes, which is the case if you installed laravel and didn't change it.
Take a closer look at middlewares in the Documentation
Also note, that this will work only when there is an authenticated user. If there isn't then auth()->user() will return null and you'll get and error like 'Trying to call method logout() on null'. To avoid this error you need to make sure that there is an authenticated user and only then check if he's active. To do so Laravel provides built in auth middleware. Just append it to the middlewaresGroups's web key before your own middleware.
But again this is to work if it's fit your projects needs. If you do require user to be authenticated to go to any of the pages of your app then do this approach. if not - you need to limit some requests to be filtered by middlewares. Again, you may find this on docs or ask here if needed, I'll provide samples
I was able to use Socialite in my project as per this tutorial : http://www.codeanchor.net/blog/complete-laravel-socialite-tutorial/
Everything worked fine but I have a Security concern in it.
if (!$request) {
return $this->getAuthorizationFirst($provider);
}
This checks for token. But is this a correct way to check for token?
When I look at the tutorial what I noticed is that the $this->getAuthorizationFirst($provider) calls the getAuthorizationFirst method declared inside the custom AuthenticateUser class.
The getAuthorizationFirst method simply forces the socialite driver to redirect the user to the provide (Facebook, Github, Twitter, etc.) in order to authenticate the user against the provider. The process then returns the authenticated user back to the calling application.
// Authenticate by connecting to the Provider API
private function getAuthorizationFirst($provider) {
return $this->socialite->driver($provider)->redirect();
}
// Provider returns authenticated user for post-authentication processing
private function getSocialUser($provider) {
return $this->socialite->driver($provider)->user();
}
This ties in with the processes exposed by Socialite which is to redirect and return to the specified url for post-authentication user processing.
From a security perspective this relies on the application calling the provider's API and getting the authenticated user back.
Socialite is simply exposing a redirect and return method for authentication. You will be unable to do a OAuth authenticate without actually redirecting to the provider API first.
This tutorial looks to be heavily based on the tutorial that was put together by Jeffrey Way over at Laracasts.com which can be found here:
https://laracasts.com/series/whats-new-in-laravel-5/episodes/9