Laminas Authentication adapter is being overwritten - laminas

I'm running into an issue where Laminas's Authentication Service's adapter is being overwritten from another module. How I have it set up is that under the Application module it sets the adapter, which is fine, but when I have it set the Adapter under another module, it overwrites the adapter in Application, or so I think that this is the issue, because when I prevent the other module from being initialized, it works just fine.
Here is the code I have in place for Module.php in the Application Module:
AuthenticationService::class => function($container) {
$db_adapter = $container->get(Adapter::class);
$auth_adapter = new DbTableAuthAdapter($db_adapter, 'users', 'username', 'password');
$auth_service = new AuthenticationService();
$auth_service->setAdapter($auth_adapter);
$auth_service->setStorage($container->get(LoginAuthStorage::class));
return $auth_service;
},
And here is the code in the other module
AuthenticationService::class => function($container) {
$db_adapter = $container->get(Adapter::class);
$auth_adapter = new DbTableAuthAdapter($db_adapter, 'admins', 'username', 'password');
$auth_service = new AuthenticationService();
$auth_service->setAdapter($auth_adapter);
$auth_service->setStorage($container->get(AdminLoginAuthStorage::class));
return $auth_service;
},
For further clarification, here is the list of module namespaces being loaded:
/**
* List of enabled modules for this application.
*
* This should be an array of module namespaces used in the application.
*/
return [
'Laminas\Navigation',
'Laminas\Mail',
'Laminas\Di',
'Laminas\Log',
'Laminas\Db',
'Laminas\Mvc\Plugin\FilePrg',
'Laminas\Mvc\Plugin\FlashMessenger',
'Laminas\Mvc\Plugin\Identity',
'Laminas\Mvc\Plugin\Prg',
'Laminas\Session',
'Laminas\Mvc\I18n',
'Laminas\Form',
'Laminas\Hydrator',
'Laminas\InputFilter',
'Laminas\Filter',
'Laminas\I18n',
'Laminas\Router',
'Laminas\Validator',
'Laminas\Diactoros',
'Application',
'Admin',
];
If I comment out 'Admin', it works fine but if not, it tries to fetch the adapter I defined for the admin module under application.
I apologize in advance if my question is unclear, I am uncertain how to phrase it to be honest. What I am basically am trying to do is to have one authentication service for users under the application module and an authentication service for admins under the admin module.
Thanks!

Related

Migration to Minimal API - Test Settings Json not overriding Program

Thanks to this answer: Integration test and hosting ASP.NET Core 6.0 without Startup class
I have been able to perform integration tests with API.
WebApplicationFactory<Program>? app = new WebApplicationFactory<Program>()
.WithWebHostBuilder(builder =>
{
builder.ConfigureServices(services =>
{
});
});
HttpClient? client = app.CreateClient();
This has worked using the appsettings.json from the API project. Am now trying to use integrationtestsettings.json instead using:
IConfiguration configuration = new ConfigurationBuilder()
.SetBasePath(ProjectDirectoryLocator.GetProjectDirectory())
.AddJsonFile("integrationtestsettings.json")
.Build();
WebApplicationFactory<Program>? app = new WebApplicationFactory<Program>()
.WithWebHostBuilder(builder =>
{
builder.ConfigureAppConfiguration(cfg => cfg.AddConfiguration(configuration));
builder.ConfigureServices(services =>
{
});
});
_httpClient = app.CreateClient();
I have inspected the configuration variable and can see the properties loaded from my integrartiontestsettings.json file. However, the host is still running using the appsettings.json from the server project.
Previously, in .Net5, I was using WebHostBuilder and the settings were overridden by test settings.
WebHostBuilder webHostBuilder = new();
webHostBuilder.UseStartup<Startup>();
webHostBuilder.ConfigureAppConfiguration(cfg => cfg.AddConfiguration(_configuration));
But cannot get the test settings to apply using the WebApplicationFactory.
It seems the method has changed.
Changing:
builder.ConfigureAppConfiguration(cfg => cfg.AddConfiguration(configuration));
To:
builder.UseConfiguraton(configuration);
has done the trick.
builder.ConfigureAppConfiguration, now it's configuring the app (after your WebApplicationBuilder.Build() is called) and your WebApplication is created.
You need to "inject" your configurations before the .Build() is done. This is why you need to call UseConfiguraton instead of ConfigureAppConfiguration.

Laravel 8 Jetstream: unable to login with the account seeded using factories and seeder

