F1.25 Laravel(Lumen) Security Step7: Configuration (Wpf, Xamarin, Angular SPA, Reactjs SPA) - chempkovsky/CS2WPF-and-CS2XAMARIN GitHub Wiki
1. Add Auth.php to the project
- for the lumen project
- copy vendor/laravel/lumen-framework/config/auth.php-file into the config-folder
2. Open generated "app/Providers/AspnetuserViewAuthServiceProvider.php"
- at the beginning of the file you will find what and how to configure
// ///////////////////////////////////////////////////////////////////////////////
// the generated code expects "config/database.php"-file to have the following config parameters
// ///////////////////////////////////////////////////////////////////////////////
// ...
// 'connections' => [
// ...
//
// 'aspnetforphpchckdbcontext' => [
// 'url' => 'mysql://root:[email protected]:3306/database_name_here?serverVersion=8.0&charset=utf8mb4',
// ],
// ...
// ///////////////////////////////////////////////////////////////////////////////
// the generated code expects "bootstrap/app.php"-file to have the following config parameters
// ///////////////////////////////////////////////////////////////////////////////
//
// ------------- ==1== uncomment the lines -------------
// $app->routeMiddleware([
// 'auth' => App\Http\Middleware\Authenticate::class,
// ]);
// ------------- ==1== end -------------
//
// ------------- ==2== uncomment the line -------------
// $app->register(App\Providers\AuthServiceProvider::class);
// ------------- ==2== End -------------
//
// ------------- ==3== add the line -------------
// $app->register(App\Providers\AspnetuserViewAuthServiceProvider::class);
// ------------- ==3== End -------------
//
// ------------- ==4== add the line -------------
// $app->configure('auth');
// ------------- ==4== End -------------
//
// ///////////////////////////////////////////////////////////////////////////////
// the generated code expects "config/auth.php"-file to have the following config parameters
// ///////////////////////////////////////////////////////////////////////////////
//
// return [
//
// 'defaults' => [
// 'guard' => env('AUTH_GUARD', 'aspnetsecurity'),
// ],
//
//
// 'guards' => [
// 'aspnetsecurity' => ['driver' => 'aspnetsecurityguard'],
// 'api' => ['driver' => 'api'],
// ],
//
// ];
// ///////////////////////////////////////////////////////////////////////////////
// the generated code expects ".env"-file to have the following config parameters
// ///////////////////////////////////////////////////////////////////////////////
// ...
//
// AUTH_GUARD=aspnetsecurity
// JWT_SECRET_KEY=SOME_SECRET_HERE
//
// ///////////////////////////////////////////////////////////////////////////////
// To protect controllers using roles modify Middleware/Authenticate.php like bellow
//
//
// public function handle($request, Closure $next, $guard = null)
// {
//
// if ($this->auth->guard($guard)->guest()) {
// return response('Unauthorized.', 401);
// }
// $roles=null;
// $user = $request->user();
// if ($user !== null)
// if($user instanceof GenericUser)
// $roles= $user->roles;
// switch( $request->path() ) {
// case 'litgenreviewwebapi/addone':
// if ($roles === null)
// return response('Unauthorized.', 401);
// // check if current user has a required role (or roles)
// break;
// case 'litgenreviewwebapi/updateone':
// if ($roles === null)
// return response('Unauthorized.', 401);
// // check if current user has a required role (or roles)
// break;
// case 'litgenreviewwebapi/deleteone':
// if ($roles === null)
// return response('Unauthorized.', 401);
// // check if current user has a required role (or roles)
// break;
// }
// return $next($request);
// }
//
// for the paths above routes/web.php must be defined as follows:
//
// // protected methods
// $router->group(['middleware' => 'auth:aspnetsecurity'], function () use ($router) {
// $router->post('/litgenreviewwebapi/addone', ['uses' => 'LitGenreViewController@addone']);
// $router->put('/litgenreviewwebapi/updateone', ['uses' => 'LitGenreViewController@updateone']);
// $router->delete('/litgenreviewwebapi/deleteone', ['uses' => 'LitGenreViewController@deleteone']);
// });
// // unprotected methods
// $router->group([], function () use ($router) {
// $router->get('/litgenreviewwebapi/getall', ['uses' => 'LitGenreViewController@getall']);
// $router->get('/litgenreviewwebapi/getwithfilter', ['uses' => 'LitGenreViewController@getwithfilter']);
// $router->get('/litgenreviewwebapi/getone', ['uses' => 'LitGenreViewController@getone']);
// });
//
// ///////////////////////////////////////////////////////////////////////////////
3.1. bootstrap/app.php-file settings
Here is a set of all changes:
$app->withFacades();
$app->configure('app');
$app->configure('database');
$app->configure('auth');
$app->routeMiddleware([
'auth' => App\Http\Middleware\Authenticate::class,
]);
$app->register(App\Providers\AuthServiceProvider::class);
$app->register(App\Providers\AspnetuserViewAuthServiceProvider::class);
Reminder: We do not to uncomment the line
// $app->withEloquent();
-So App\Providers\AuthServiceProvider-class will not work correctly
3.2. config/auth.php-file settings
Here is a set of all changes:
'defaults' => [
'guard' => env('AUTH_GUARD', 'aspnetsecurity'),
],
'guards' => [
'aspnetsecurity' => ['driver' => 'aspnetsecurityguard'],
'api' => ['driver' => 'api'],
],
4. config/database.php settings
Here is a set of all changes:
'connections' => [
...
'aspnetforphpchckdbcontext' => [
'url' => 'mysql://root:[email protected]:3306/database_name_here?serverVersion=8.0&charset=utf8mb4',
],
...
];
5. .env settings
Here is a set of all changes:
JWT_SECRET_KEY=SOME_SECRET_KEY_HERE
JWT_SECRET_KEY is required inside boot()-method of the app/Providers/AspnetuserViewAuthServiceProvider.php
$jwt = (array) JWT::decode(
$bearer,
env('JWT_SECRET_KEY'),
['HS256']
);
6. Open generated "app/Http/Controllers/AspnetuserViewAuthController.php"
- at the beginning of the file you will find what and how to configure
// ///////////////////////////////////////////////////////////////////////////////
// the generated code expects "config/database.php"-file to have the following config parameters
// ///////////////////////////////////////////////////////////////////////////////
// ...
// 'connections' => [
// ...
//
// 'aspnetforphpchckdbcontext' => [
// 'url' => 'mysql://root:[email protected]:3306/database_name_here?serverVersion=8.0&charset=utf8mb4',
// ],
// ...
// ///////////////////////////////////////////////////////////////////////////////
// Laravel: the generated code expects "routes/api.php"-file to have the following config parameters
// ///////////////////////////////////////////////////////////////////////////////
// ...
// use App\Http\Controllers\AspnetuserViewAuthController;
// ...
// Route::post('/api/Account/register', [AspnetuserViewAuthController::class,'register']);
// Route::post('/api/Account/changePassword', [AspnetuserViewAuthController::class,'changePassword']);
// Route::post('/api/Account/logout', [AspnetuserViewAuthController::class,'logout']);
// Route::post('/token', [AspnetuserViewAuthController::class,'token']);
// ...
// ///////////////////////////////////////////////////////////////////////////////
// Lumen: the generated code expects "routes/web.php"-file to have the following config parameters
// ///////////////////////////////////////////////////////////////////////////////
// ...
//
// //$router->group(['prefix' => 'api'], function () use ($router) {
// $router->group([], function () use ($router) {
// $router->post('/api/Account/register', ['uses' => 'AspnetuserViewAuthController@register']);
// $router->post('/token', ['uses' => 'AspnetuserViewAuthController@token']);
// });
//
// $router->group(['middleware' => 'auth:aspnetsecurity'], function () use ($router) {
// $router->post('/api/Account/changePassword', ['uses' => 'AspnetuserViewAuthController@changePassword']);
// $router->post('/api/Account/logout', ['uses' => 'AspnetuserViewAuthController@logout']);
// });
//
// ...
// ///////////////////////////////////////////////////////////////////////////////
// the generated code expects ".env"-file to have the following config parameters
// ///////////////////////////////////////////////////////////////////////////////
// ...
//
// JWT_SECRET_KEY=SOME_SECRET_HERE
// ///////////////////////////////////////////////////////////////////////////////
7. modify "routes/web.php" for Lumen-project
$router->group([], function () use ($router) {
$router->post('/api/Account/register', ['uses' => 'AspnetuserViewAuthController@register']);
$router->post('/token', ['uses' => 'AspnetuserViewAuthController@token']);
});
$router->group(['middleware' => 'auth:aspnetsecurity'], function () use ($router) {
$router->post('/api/Account/logout', ['uses' => 'AspnetuserViewAuthController@logout']);
$router->post('/api/Account/changePassword', ['uses' => 'AspnetuserViewAuthController@changePassword']);
});
This means that only authorized users can log out and change the password.
8. Role-bases protection: modify "handle()"-method of "app/Http/Middleware/Authenticate.php"
- Open generated "app/Providers/AspnetuserViewAuthServiceProvider.php"-file again
- at the beginning of the file you will find how to modify handle()-method of "app/Http/Middleware/Authenticate.php"-file to turn on Role-based protection:
public function handle($request, Closure $next, $guard = null)
{
if ($this->auth->guard($guard)->guest()) {
return response('Unauthorized.', 401);
}
$roles=null;
$user = $request->user();
if ($user !== null)
if($user instanceof GenericUser)
$roles= $user->roles;
switch( $request->path() ) {
case 'litgenreviewwebapi/addone':
if ($roles === null)
return response('Unauthorized.', 401);
// check if current user has a required role (or roles)
break;
case 'litgenreviewwebapi/updateone':
if ($roles === null)
return response('Unauthorized.', 401);
// check if current user has a required role (or roles)
break;
case 'litgenreviewwebapi/deleteone':
if ($roles === null)
return response('Unauthorized.', 401);
// check if current user has a required role (or roles)
break;
}
return $next($request);
}
- In the sample code above we have three paths:
- litgenreviewwebapi/addone
- litgenreviewwebapi/updateone
- litgenreviewwebapi/deleteone
- To get ready role-protection for these paths we should define rotes in the "routes/web.php":
$router->group(['middleware' => 'auth:aspnetsecurity'], function () use ($router) {
$router->post('/litgenreviewwebapi/addone', ['uses' => 'LitGenreViewController@addone']);
$router->put('/litgenreviewwebapi/updateone', ['uses' => 'LitGenreViewController@updateone']);
$router->delete('/litgenreviewwebapi/deleteone', ['uses' => 'LitGenreViewController@deleteone']);
});
if we want some routes to be available for unauthorized users we do not attach 'auth:aspnetsecurity'-middleware
$router->group([], function () use ($router) {
$router->get('/litgenreviewwebapi/getall', ['uses' => 'LitGenreViewController@getall']);
$router->get('/litgenreviewwebapi/getwithfilter', ['uses' => 'LitGenreViewController@getwithfilter']);
$router->get('/litgenreviewwebapi/getone', ['uses' => 'LitGenreViewController@getone']);
});
if we want some routes to be available for authorized users without any roles we attach 'auth:aspnetsecurity'-middleware but we do not check them in handle()-method of "app/Http/Middleware/Authenticate.php"-file
$router->group(['middleware' => 'auth:aspnetsecurity'], function () use ($router) {
$router->get('/litgenreviewwebapi/getall', ['uses' => 'LitGenreViewController@getall']);
$router->get('/litgenreviewwebapi/getwithfilter', ['uses' => 'LitGenreViewController@getwithfilter']);
$router->get('/litgenreviewwebapi/getone', ['uses' => 'LitGenreViewController@getone']);
});
9. App User with "roles"-field
- For the Lumen project we use "GenericUser"-class for App User
- Open generated "app/Providers/AspnetuserViewAuthServiceProvider.php"-file
- find the code:
- Open generated "app/Providers/AspnetuserViewAuthServiceProvider.php"-file
return new GenericUser(
[
'Id' => $user->Id,
'Email'=> $user->Email,
'EmailConfirmed'=> $user->EmailConfirmed,
'PasswordHash'=> $user->PasswordHash,
'SecurityStamp'=> $user->SecurityStamp,
'PhoneNumber'=> $user->PhoneNumber,
'PhoneNumberConfirmed'=> $user->PhoneNumberConfirmed,
'TwoFactorEnabled'=> $user->TwoFactorEnabled,
'LockoutEndDateUtc'=> $user->LockoutEndDateUtc,
'LockoutEnabled'=> $user->LockoutEnabled,
'AccessFailedCount'=> $user->AccessFailedCount,
'UserName' => $user->UserName,
'roles' => $roles
]
);
10. Note:
- The current implementation of role-based security is optimal
- role check is done before starting any controller
- bit-mask only sent once to client app right after login
11. modify routes/web.php for the generated controllers:
- open each generated controller:
- AspnetdashboardViewController.php
- AspnetmodelViewController.php
- AspnetrolemaskViewController.php
- AspnetroleViewController.php
- AspnetuserclaimViewController.php
- AspnetuserloginViewController.php
- AspnetusermaskViewController.php
- AspnetuserrolesViewController.php
- AspnetuserViewController.php
- at the gebining of each file you will find "How to configure routes/web.php and config/database.php".
- For instance, open the file AspnetdashboardViewController.php:
// ///////////////////////////////////////////////////////////////////////////////
// the generated code expects "config/database.php"-file to have the following config parameters
// ///////////////////////////////////////////////////////////////////////////////
// ...
// 'connections' => [
// ...
//
// 'aspnetforphpchckdbcontext' => [
// 'url' => 'mysql://root:[email protected]:3306/database_name_here?serverVersion=8.0&charset=utf8mb4',
// ],
// ...
// ///////////////////////////////////////////////////////////////////////////////
// Laravel: the generated code expects "routes/api.php"-file to have the following config parameters
// ///////////////////////////////////////////////////////////////////////////////
// ...
// use App\Http\Controllers\AspnetdashboardViewController;
// ...
// Route::get('/aspnetdashboardviewwebapi/getall', [AspnetdashboardViewController::class,'getall']);
// Route::get('/aspnetdashboardviewwebapi/getwithfilter', [AspnetdashboardViewController::class,'getwithfilter']);
// Route::get('/aspnetdashboardviewwebapi/getone', [AspnetdashboardViewController::class,'getone']);
// Route::post('/aspnetdashboardviewwebapi/addone', [AspnetdashboardViewController::class,'addone']);
// Route::put('/aspnetdashboardviewwebapi/updateone', [AspnetdashboardViewController::class,'updateone']);
// Route::delete('/aspnetdashboardviewwebapi/deleteone', [AspnetdashboardViewController::class,'deleteone']);
// ...
// ///////////////////////////////////////////////////////////////////////////////
// Lumen: the generated code expects "routes/web.php"-file to have the following config parameters
// ///////////////////////////////////////////////////////////////////////////////
// ...
//
// //$router->group(['prefix' => 'api'], function () use ($router) {
// $router->group([], function () use ($router) {
// $router->get('/aspnetdashboardviewwebapi/getall', ['uses' => 'AspnetdashboardViewController@getall']);
// $router->get('/aspnetdashboardviewwebapi/getwithfilter', ['uses' => 'AspnetdashboardViewController@getwithfilter']);
// $router->get('/aspnetdashboardviewwebapi/getone', ['uses' => 'AspnetdashboardViewController@getone']);
// $router->post('/aspnetdashboardviewwebapi/addone', ['uses' => 'AspnetdashboardViewController@addone']);
// $router->put('/aspnetdashboardviewwebapi/updateone', ['uses' => 'AspnetdashboardViewController@updateone']);
// $router->delete('/aspnetdashboardviewwebapi/deleteone', ['uses' => 'AspnetdashboardViewController@deleteone']);
// });
// ...
// ///////////////////////////////////////////////////////////////////////////////
Note:
- do not forget about middleware and handle()-method of "app/Http/Middleware/Authenticate.php"-file
Example for aspnetdashboardviewwebapi:
- all nine Aspnet...ViewController.php controllers are for Admin user (user with admin role)
- suppose the name of such a role will be ROLE_ADMIN
- in the routes/web.php we add
$router->group(['middleware' => 'auth:aspnetsecurity'], function () use ($router) {
$router->get('/aspnetdashboardviewwebapi/getall', ['uses' => 'AspnetdashboardViewController@getall']);
$router->get('/aspnetdashboardviewwebapi/getwithfilter', ['uses' => 'AspnetdashboardViewController@getwithfilter']);
$router->get('/aspnetdashboardviewwebapi/getone', ['uses' => 'AspnetdashboardViewController@getone']);
$router->post('/aspnetdashboardviewwebapi/addone', ['uses' => 'AspnetdashboardViewController@addone']);
$router->put('/aspnetdashboardviewwebapi/updateone', ['uses' => 'AspnetdashboardViewController@updateone']);
$router->delete('/aspnetdashboardviewwebapi/deleteone', ['uses' => 'AspnetdashboardViewController@deleteone']);
});
- we modify handle()-method of "app/Http/Middleware/Authenticate.php"-file as follows
public function handle($request, Closure $next, $guard = null)
{
if ($this->auth->guard($guard)->guest()) {
return response('Unauthorized.', 401);
}
$roles=null;
$user = $request->user();
if ($user !== null)
if($user instanceof GenericUser)
$roles= $user->roles;
switch( $request->path() ) {
case 'litgenreviewwebapi/addone':
if ($roles === null)
return response('Unauthorized.', 401);
// check if current user has a required role (or roles)
break;
case 'litgenreviewwebapi/updateone':
if ($roles === null)
return response('Unauthorized.', 401);
// check if current user has a required role (or roles)
break;
case 'litgenreviewwebapi/deleteone':
if ($roles === null)
return response('Unauthorized.', 401);
// check if current user has a required role (or roles)
break;
case 'aspnetdashboardviewwebapi/getall':
case 'aspnetdashboardviewwebapi/getwithfilter':
case 'aspnetdashboardviewwebapi/getone':
case 'aspnetdashboardviewwebapi/addone':
case 'aspnetdashboardviewwebapi/updateone':
case 'aspnetdashboardviewwebapi/deleteone':
if ($roles === null)
return response('Unauthorized.', 401);
if (!in_array("ROLE_ADMIN", $roles)
return response('Unauthorized.', 401);
break;
}
return $next($request);
}