How to make custom authentication on Laravel? - laravel

I have a Laravel application, with a login form. I am using subdomains, for example: {{business_subdomain}}.laravel.test. The purpose of my application is create companies in my database, and make registered users login on their attached company and specific domain.
So in my web.php I declared the following:
Route::domain('{business_subdomain}.' . env('APP_URL'))->middleware(['business.subdomain'])->group(function ()
My database contains the following tables:
* Standard tables like users
* Businesses which contains id, name and subdomain
* Business_user which contains business_id and user_id
The business_id (business_user table) has a relationship with id (business table)
The user_id (business_user table) has a relationship with id (users table)
I created a middleware that checks the following:
$business = $request->user()->businesses->where('subdomain', $request->route('business_subdomain'))->first();
So the subdomain value (business table) must be the equal to the URL/route subdomain value, otherwise when you login, you will get an 403 page.
So what I want to achieve: When a user has an account, but is not attached to the right company/business, I want to display the standard authentication error: These credentials do not match our records.
I've tried to use the code in my middleware on the LoginController and override almost every function separately, but it didn't work.
Do I need to override Laravel authentication functions, do I need to create another middleware or use Guards?
The laravel authentication login order (the order the application uses for logging in a user) seems very confusing for me.
Maybe someone can provide me with more information or help me out!
I tried to use the code in my middleware on the LoginController and override almost every function separately, but it didn't work.

Sounds like you might be looking for multi-tenancy. There are a couple different packages available, both for single and multi-database setups.
Here are some very informative slides on the topic, specific to Laravel. And an excellent article for more detail.
We have a similar application with subdomain routing using torzer/awesome-landlord. The tenant package ensures that login will only search for users of the correct tenant.
To set the correct tenant we added a middleware (App is the tenant, in your case it would be Business):
public function handle($request, Closure $next)
{
$subdomain = $this->getSubdomain($request->server('HTTP_HOST'));
if ($subdomain) {
$app = App::getBySlug($subdomain);
abort_if(!$app, 404);
$this->tenantManager->addTenant($app);
} else {
// empty or generic subdomain
// this is OK as long as there is no user
if ($user = $request->user()) {
return $this->redirectToUserAppSubdomain($request, $user);
}
}
return $next($request);
}

Related

Retain session when login in with Laravel Socialite in google or reset id with previous one

I currently have a Laravel app that signs in using Socialite.
public function redirectToGoogle(Request $request)
{
return Socialite::driver('google')->stateless()->redirect();
}
When it loads and redirects I lose the previous session id.
I need that session id because it's loaded in my db to track some cart info.
Is there away I can retain the previous session id when Socialite loads the google driver?
I can also reset the session id since I have it in my db. However I don't know how to regenerate it with a custom string.
$request->session()->regenerate();
But passing it a custom string?
$request->session()->regenerate('some value');
session()->getId(); //does not regenerate 'some value'
I would setId like so:
session()->setId($myCustomId);
From here

Laravel middleware is "bypassed" when i submit the invalid token, but when it is a valid token, the middleware is executed

all my fellow friends, i have a question.
Route::group([
'middleware' => ['ensure.role:store', 'auth:api']
]
For simplification,
i have two roles : ADMIN and STORE
I have created a middleware that will validate user role, and if the user role is correct, then will allow the user to access the route.
It works fine.
I tried using ADMIN Jwt Token to access STORE routes, and rightfully i am kicked out, and vice versa.
But now, if i modify the token, lets say i add a string to any part of the token, and try to access any route, actually i am allowed to.
I tried var_dump and print something on the related middleware, and here are my observation.
1. If the token is VALID as one of the user role, then
the var_dump is executed, (means the middleware is executed)
2. if the token is INVALID as in i add / modify the original
token, then the var_dump is not executed, and so are the
others route middleware.
I am wondering what causes this behavior, and what could be the fix for this issue, as i need to throw 401 unauthenticated in any token invalid case.
Thank you
I figured it out.
After series of testing and reading, i found out that after laravel 5.3 and above, constructor is called before middleware. And because in my constructor i am using an user object before i am authenticated by the middleware, i encountered constructor error, because user is null.
Of course it is a bad practice to use user object in the construct, however due to the convenience of usage, i still decided to use it.
It sounds complex to use closure based middleware as alternative solution
So i use a workaround to do it.
I create a helper function that return me true if there is an user object or return abort(401); if there is no user object, then add this one line to all the constructors.
$this->checkAccess = EnsureRoleUtil::check('admin');
After that, i just do my next constructor as normally
public function __construct() {
$this->checkAccess = EnsureRoleUtil::check('admin');
$this->user = Auth::user();
$this->categoryM = new CategoryManager($this->user);
}
However, to be noted, it is not a good practice, it is just a hack / workaround.

Laravel advanced middlewares

I'm using this line in some controller's __construct
$this->middleware('auth');
This results, that every not logged user will be redirected to login page. It's of course, ok, but there is a problem.
I have two groups of users. In database I have a column called "role", which is boolean. 0 means basic users and 1 means admins. How can I treat, that entrance to some of controllers will be allowed only for admins? I really don't know how to do that in pretty way.
You can pass things to the middleware, like
$this->middleware('auth:1');
Now in the middleware you can check to see if the authenticated user has a role that you passed (in the example, 1). If they don't have the role that you require, then you can redirect them to the login screen or however you want to handle it.
you can use the following code to get the authenticated user and then write custom logic.
if(Auth::user()->role==0)
{
//you are basic user
}
esle if(Auth->user()->role==1)
{
//you are admin
}
you can also Gates and Policies for this type of work.

Laravel Policy for a large array of permissions

I am looking to use Laravel's policy to authorize a large array of user permissions with Blade.
My authorizations are under a ActionPermission model holding all app permissions.
User has his permission names accessed via a pivot
using:
public function permissions(){
return $this->belongsToMany('App\ActionPermission', 'users__action_permission');
}
So essentially I am receiving a collection of permissions for user and calling $user->permissions()->pluck('module_name')->toArray(); I will get the names of permissions as array.
Is there a way to bring an array of policies via blade in one line? So in my layout I will insert a one line blade directive?
In Laravel's docs, they use the name of the function as the blade directive, like this in the policy:
public function update(User $user, Post $post)
{
return $user->id === $post->user_id;
}
And in the blade:
#can('update', $post)
<!-- The Current User Can Update The Post -->
#elsecan('create', App\Post::class)
<!-- The Current User Can Create New Post -->
#endcan
The above isn't relevant when you have a large number of permissions.
What would be the best practice to execute a large array of permissions?
BTW any method (not only provider bootstrap) would be welcome.
Accessing the model to create from the blade can be dangerous, as you're skipping some validation and requiring Eloquent to handle everything well (it won't).
Aside from that, I hope this can explain what you seek:
$user = User::find($idOfUser) // retrieves an active record of User
$user->permissions // gets the record of permissions that is related to the user, believing the relationship is estabished
User::find($idOfUser)->permissions->update($data) // updates the record in permissions that is related to that user

Laravel "conditional routing"?

I have an interesting problem I need to solve and looking for best way to approach it.
Have a laravel 5.4 app that is fronted by a few public static pages. App is a golf performance tracking app. User enters data and the app does some analysis and shows graphs etc.
Problem is that if a new user signs up they have no data and they need to enter a score for the app to "work"
User signs up, verifies email, etc. so they now have authorized access to the app.
What I want to do is check if the user has any scores in the db (fine I can do that easily) then if they have at least one score entered send them to the app. Fine.
If they have no scores I want to send them to a view that explains that they need to have to enter a score and present the form to do so.
Question is:
where is the best place to check if the user has no scores and then send them to the view to enter the score and not allow them to access any of the app views.
Looking for the best way to do this? Is it create the condition and check the db in the routes/web.php? Is there a better way to accomplish it? and if so what do I do with the routes that sit in:
Route::group(['middleware' => ['web','auth']], function () {
Create a custom middleware and check if the user has score and redirect to any route or display any view you want. Something like this.
public function handle($request, Closure $next)
{
$user = $request->user();
if ($user && ($user->score == 0)) {
return redirect()->route('instruction');
}
return $next($request);
}
Apply this new middleware to all the routes which shouldn't be accessed by users with no scores. Also make sure you use this middleware after auth and not before.

Resources