I am working on a Laravel 8 project. I have noticed that a couple of things have changed including authentication. I am using Jetstream for authentication.
I have installed the Jetstream and I can register and login going to the route /register and /login on the browser. What I am doing now is that for local development, I am creating seeder class so that I can seed the users and log in using those seeded users for local development. But when I log in using those account, it is always complaining that "These credentials do not match our records.".
This is what I have done. I have registered an account on browser using password, "Testing1234". The password hash is saved in the users table. I copied the password and use it in the UserFactory class as follow.
<?php
namespace Database\Factories;
use App\Models\Role;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
class UserFactory extends Factory
{
use WithFaker;
/**
* The name of the factory's corresponding model.
*
* #var string
*/
protected $model = User::class;
/**
* Define the model's default state.
*
* #return array
*/
public function definition()
{
return [
'name' => $this->faker->name,
'email' => $this->faker->unique()->safeEmail,
'email_verified_at' => now(),
'password' => '$2y$10$tive4vPDzIq02SVERWxkYOAeXeaToAv57KQeF1kXXU7nogh60fYO2', //Testing.1234
'remember_token' => Str::random(10),
];
}
}
Then I created a user using factory as follow.
User::factory()->create(['email' => 'testing#gmail.com']);
Then I tried to log in using the user I just created. But it is always complaining, "These credentials do not match our records.". I cannot use the other passwords too. Even the default password that comes with the default user factory class. What is wrong with my code and how can I fix it?
Try using
User::factory()->make([
'email' => 'testing#gmail.com',
]);
I have finally found the issue.
In the JetstreamServiceProvider class, I have added the following code to customise the login flow.
Fortify::authenticateUsing(function (Request $request) {
});
My bad. That is what makes it failing.

Using CakePHP Form and Basic Authentication together

I've created a simple test site using CakePHP 3.8 and Authentication 1.0 to try it out. I'd like to use both Form and Basic authentication since the intended app will offer REST calls.
The site works properly if the HttpBasic is not included, that is the Login window is displayed. However, with HttpBasic, the site goes directly to basic authentication.
The code is directly from the cookbook.
What am I missing?
public function getAuthenticationService(ServerRequestInterface $request, ResponseInterface $response)
{
$service = new AuthenticationService();
$service->setConfig([
'unauthenticatedRedirect' => '/users/login',
'queryParam' => 'redirect'
]);
$fields = [
'username' => 'user',
'password' => 'password',
];
// Load Identifiers
$service->loadIdentifier('Authentication.Password', compact('fields'));
// Load the authenticators
$service->loadAuthenticator('Authentication.Session');
$service->loadAuthenticator('Authentication.Form', [
'fields' => $fields,
'loginUrl' => '/users/login',
]);
$service->loadAuthenticator('Authentication.HttpBasic');
return $service;
}
As mentioned in the comments, using the form authenticator and the HTTP basic authenticator together won't work overly well, this is due to the fact that the authentication service won't stop executing all loaded authenticators, unless one of them returns a response that indicates successful authentication.
This means that you'd always be presented with the authentication challenge response, and never see your login form. Only the actual authentication part would work in that constellation, ie directly sending your login credentials as form data to the login endpoint.
If you don't actually need the basic auth challenge response that is preventing you from accessing the login form, then you could use a custom/extended authenticator that doesn't cause a challenge response to be returned, which should be as simple as overriding \Authentication\Authenticator\HttpBasicAuthenticator::unauthorizedChallenge():
src/Authenticator/ChallengelessHttpBasicAuthenticator.php
namespace App\Authenticator;
use Authentication\Authenticator\HttpBasicAuthenticator;
use Psr\Http\Message\ServerRequestInterface;
class ChallengelessHttpBasicAuthenticator extends HttpBasicAuthenticator
{
public function unauthorizedChallenge(ServerRequestInterface $request)
{
// noop
}
}
$service->loadAuthenticator(\App\Authenticator\ChallengelessHttpBasicAuthenticator::class);
Also not that you might need to add additional checks in case your application uses the authentication component's setIdentity() method, which would cause the identity to be persisted in the session, even when using stateless authenticators. If you don't want that, then you'd need to test whether the successful authenticator is stateless before setting the identity:
$provider = $this->Authentication->getAuthenticationService()->getAuthenticationProvider();
if (!($provider instanceof \Authentication\Authenticator\StatelessInterface))
{
$this->Authentication->setIdentity(/* ... */);
}

Phalcon forward to different module

