Share Laravel Session on Subdomains with different guards - php

I'm currently using spatie/permissions and a Subclass of User with constraints to permissions to Login to subdomains in my application.
I now want to be able to share the session between my main domain example.com and the domains some.example.com where some is dynamically loaded from database.
When my logged-in User in example.com accesses the abc.example.com domain and is able to log in there I want to use the current session.
I use different guards on subdomain and domain with the SubUser and User classes as providers.
I already use the database session driver and can see in the logs that the same session id is loaded from database.
As the application is loading the same session from database I'm wondering why my user is not already logged in.
Anyone ever tried this and got a solution for this?

So I managed to resolve this issue.
My setup is all subdomains got the user guard and the main domain has the admin guard.
I realised that the Auth::getName() included the guard name and as I logged in using different guards I ended up having two active logins in one session. But these logins had different names and where only valid with the right guard. This guard being different in main domain and subdomains resulted in not really sharing login-state over domain and subdomains.
I managed to resolve this by overriding the default laravel SessionGuard and adding my own driver like so:
In config/auth.php:
'guards' => [
'user' => [
'driver' => 'extended_session',
'provider' => 'users',
],
'admin' => [
'driver' => 'extended_session',
'provider' => 'admins',
],
]
In AppServiceProvider.php
\Auth::extend('extended_session', function ($app, $name, $config) {
$providerConfig = $this->app['config']['auth.providers.'.$config['provider']];
// If you don't use eloquent you need to alter the next line accordingly
$provider = new EloquentUserProvider($app['hash'], $providerConfig['model']);
return new SessionGuardExtended('extended_session', $provider, $this->app['session.store']);
});
And add a new Class named SessionGuardExtended like this:
use Illuminate\Auth\SessionGuard;
class SessionGuardExtended extends SessionGuard{}
This results in a shared session with the same auth name for domain and subdomains.

Add SESSION_DOMAIN to your .env file and set it to .example.com

Related

I need to know which is better ? to use session or auth()->user()->property

I'm working on a social website and when I login, I need some information about the user to use in the front-end such as name, image link, username, id.
So which is better to use, a session to store all data I need, or just using
auth()->user()->property?
The auth() helper function or the facade for that matter is already using the session as a driver for the web guard by default...
config/auth.php
'guards' => [
'web' => [
'driver' => 'session', // <-- Here
'provider' => 'users',
],
]
So what you're really asking is if the session or the session is better...
you can use auth() helpler :
auth()->user()
Or Auth Facade :
Auth::user()
both the same and get authenticate user details from database
You can use auth helper for that and it's optimal enough cause its in global bootstraped $app variable witch loads in the beginning of laravel kernel loading.
Also its a good way to load other everytime-needed attributes from user in the auth() helper like auth()->id.
You can use marco to add such data to auth helper this way:
Add this to AppServiceProvider boot method or any custom provider you have to add extra field to auth helper through SessionGuard which is macroable:
SessionGuard::macro('mobile', function () {
return auth()->check() ? auth()->user()->mobile : null;
});
Now everywhere you need simply use
auth()->mobile();

Separate authentication for front-end user and admin in cakephp 3.x

We are working on a project where are 4 roles. But in cakephp 3.x Auth component holds authenticate user data in session with Auth.User indexing using
$this->Auth->setUser($user);
Due to this we are not able to access front-end user account from admin panel for some purpose, because of when we login to front-end user from admin panel, front-end login action performs and over write of session value.
So if there is any process to handle this please suggest us.
Thank you in advance.
As well I have understood that you are not using prefix to manage back-end and front-end user then may be you worked with separate folder structure for back-end, May I right?
You are right that $this->Auth->setUser($user); always holds session with Auth.User indexing. So you need to write different session indexing for back-end, and you can do it as follow :
For back-end user authentication :
**
$this->loadComponent('Auth', [
'authorize' => ['Controller'], // Added this line
'loginRedirect' => [
'controller' => 'Users',
'action' => 'dashboard',
'prefix' => 'admin_panel'
],
'logoutRedirect' => [
'controller' => 'Users',
'action' => 'login',
'prefix' => 'admin_panel'
],
'storage' => [
'className' => 'Session',
'key' => 'Auth.Admin',
]
]);
**
Here you can pass your desired index in 'storage' array key value.
I think it'll works for you.
Check out the section Authentication and Authorization in this curated list of CakePHP Plugins.
You could, for example, use dereuromarks TinyAuth Plugin to authorize your users and configure what they are able to see.
This way you can use the same authentication (be aware of the differences between Authentication and Authorization) and the same users table, which will prevent the Session conflict you mentioned.
The Auth component overwrite the previous session because it store the session in Auth.users all the time so we have to change the session key for different role.
If you are using URL prefix for the different roles to access then you can do like this.
AppController.php
public function beforeFilter(Event $event)
{
if($this->request->params['prefix']){
$this->Auth->config('storage', [
'key'=>'Auth.'.$this->request->params['prefix'],
'className'=>'Session'
]);
}
return parent::beforeFilter($event); // TODO: Change the autogenerated stub
}
This will create different roles in Auth as you required.
The session will be like this
[
'Auth'=>[
'User'=>['id'=>''],
'Admin'=>['id'=>''],
]
]
Tested it, working great for me.

How to limit user actions with Laravel Passport Scopes + Password Grant Type

