Laravel API token authentication vs web Route::prefix('api') - php

I'm wondering what is a better practice using the API with a token or the web router with a prefix:
Have a token that changes every time a user logs in. API:
Route::get('userdata/{key}', 'userController#show');
vs
Route::prefix('api')->group(function () {
Route::get('userdata', 'userController#show');
});
In the web router I can use cookies to verify an user.
Which of these is better?

You don`t need to use any 'key' or something fields in route/body to get data for logged / session user. You can create static route for get 'self-data' and fetch current user data according header (bearer or something other auth) or cookies.
Route::get('userdata', 'userController#show');
with cookies/header will be ok. Also don`t forget to use auth middleware, where you should validate cookies/header.

Group all the api routes and apply your custom middleware that will check for the bearer token in request headers. Here is how to do it:
Create a middleware:
class AuthenticateWithToken
{
public function handle($request, Closure $next)
{
$token = $request->bearerToken();
if ($token==='valid') {
return $next($request);
}
throw new Exception();
}
}
Register your middleware in App\Http\Kernel.php:
class Kernel extends HttpKernel
{
protected $routeMiddleware = [
'authenticateWithToken' => AuthenticateWithToken::class
];
}
Apply middleware on routes:
Route::middleware('authenticateWithToken')
->prefix('api')
->group(function () {
Route::get('/route1', 'YourController#action1');
Route::get('/route2', 'YourController#action2');
});

Related

Using multiple authentication middlewares for one route

I have a requirement where there is an API method (guarded by the well-known package tymon/jwt-auth) and I need to also be able to access it using the basic session based web middleware.
I don't want to repeat the route in both api.php and web.php even though that would totally work.
I tried adding both to the route but they simply don't work, like: ['auth:api', 'web']
I also tried creating a new middleware with the intention of checking both api and web like so:
class CombinedAuth
{
public function handle($request, Closure $next)
{
$web = Auth::guard('web')->user();
if ($web) {
return $next($request);
}
$api = Auth::guard('api')->user();
if ($api) {
return $next($request);
}
if(!$web && !$api) {
throw new AuthorizationException;
}
}
}
and that also doesn't work. The api middleware works fine but the web middleware doesn't and it always signs me out and redirects to the login page.
So Is there a neat way of protecting a route with api and web middlewares at the same time in Laravel 5.8?
You can use 'auth:api,web' to check for multiple guards.
Using multiple can middleware calls in Laravel 9 route;
<?php
Route::get('/', function () {
return view('money');
})
->middleware([
'can:manage,App\Models\Payment',
'can:manage,App\Models\Withdraw',
]);
?>

Problem in my test function trying to access group of routes

