Is there a way to rename Laravel's remember cookie prefix "remember_web_*"?
I have 3 Laravel projects on the same server, can be accessed by IP address, ports distinguish the projects. However, because of cookies are shared between ports, all projects read all cookies, every project can distinguish its own session cookies, but remember token cookies can't be distinguished. So only one project's session can be remembered at the same time.
Is there a way to rename Laravel's remember cookie prefix "remember_web_*"?
You'd have to create your own SessionGuard which extends from the original since there is no option to define this cookie name. It might be worth a PR for the original framework though.
Step 1: create an override for the original SessionGuard:
<?php
namespace App\Guards;
use Illuminate\Auth\SessionGuard;
class MySessionGuard extends SessionGuard
{
public function getRecallerName()
{
// The original class does:
// `return 'remember_'.$this->name.'_'.sha1(static::class);`
// which is the same for every app.
// Return your own cookie name:
return 'remember_me_some_app';
}
}
Step 2: add the guard to the bootstrap:
use App\MySessionGuard;
use Illuminate\Auth\EloquentUserProvider;
// SomeServiceProvider.php
public function boot() {
Auth::extend(
'my_session_guard',
function ($app) {
$provider = new EloquentUserProvider($app['hash'], config('auth.providers.users.model'));
$guard = new MySessionGuard('my_session_guard', $provider, app()->make('session.store'), request());
$guard->setCookieJar($this->app['cookie']);
$guard->setDispatcher($this->app['events']);
$guard->setRequest($this->app->refresh('request', $guard, 'setRequest'));
return $guard;
}
);
}
Step 3: define the new guard in config/auth.php:
'guards' => [
'web' => [
'driver' => 'my_session_guard',
'provider' => 'users',
],
]
When logging in with remember-me checked, the new cookie name should now be used.
Related
Is it possible to catch external requests in Laravel Telescope. I'm new to telescope and I've done my research but I couldn't find any blog/article that mentioned this except this but it didn't work for me
I've Installed telescope on my app according to the documentation, Iv'e created a new watcher called GuzzleRequestWatcher and registered it under config/telescope.php, I've also created a test route that sends an http::post message to this. Telescope is catching my API request and recording it under requests as shown in the screenshot but I need it to see the URL the request is hitting not only the route for example rather than showing in Path '/api/v1/guzzle-test' I need it to show the URL I'm requesting 'http://httpbin.org/anything'.screenshot
<?php
declare(strict_types=1);
namespace App\Telescope\Watchers;
use Closure;
use GuzzleHttp\Client;
use GuzzleHttp\TransferStats;
use Illuminate\Foundation\Application;
use Illuminate\Support\Facades\Log;
use Laravel\Telescope\IncomingEntry;
use Laravel\Telescope\Telescope;
use Laravel\Telescope\Watchers\FetchesStackTrace;
use Laravel\Telescope\Watchers\Watcher;
final class GuzzleRequestWatcher extends Watcher
{
use FetchesStackTrace;
public function register($app)
{
$app->bind(Client::class, $this->buildClient($app));
}
private function buildClient(Application $app): Closure
{
return static function (Application $app): Client {
$config = $app['config']['guzzle'] ?? [];
if (Telescope::isRecording()) {
$config['on_stats'] = function (TransferStats $stats) {
$caller = $this->getCallerFromStackTrace();
Telescope::recordQuery(
IncomingEntry::make([
'connection' => 'guzzle',
'bindings' => [],
'sql' => (string) $stats->getEffectiveUri(),
'time' => number_format(
$stats->getTransferTime() * 1000,
2,
''
),
'slow' => $stats->getTransferTime() > 1,
'file' => $caller['file'],
'line' => $caller['line'],
'hash' => md5((string) $stats->getEffectiveUri()),
])
);
};
}
return new Client(
$config
);
};
}
}
I have this new Laravel project to work on. We would like to make it available in multiple languages.
I started the project with JetStream. Routes for authentication and such are automatically handled by JetStream / Fortify. I then added https://github.com/mcamara/laravel-localization to handle the localization. it works fine for the routes I created myself :
Route::group(
[
'prefix' => LaravelLocalization::setLocale(),
'middleware' => [ 'localeSessionRedirect', 'localizationRedirect', 'localeViewPath' ]
], function()
{
Route::get('/', function () {
return view('welcome');
});
Route::middleware(['auth:sanctum', 'verified'])->get('/dashboard', function () {
return view('dashboard');
})->name('dashboard');
});
But how can I set the group, prefix and middleware on the routes handled by Jetstream and Fortify?
[EDIT]
So after some suggestions from #TEFO, I'm trying to add a middleware to handle setting the locale. Added :
Fortify.php :
'path' => '{lang}',
'middleware' => ['web', 'setLang']
new middleware setLang :
class SetLang {
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle(\Illuminate\Http\Request $request, Closure $next) {
// $lang = 'en';
// $request->attributes->add(['lang' => 'en']);
$request->route()->setParameter('lang', 'en');
// $request->request->set('lang', 'en');
return $next($request);
}
}
Added the middleware to $routeMiddleware.
I'm receiving this error when trying to reach http://mylaravel/en/login :
ErrorException
Missing required parameters for [Route: login] [URI: {lang}/login]. (View: /var/www/resources/views/auth/login.blade.php)
Finally successfully nailed this. I simply disabled routes from Fortify and Jetstream, copied them over and shoved them inside my grouped prefix routes. Still using https://github.com/mcamara/laravel-localization but it should work anyway you want it - make your own system or whatever, as long as you control the routes you're good to go.
In JetstreamServiceProvider :
public function register() {
Jetstream::ignoreRoutes();
}
In FortifyServiceProvider :
public function register() {
Fortify::ignoreRoutes();
}
And copy over routes from Fortify vendor/laravel/fortify/routes/routes.php and Jetstream vendor/laravel/jetstream/routes/livewire.php (I guess adapt to Inertia if you're working with this) over to your web.php file, inside a route group with the prefix you need.
I faced almost the same problem with the expection that i do not use mcamara/laravel-localization at the moment.
Based on the useful discussion above between #JeremyBelolo and #TEFO, the following solution worked for me:
Added 'path' => '{locale}/my-secret-path' to config/fortify.php. As #JeremyBelolo and #ETO discussed, the support for that was recenlty added.
Added my middleware before \Laravel\Jetstream\Http\Middleware\AuthenticateSession::class to the web $middlewareGroups
Where my middleware set the locale app()->setLocale($locale); and the default {locale} url parameter URL::defaults(['locale' => $locale]); before passing the request deeper into the application.
Considering Jetstream I had to apply the same steps as #JeremyBelolo did, exept I didn't copy the jetsream/livewire routes but used the following inside the route group:
require base_path('vendor/laravel/jetstream/routes/livewire.php');
Now I can access {locale}/my-secret-path/login where {locale} is a supported locale for my site.
UPDATE [Fortify config option changed]:
The path fortify config option changed to prefix. Thus in config/fortify.php the following key should be used:
'prefix' => '{locale}/my-secret-path'
I made a new Laravel Project using Jetstream. I wanted to use multi-language support in my project, but when I used Prefix (en/login, de/login) according to languages in url, I was also having a problem with Route. I solved my problem by following these steps. I hope you will be useful too:
1 - I have included the package on this https://github.com/mcamara/laravel-localization in my project. and followed the instructions sequentially.
2 - I made the Route settings in the "rautes\web.php" file as follows.
Route::group(['prefix' => LaravelLocalization::setLocale(),'middleware' => [
'localeSessionRedirect', 'localizationRedirect','localeViewPath' ]], function(){
/** ADD ALL LOCALIZED ROUTES INSIDE THIS GROUP **/
Route::get('/', function () {return view('welcome');});
Route::middleware(['auth', 'verified'])->get('/dashboard', function () {
return view('back.dashboard');})->name('dashboard');
});
3 - I have included the in app\Http\Middleware\Kernel.php. In middlewareGroups end of web prefix.
protected $middlewareGroups = [
'web' => [....
\Mcamara\LaravelLocalization\Middleware\LaravelLocalizationRoutes::class,
\Mcamara\LaravelLocalization\Middleware\LaravelLocalizationRedirectFilter::class,
\Mcamara\LaravelLocalization\Middleware\LocaleSessionRedirect::class,
\Mcamara\LaravelLocalization\Middleware\LocaleCookieRedirect::class,
\Mcamara\LaravelLocalization\Middleware\LaravelLocalizationViewPath::class,]
4 - Fortify Routes, include prefix in vendor\laravel\fortify\routes.php - Route::group like this:
Route::group(['prefix' => LaravelLocalization::setLocale(),
'middleware' => config('fortify.middleware', ['web'])], function () {
$enableViews = config('fortify.views', true);
.......
5 - Livewire Routes, include prefix in vendor\laravel\jetstream\routes\livewire.php - Route::group like this:
Route::group(['prefix' => LaravelLocalization::setLocale(),
'middleware' =>config('jetstream.middleware', ['web'])], function () {
if (Jetstream::hasTermsAndPrivacyPolicyFeature()) {
Route::get('/terms-of-service', [TermsOfServiceController::class, 'show'])-
>name('terms.show');
Route::get('/privacy-policy', [PrivacyPolicyController::class, 'show'])-
>name('policy.show');}
6 - If you want to separate backend and frontend, you can add in app\Http\Middleware\Kernel.php end of protected $routeMiddleware with prefix like in this https://github.com/mcamara/laravel-localization.
protected $routeMiddleware = [
........
'localize'=> \Mcamara\LaravelLocalization\Middleware\LaravelLocalizationRoutes::class,
'localizationRedirect' => \Mcamara\LaravelLocalization\Middleware\LaravelLocalizationRedirectFilter::class,
'localeSessionRedirect' => \Mcamara\LaravelLocalization\Middleware\LocaleSessionRedirect::class,
'localeCookieRedirect' => \Mcamara\LaravelLocalization\Middleware\LocaleCookieRedirect::class,
'localeViewPath' => \Mcamara\LaravelLocalization\Middleware\LaravelLocalizationViewPath::class,
]
7 - And the happy end...
I designed a site with Laravel. now I want add new language to it.I read laravel document . It was good but I have a problem.suppose I have a page that show detail of products so I have a route like mysite.com/product/id that get product's id and show it.also I have a method in controller like
public function showProduct($id){
...
}
If I add new Language , the route will change to this: mysite/en/product/id
now I must change my method because now two parameter send my method.something like this :
public function showProduct($lang,$id){
...
}
So two problems arise:
I must change all method in my site which is time consuming
I do not need language parameter in methods because I set $locan via middleware
pay attention that I do not want remove for example en from my URL (because of SEO)
Open your RouteServiceProvider and say that language parameter actually is not a parameter, it's a global prefix.
protected function mapWebRoutes()
{
Route::group([
'middleware' => 'web',
'namespace' => $this->namespace,
'prefix' => Request::segment(1) // but also you need a middleware about that for making controls..
], function ($router) {
require base_path('routes/web.php');
});
}
here is sample language middleware, but it's need to be improve
public function handle($request, Closure $next)
{
$langSegment = $request->segment(1);
// no need for admin side right ?
if ($langSegment === "admin")
return $next($request);
// if it's home page, get language but if it's not supported, then fallback locale gonna run
if (is_null($langSegment)) {
app()->setLocale($request->getPreferredLanguage((config("app.locales"))));
return $next($request);
}
// if first segment is language parameter then go on
if (strlen($langSegment) == 2)
return $next($request);
else
// if it's not, then you may want to add locale language parameter or you may want to abort 404
return redirect(url(config("app.locale") . "/" . implode($request->segments())));
}
So in your controller, or in your routes. you don't have deal with language parameter
Something like
Route::group(['prefix' => 'en'], function () {
App::setLocale('en');
//Same routes pointing to the same methods...
});
Or
Route::group(['prefix' => 'en', 'middleware' => 'yourMiddleware'], function () {
//Same routes pointing to the same methods...
});
I'm building a multi-tenant app, using the subdomain to separate the users.
e.g. .myapp.com
I want to give each tenant their own database too.
How can I detect the subdomain and set the database dynamically?
Also, the code below is from the official documentation and shows us how we can get the subdomain when setting up a route. But how do we pass the subdomain value to a controller function?
Route::group(array('domain' => '{account}.myapp.com'), function()
{
Route::get('user/{id}', function($account, $id)
{
//
});
});
The best way to achieve this would be in a before filter that you apply to the route group.
Route::group(['domain' => '{account}.myapp.com', 'before' => 'database.setup'], function()
{
// Your routes...
}
This before filters gets a $route parameter and a $request parameter given to it, so we can use $request to get the host.
Route::filter('database.setup', function($route, $request)
{
$account = $request->getHost();
}
You could then use the account to adjust the default database connection using Config::set in the filter. Perhaps you need to use the default connection first up to fetch the users database details.
$details = DB::details()->where('account', '=', $account)->first();
// Make sure you got some database details.
Config::set('database.connections.account', ['driver' => 'mysql', 'host' => $details->host, 'database' => $details->database, 'username' => $details->username, 'password' => $details->password]);
Config::set('database.connections.default', 'account');
During runtime you create a new database connection and then set the default connection to that newly created connection. Of course, you could leave the default as is and simply set the connection on all your models to account.
This should give you some ideas. Please note that none of this code was tested.
Also, each method on your controllers will receive the domain as the first parameter. So be sure to adjust for that if you're expecting other parameters.
I'm planning to use ZF2 in a future project, so I'm trying Zend Framework 2 RC1 now. I started with authentication step, and noticed that when i chose a different name than 'Zend_Auth' for session storage namespace, i can't access to object stored in session (AuthenticationService class' hasIdentity method returned false, despite User object data set in session).
<?php
namespace Application\Controller;
use Zend\Authentication\Adapter\DbTable as AuthAdapter;
use Zend\Authentication\AuthenticationService;
use Zend\Authentication\Storage\Session as SessionStorage;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Application\Model\User;
use Application\Form\LoginForm;
class LoginController extends AbstractActionController
{
public function indexAction()
{
$auth = new AuthenticationService();
if ($auth->hasIdentity()) {
return $this->redirect()->toRoute('application');
}
$form = new LoginForm();
return array('form' => $form);
}
public function loginAction()
{
$auth = new AuthenticationService();
$form = new LoginForm();
$form->get('submit')->setAttribute('value', 'Add');
$request = $this->getRequest();
if ($request->isPost()) {
$user = new User();
$form->setInputFilter($user->getInputFilter('login'));
$form->setData($request->getPost());
if ($form->isValid()) {
$data = $form->getData();
// Configure the instance with constructor parameters...
$sm = $this->getServiceLocator();
$dbAdapter = $sm->get('db-adapter');
$authAdapter = new AuthAdapter($dbAdapter, 'users', 'username', 'password');
$authAdapter
->setIdentity($data['username'])
->setCredential(sha1($data['password']));
// Use 'users' instead of 'Zend_Auth'
$auth->setStorage(new SessionStorage('users'));
$result = $auth->authenticate($authAdapter);
if ($result->isValid()) {
// store the identity as an object where only the username and
// real_name have been returned
$storage = $auth->getStorage();
// store the identity as an object where the password column has
// been omitted
$storage->write($authAdapter->getResultRowObject(
null,
'password'
));
// Redirect to list of application
return $this->redirect()->toRoute('application');
}
}
}
// processed if form is not valid
return array('form' => $form);
}
}
In this code, when i changed the below line,
$auth->setStorage(new SessionStorage('users'));
like this:
$auth->setStorage(new SessionStorage());
hasIdentity method returned true.
I checked two classes Zend\Authentication\AuthenticationService and Zend\Authentication\Storage\Session, and didn't see a way to access session data which has different session namespace other than default.
What i need to understand is how can i access session data which has a different namespace and if there is no way to do it for now, should we define this as a bug?
I can update the question if any other information needed.
We are kinda missing one part of your code, the one where you try and receive the user identity. im guessing that you have forgotten to pass the the SessionStorage Object with the same namespace.
Also the configuration of the Authentication object should be moved to a factory so these kind of issues to not arrise.
Thats my five cents atleast :)