I have set up the Laravel Passport package for Laravel 5.3 just as described in the official documentation (https://laravel.com/docs/5.3/passport#introduction).
I want the API to be consumed by a mobile application, so I am trying to implement Password Grant Tokens. I have created a password grant client, and the token request process...
$response = $http->post('http://my-app.com/oauth/token', [
'form_params' => [
'grant_type' => 'password',
'client_id' => 'client-id',
'client_secret' => 'client-secret',
'username' => 'my#email.com',
'password' => 'my-password',
'scope' => '',
],
]);
...Just works as expected, returning an access-token and a refresh-token for one of my users.
But now I want to define some scopes so I can limit the access of users... Following the documentation again, I have them defined in boot method of AuthServiceProvider.php like:
Passport::tokensCan([
'admin' => 'Perform every action',
'user' => 'Perform only normal user actions',
]);
In this scenario, if a "malicious" normal user requested a token (using the above POST call) specifying 'scope' => 'admin', he or she would get an 'admin' token... and that is not what I want.
Thus, I would like to know how is the workflow in this situation to effectively limit the access to normal users, and where do I have to implement the scope validation logic.
Thanks in advance.
One way to go about this would be to create a middleware
For example if you only want users with an email from example.com to request the admin domain you can do something like this
Example ScopeLogic.php middleware:
if ($request->input('grant_type') === 'password') {
$scope = $request->input('scope');
$username = $request->input('username');
if ($scope === 'admin' && (strpos($username, '#example.com') === false)) {
return response()->json(['message' => "Not authorized to request admin scope"], 401);
}
}
return $next($request);
Of course, you would have to add this scope to your $routeMiddleware array in Kernel.php
protected $routeMiddleware = [
...
'check-scopes' => \App\Http\Middleware\ScopeLogic::class
]
As well as wrap Passport::routes() in AuthServiceProvider.php to check for this middleware
\Route::group(['middleware' => 'check-scopes'], function() {
Passport::routes();
});
Passport will also check that a correct username and passport combination was passed so you don't have to worry about that in the middleware
In my opinion, I think what confuses most people with OAuth and APIs is that scopes are tied to "clients" and not the "resource owner" themselves. Clients should be able to talk to an API using an admin scope or no scopes at all if needed. If they use an admin-ish type scope together with user context (password grant, authorization code grant, etc), then there is no stopping them from making calls that require such a scope against that user in the API. To me, the only person that can truly be classified as malicious would be one who manages to steal an access token containing an admin scope. That is why API implementors are allowed to specify what scopes to grant a client and if it's a first party app that uses something like the Password Grant, then you as a user has no choice but to trust it with your data.
I don't know how one would do this and use the retrieved token inside another's mobile app but if you did try requesting a token manually yourself with an admin scope, then I really don't see anything wrong that (other than you giving the app more control with you set as user context, so it may even be counter productive?)
If you need more control than that, then you need to go past your API and create something like application-level permissions for each user inside your resource server.
I forget where I read it, some Github issues somewhere, but apparently Laravel doesn't have that ability built in, each client is the treated the same equally, out of the box.
A user provided a good solution, and I built upon it here: https://stackoverflow.com/a/55285483/1132557

Authenticate with a cookie using laravel 5.1 and jwt

I'm setting up a laravel app (v5.1) with jwt auth (https://github.com/tymondesigns/jwt-auth). Got a boilerplate from this project: https://github.com/francescomalatesta/laravel-api-boilerplate-jwt . This way I want to be able to authenticate different apps in the future.
My problem is that I'm trying to create some protected pages in the same project, but I'm currently unable to stop users without the cookie (not authenticated). To "protect" the access I have the following values in my $routeMiddleware (in Kernel.php):
'csrf' => \csd\Http\Middleware\VerifyCsrfToken::class,
'auth' => \csd\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'guest' => \csd\Http\Middleware\RedirectIfAuthenticated::class,
'jwt.refresh' => \Tymon\JWTAuth\Middleware\RefreshToken::class
I also tried to add 'jwt.auth' => \Tymon\JWTAuth\Middleware\GetUserFromToken::class to this array, but nothing changed.
How can I block users from accessing my pages while using this jwt-auth?
Thanks in advance.
To apply it on all routes, you must put it in the $middleware, not the $routeMiddleware property on the Http kernel (app/Http/Kernel.php).
Alternatively, when using it with the $routeMiddleware, you must reference explicitly it in your route definition; something like this:
Route::group(['middleware'=>'jwt.auth'], function($router){
$router->resource('protected-resource', 'SecretSauceController');
});

CakePHP Missing ApplicationNameController

I have had installed and configured a new CakePHP 2.4 instance. Now I wanted to implement the AuthComponent. I have had implemented the component exactly as in recent projects, too. Now I have the problem that I get the error message Missing Controller: SteadinessController. But Steadiness is the project name. I'm confused haha.
The URL I want to open is http://localhost/steadiness/users/view/2 and I will be redirected to http://localhost/steadiness/steadiness/users/login. The controller and action invoked is correct but the second steadiness is false.
Thanks a lot for help.
Alex
Can you post the code where you try to direct the user to this url?
I'm guessing it's a redirect to do with the Auth component.
In your AppController when you set up Auth there should be something similar yo:
public $components = array(
'Session',
'Auth' => array(
'loginRedirect' => '/',
'logoutRedirect' => '/',
'authorize' => 'controller',
'unauthorizedRedirect' => '/'
)
);
The loginRedirect tells Cake where to send users after login who went directly to the login page (they didn't try to access a restricted link whilst not logged in).
The logoutRedirect tells Cake where to send users after logout.
The unauthorizedRedirect tells Cake where to send logged in users that try to access a link that they do not have the priveleges for (e.g. a non-admin tries to delete an account - only admins should be able to do that).
If one of these is not set then when Cake tries to use it you get redirected to '/{app-directory}' which is NOT the same as '/'.
This is most likely why your url has the domain appear twice - so Cake thinks it is looking for a controller with the same name as your domain.

Categories