What I'm trying to test is to access some routes but these routes are in laratrust role middleware this role is the auth user must be super admin to go in this routes my problem is I don't know how to write this function.
I tried to make the user super admin in the test function like this
public function Test()
{
$user = factory(User::class)->create();
$user->attachRole('superadministrator');
$this->actingAs($user, 'api');
$response = $this->json('GET', 'api/users');
$response->assertStatus(200);
}
but it didn't work even I checked the data base this user is superadministrator and the test give like I'm not super admin
This is my api routes:
Route::group(['middleware' => ['auth:api', 'role:superadministrator']],
function()
{
Route::apiResource('users', 'UserController');
}
This is my index function in UserController:
public function index()
{
return Response()->json(User::all, 200);
}
What I'm expect is a function can access this route because there is more routes in this group and the rest of the tests depends on this function
I've never used Laratrust, but after a quick look at its source code, it looks like the issue is that you need to specify the api guard on your role middleware check. Add ,guard:api to your role middleware:
Route::group(['middleware' => ['auth:api', 'role:superadministrator,guard:api']], function() {
Route::apiResource('users', 'UserController');
}
In the role middleware check, if you don't specify the guard, it will use the default guard defined in your auth config (which is web if you haven't changed it). So, the middleware will be looking for the user from the web auth guard, which doesn't exist.
The good news is, your test worked! It found a bug in your route definition.

How to change Laravel's default broadcast auth middleware

So, as my title says, I want to change Laravel's default Broadcast auth middleware to a custom auth middleware that I made which uses token-based authentication. I made this because my app is an API-based app, and, when a user authenticates, I create a session token and send it to him and also store it inside the DB with an expires_at column.
I am using Pusher.
I have the following middleware:
class AuthCustom
{
public function handle($request, Closure $next)
{
// if we have the session token stored in header
if ($request->header('x-session')) {
$session = Session::where('id', $request->header('x-session'))->where('expires_on', '>=', date('Y-m-d G:i:s'))->with('user')->first();
if ($session !== null) {
$user = (new User())->where('id', $session->user_id)->first();
if ($user !== null) {
$request->merge(['user' => $user]);
return $next($request);
}
}
}
}
My BroadcastServiceProvider code is as follows:
class BroadcastServiceProvider extends ServiceProvider
{
public function boot()
{
Broadcast::routes();
require base_path('routes/channels.php');
}
}
If I put Broadcast::routes(['middleware' => 'authcustom']); in BroadcastServiceProvider, the boradcasting/auth gives a 403 status code because $request->user() is null, which then results in an Access forbidden.
I have tried searching the whole damn web, and I found nothing about changing the default auth middleware for broadcasting.
I even tried removing Broadcast::routes() and customizing a new route /broadcast which returned a Pusher socket_auth object and everytime I got a 419 Unkown status code.
Any ideas or maybe you can point me in the direction where I could manage this?
Thank you!
Later edit:
My JS Echo connection looks like this:
Vue.use(VueEcho, {
broadcaster: 'pusher',
key: 'xxxxxxxxxxxxxx',
cluster: 'eu',
authEndpoint: 'http://localhost/api.easycargo.ro/public/broadcasting/auth',
auth: {
headers: {
'x-session': this.auth.token
}
}
});
I'm glad you got something working. For later readers, here's a more Laravel-esque way to solve the problem in the question: create a custom auth guard used to authenticate requests for the special routes.
Laravel's AuthManager includes a helper method—viaRequest()—that simplifies the creation of a Guard that authenticates a user with data from the request context without the need to fully-implement Illuminate\Contracts\Auth\Guard. We can bind our custom guard in the boot() method in AuthServiceProvider.php:
public function boot()
{
Auth::viaRequest('custom-auth', function ($request) {
// Any custom user-lookup logic here. For example:
if ($request->header('x-session')) {
$user = // ...try to fetch a user...
return $user;
}
});
}
As we can see, we just pass a closure to the viaRequest() method that returns a User object when authentication succeeds, or null when authentication fails.
Next, we'll tell Laravel about our new auth guard by adding an entry to the 'guards' array in config/auth.php:
'guards' => [
...
'broadcasting' => [
'driver' => 'custom-auth',
],
],
Finally, we need to update the middleware for any routes that should authenticate a user with our custom Guard. We can use Laravel's built-in auth middleware and specify which guard to apply as a middleware parameter. For example, we'll initialize the broadcasting routes in the question's BroadcastServiceProvider.php:
Broadcast::routes([ 'middleware' => [ 'auth:broadcasting', ... ] ]);
...where broadcasting matches the name we assigned to our custom Guard in config/auth.php.
This approach enables us to use all of Laravel's Auth services, provides a more central place to define our authentication logic, and simplifes automated testing because we can more easily mock up authentication as needed.
I actually managed to find a solution, so all I needed to do was bind the $user that I got in my custom auth middleware to the request by doing the following thing:
$request->merge(['user' => $user]);
//add this
$request->setUserResolver(function () use ($user) {
return $user;
});
and now $request->user() which laravel checks returns the user object and passes the validation.

laravel modify headers before authenticating (token based / api)

I have an api that used Dingo API. The current users therefore use a
X-Api-Key header for authentication. I now want to switch to laravels implemented API system which requires an Authorization header.
Is it possible to tell laravel which header to use.
Or preferrably:
Is there a hook that I can use to modify the headers (e.g. copy the X-Api-Key header value into the Authorization header), before authentication takes place?
You can create middleware like this:
<?php
namespace App\Http\Middleware;
use Closure;
class ModifyHeader extends BaseAuthorize
{
public function handle($request, Closure $next)
{
if ($authorization = $request->header('X-Api-Key')) {
$request->headers->set('Authorization', $authorization);
}
return $next($request);
}
}
Then you need to add this middleware to $middlewareGroups or $routeMiddleware for example like this:
protected $middlewareGroups = [
'api' => [
// ...
\App\Http\Middleware\ModifyHeader::class,
],
// ...
];
and then you should make sure that routes you want to make the change are in api middleware. Of course you can also create custom middleware group for this or apply this for selected routes.
Then for example if you add such route:
Route::group(['middleware' => 'api'], function () {
Route::get('/test', function () {
dd(request()->header('Authorization'));
});
});
You should get the same value that is passed in X-Api-Key header.

Attach middleware to all method

I have this in Laravel 4.2 Route::when('*', 'csrf', ['post']); that insert csrf verification to all post, how can I port to Larevel 5.2 ?
This is my own csrf, without using default provide by Laravel:
<?php
namespace App\Http\Middleware;
use Closure;
use Input;
class VerifyCsrfToken1
{
public function handle($request, Closure $next)
{
$token = $request->ajax() ? $request->header('X-CSRF-Token') : $request->input('_token');
if ($request->session()->token() === $token) {
return $next($request);
}
throw new TokenMismatchException;
}
}
I created my personal csrf middleware, but I don't know how to attach them on ALL post request
I want to attach it to all post via Route's facade. (file routes.php)
Thanks :)
Laravel 5 wires up middleware a bit differently, you won't be doing this through the Request facade.
You want to first register your Middleware as global. Open up app/Http/Kernel.php and add it to the global $middleware array.
protected $middleware = [
VerifyCsrfToken1::class
...
Then in your middleware class, check to see if it is handling a POST request. If not, have it just pass the request along without doing anything.
if($request->method() != "POST") {
// Move right along
return $next($request);
}
Side note: As you noted Laravel has a VerifyCsrfToken middleware baked in already. I'd advise trying to adapt this if possible.

Categories