Trying to work my way through setting up a phalcon mvc application.
I have 2 modules current set up for testing. "Frontend" and "Admin".
I have different views set up so I can confirm I am getting through to each of the modules. When I change the defaultnamespace and defaultmodule I can indeed see that both modules are being accessed fine and loading ok. I can see that the admin controllers are being accessed correctly and the frontend controllers are being accessed when I change this.
The problem I am currently having is when I try to authenticate a user and start the session I want to forward the request over from "Frontend" to "Admin":
return $this->dispatcher->forward(array(
'namespace' => 'Qcm\Admin\Controllers',
'action' => 'index',
'controller' => 'index'
));
Again I have confirmed these namespaces work fine. The problem is when I now forward onto the new namespace it can no longer find the admin index controller?
"Qcm\Admin\Controllers\IndexController handler class cannot be loaded"
However I have already confirmed that I can switch between the modules by changing the defaultnamespace/defaultmodule. Is this a limitation within the dispatcher that I can not forward to a different module?
Just to clarify I am also using the same url's so for example after login I want it to go back to '/' (root) but because it has forwarded to the admin module this should work fine correct?
The phalcon dispatcher can only forward to actions within the same module. It cannot forward you outside the current module. This restriction comes because the dispatcher is only aware of the module in which it is declared.
In order to forward to another module, you must instead return a redirect response from a controller action. In my case, I wanted to forward the user to a login screen or a 404 error page based on their ACL permissions in the beforeDispatch() method of a plugin. The dispatcher is native to this method, but cannot forward the user outside of the current module. Instead, I have the dispatcher forward the user to a controller in the same module that has a custom action that in turn performs the redirect.
// hack to redirect across modules
$dispatcher->forward(
array(
'controller' => 'security',
'action' => 'redirect',
'params' => array(
'redirect' => '/home/index/login'
),
)
);
return false; // stop progress and forward to redirect action
This means that each module needs to have a copy of this custom redirect action in one of its controllers. I accomplished this by putting the action in the base controller that all of my controllers extend from.
/**
* the phalcon dispatcher cannot forward across modules
* instead, forward to this shared action which can then redirect across modules
* */
public function redirectAction(){
$this->view->disable();
$params = $this->dispatcher->getParams();
$redirect = '/';
if( ! empty( $params['redirect'] ) ){
$redirect = $params['redirect'];
}
return $this->response->redirect( $redirect );
}
Because phalcon didn't add all modules to global loader, so the namespace is not registered. you need to register another module in current module bootstrap file, modify your Module.php as
class Module
{
public function registerAutoloaders()
{
$loader = new \Phalcon\Loader();
$loader->registerNamespaces(array(
//Your current module namespaces here
....
//Another module namespaces here
'Qcm\Admin\Controllers' => 'controller path',
));
$loader->register();
}
}
The main reason it shows IndexController class not loaded is because you might not have added Dispatcher in that module or in the bootstrap file(depending on your approach)
Add
$debug = new \Phalcon\Debug();
$debug->listen();
to your code before
$application->handle()->getcontent();
to view erros.
Before the replacement line:
echo $application->handle()->getContent();
code:
$router = $this->getDi()->get("router");
$params = $router->getParams();
$modName = $router->getModuleName();
$url = null;
if ($modName == "admin" && isset($params[0])) {
$module = "/" . $params[0];
$controller = isset($params[1]) ? "/" . $params[1] . "/" : null;
$action = isset($params[2]) ? $params[2] . "/" : null;
$params = sizeof($params) > 3 ? implode("/", array_slice($params, 3)) . "/" : null;
$url = $module . $controller . $action . $params;
}
echo $application->handle($url)->getContent();

ZF2 + How to change session timeout with Zend\Authentication

I was able to define my own authentication service with Zend\Authentication. Now I want to be able to change the session timeout limit.
I don't want to change the general session config but only for this Authentication manager.
As I want to reuse this adapter, I want to find a way to do it through the module.config.php configuration file.
For the moment, I am creating my service like that
public function getServiceConfig()
{
return array(
'factories' => array(
'cas_auth_service' => function ($sm) {
$authService = new \Zend\Authentication\AuthenticationService;
$authService->setAdapter($sm->get('\CasAuthen\Authentication\Adapter\Cas'));
return $authService;
},
),
);
}
I was thinking about creating my own Authentication Storage adapter and then injecting it but I don"t know if it is the right way to do it.
Does anymore know how to proceed ?
Thank you in advance and have a nice day.