The Idea (pasted from Slack)
I'm using the the two factor auth via app, everything is working fine in the frontend.
Now I had the idea to secure the backend/admin with the following:
- Check that the user has the permission to see the dashboard / access to the backend
- Check that the user has two factor enabled
- User has to type a generated code from the app again
In confluence/jira and/or github there is something simular, but they require (only) the user password again.
Current Implementation
User Access a resource
--> Middleware checks that the user is logged in
--> If defined, check also defined permissions (example via routes: `->middleware(['web', 'can:access-dashboard'])`)
--> All checks passed?
=> Yes: show requested resource
=> No: Show error message and redirect back
Future Implementation
User Access a resource
--> Middleware checks that the user is logged in
--> If defined, check also defined permissions (example via routes: `->middleware(['web', 'can:access-dashboard'])`)
--> All checks passed?
--> security check defined?
---> Check which kind of security check is required
----> Password: Show additional form, where the user has to type the password again
----> 2FA: Show additional form, where the user has to type the 2FA token from the app/sms
=> Yes: show requested resource
=> No: Show error message and redirect back
Both described implementations are simplified
How can we implement it
Via Guard / Middleware
I had the idea to create my own Guard (for example: admin guard).
Each route which should handled via the admin guard, will have a custom middleware.
Example:
`->middleware(['web', 'admin', 'can:access-dashboard'])`)
The idea was to use the existing functionality to keep the code maintainable (don't repeat yourself ;) )
This means, we can define it in the routes for each resource or via the controller "globally" for all resource actions.
Via Policy
While writing the Via Guard
section, i have checked the policy documentation.
The idea here is to define, what we need in the policies.
Why? In the current implementation we have already the definitions for the resources and actions.
Example:
| | GET|HEAD | backend | rinvex.fort.backend.dashboard.home | Rinvex\Fort\Http\Controllers\Backend\DashboardController@home | web,can:access-dashboard,auth,can:access-dashboard,dashboard |
| | GET|HEAD | backend/abilities | rinvex.fort.backend.abilities.index | Rinvex\Fort\Http\Controllers\Backend\AbilitiesController@index | web,can:access-dashboard,auth,can:list-abilities,abilities |
| | GET|HEAD | backend/abilities/create | rinvex.fort.backend.abilities.create | Rinvex\Fort\Http\Controllers\Backend\AbilitiesController@create | web,can:access-dashboard,auth,can:create-abilities,abilities |
| | POST | backend/abilities/create | rinvex.fort.backend.abilities.store | Rinvex\Fort\Http\Controllers\Backend\AbilitiesController@store | web,can:access-dashboard,auth,can:create-abilities,abilities |
| | GET|HEAD | backend/abilities/{ability} | rinvex.fort.backend.abilities.edit | Rinvex\Fort\Http\Controllers\Backend\AbilitiesController@edit | web,can:access-dashboard,auth,can:update-abilities,abilities |
| | PUT | backend/abilities/{ability} | rinvex.fort.backend.abilities.update | Rinvex\Fort\Http\Controllers\Backend\AbilitiesController@update | web,can:access-dashboard,auth,can:update-abilities,abilities |
| | DELETE | backend/abilities/{ability} | rinvex.fort.backend.abilities.delete | Rinvex\Fort\Http\Controllers\Backend\AbilitiesController@delete | web,can:access-dashboard,auth,can:delete-abilities,abilities |
| | GET|HEAD | backend/roles | rinvex.fort.backend.roles.index | Rinvex\Fort\Http\Controllers\Backend\RolesController@index | web,can:access-dashboard,auth,can:list-roles,roles |
| | POST | backend/roles/create | rinvex.fort.backend.roles.store | Rinvex\Fort\Http\Controllers\Backend\RolesController@store | web,can:access-dashboard,auth,can:create-roles,roles |
| | GET|HEAD | backend/roles/create | rinvex.fort.backend.roles.create | Rinvex\Fort\Http\Controllers\Backend\RolesController@create | web,can:access-dashboard,auth,can:create-roles,roles |
| | DELETE | backend/roles/{role} | rinvex.fort.backend.roles.delete | Rinvex\Fort\Http\Controllers\Backend\RolesController@delete | web,can:access-dashboard,auth,can:delete-roles,roles |
| | PUT | backend/roles/{role} | rinvex.fort.backend.roles.update | Rinvex\Fort\Http\Controllers\Backend\RolesController@update | web,can:access-dashboard,auth,can:update-roles,roles |
| | GET|HEAD | backend/roles/{role} | rinvex.fort.backend.roles.edit | Rinvex\Fort\Http\Controllers\Backend\RolesController@edit | web,can:access-dashboard,auth,can:update-roles,roles |
| | GET|HEAD | backend/users | rinvex.fort.backend.users.index | Rinvex\Fort\Http\Controllers\Backend\UsersController@index | web,can:access-dashboard,auth,can:list-users,users |
| | POST | backend/users/create | rinvex.fort.backend.users.store | Rinvex\Fort\Http\Controllers\Backend\UsersController@store | web,can:access-dashboard,auth,can:create-users,users |
| | GET|HEAD | backend/users/create | rinvex.fort.backend.users.create | Rinvex\Fort\Http\Controllers\Backend\UsersController@create | web,can:access-dashboard,auth,can:create-users,users |
| | DELETE | backend/users/{user} | rinvex.fort.backend.users.delete | Rinvex\Fort\Http\Controllers\Backend\UsersController@delete | web,can:access-dashboard,auth,can:delete-users,users |
| | PUT | backend/users/{user} | rinvex.fort.backend.users.update | Rinvex\Fort\Http\Controllers\Backend\UsersController@update | web,can:access-dashboard,auth,can:update-users,users |
| | GET|HEAD | backend/users/{user} | rinvex.fort.backend.users.edit | Rinvex\Fort\Http\Controllers\Backend\UsersController@edit | web,can:access-dashboard,auth,can:update-users,users
We can use this as start point and extend the existing policy classes with our new functionality to show our forms
Alternative solution (first prototype?)
Based on "Make it work then make it better", we should create a prototype.
With this, i'm very sure, we find another way how we can implement this feature... anyway...
We extend our config file, where we can define which route needs some additional security check.
Example:
'security' => [
'rinvex.fort.frontend.user.sessions' => 'password',
'rinvex.fort.backend.*' => 'twofactor'
],
Then we're creating a new middleware (e.g. security) which will be used in the same way as the web
middleware.
This middleware checks at first, if the current route is defined in the config.
If so, then we check a session variable (each route has it's own session).
If the session is valid, show the requested page.
If not, we have to redirect to our "enter password" / "enter twofactor" page.
If the confirmation was successful, we will call the original requested page again.
If you have another idea - feel free to post it :)
I hope my ideas are understandable and you can follow my thoughts.
Thanks a lot :)