1

I'm currently implementing the login feature in CakePHP4. So I wanted to divide it into a normal user model and an administrative user model.

I didn't know how to do this at all, and I spent all day looking for ways to implement it, but I couldn't do it.

I want to set up authentication in "routes.php", but I'm having trouble with "implementing AuthenticationServiceProviderInterface" in "Application.php". What should I do?

// routes.php

$routes->scope('/', function (RouteBuilder $builder) {
    /......./

    // ログイン
    //$builder->connect('mgt-account/*', ['controller' => 'MgtAccount', 'action' => 'login', 'prefix' => 'Admin']);
    $builder->connect('users/*', ['controller' => 'Users', 'action' => 'login', 'prefix' => 'Normal']);
  
    /......./
});

$routes->prefix('Normal', function (RouteBuilder $routes) {

    $loginUrl = Router::url('/normal/users/login');
    $fields   = [
        'username' => 'mail',
        'password' => 'password',
    ];
    $service = new AuthenticationService([
        'unauthenticatedRedirect' => $loginUrl,
        'queryParam' => 'redirect',
        ]);
    $service->loadAuthenticator('Authentication.Session');
 
    $service->loadAuthenticator('Authentication.Form', [
        'fields' => $fields,
        'loginUrl' => $loginUrl,
    ]);

    $service->loadIdentifier('Authentication.Password', compact('fields'));

    $routes->registerMiddleware(
        'auth',
        new \Authentication\Middleware\AuthenticationMiddleware($service)
    );
    $routes->applyMiddleware('auth');

    //$routes->connect('/:controller');
    $routes->fallbacks(DashedRoute::class);
});

$routes->prefix('Admin', function (RouteBuilder $routes) {

    $loginUrl = Router::url('/admin/mgt-account/login');
    $fields   = [
        'username' => 'mail',
        'password' => 'password',
    ];
    $service = new AuthenticationService([
        'unauthenticatedRedirect' => $loginUrl,
        'queryParam' => 'redirect',
        ]);
    $service->loadAuthenticator('Authentication.Session');

    $service->loadAuthenticator('Authentication.Form', [
        'fields' => $fields,
        'loginUrl' => $loginUrl,
    ]);

    $service->loadIdentifier('Authentication.Password', compact('fields'));

    $routes->registerMiddleware(
        'auth',
        new \Authentication\Middleware\AuthenticationMiddleware($service)
    );
    $routes->applyMiddleware('auth');

    //$routes->connect('/:controller');
    $routes->fallbacks(DashedRoute::class);
});

<?php
// src/Application.php

declare(strict_types=1);

namespace App;

use Cake\Core\Configure;
use Cake\Core\Exception\MissingPluginException;
use Cake\Error\Middleware\ErrorHandlerMiddleware;
use Cake\Http\BaseApplication;
use Cake\Http\MiddlewareQueue;
use Cake\Routing\Middleware\AssetMiddleware;
use Cake\Routing\Middleware\RoutingMiddleware;

use Authentication\AuthenticationService;
use Authentication\AuthenticationServiceInterface;
use Authentication\AuthenticationServiceProviderInterface;
use Authentication\Middleware\AuthenticationMiddleware;
use Psr\Http\Message\ServerRequestInterface;

/**
 * Application setup class.
 *
 * This defines the bootstrapping logic and middleware layers you
 * want to use in your application.
 */
class Application extends BaseApplication
// I really want to comment out and delete this bottom.
implements AuthenticationServiceProviderInterface
{
    /**
     * Load all the application configuration and bootstrap logic.
     *
     * @return void
     */
    public function bootstrap(): void
    {
        // Call parent to load bootstrap from files.
        parent::bootstrap();

        if (PHP_SAPI === 'cli') {
            $this->bootstrapCli();
        }

        /*
         * Only try to load DebugKit in development mode
         * Debug Kit should not be installed on a production system
         */
        if (Configure::read('debug')) {
            $this->addPlugin('DebugKit');
        }

        // Load more plugins here
    }

    /**
     * Setup the middleware queue your application will use.
     *
     * @param \Cake\Http\MiddlewareQueue $middlewareQueue The middleware queue to setup.
     * @return \Cake\Http\MiddlewareQueue The updated middleware queue.
     */
    public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue
    {
        $middlewareQueue
            // Catch any exceptions in the lower layers,
            // and make an error page/response
            ->add(new ErrorHandlerMiddleware(Configure::read('Error')))

            // Handle plugin/theme assets like CakePHP normally does.
            ->add(new AssetMiddleware([
                'cacheTime' => Configure::read('Asset.cacheTime'),
            ]))

            // Add routing middleware.
            // If you have a large number of routes connected, turning on routes
            // caching in production could improve performance. For that when
            // creating the middleware instance specify the cache config name by
            // using it's second constructor argument:
            // `new RoutingMiddleware($this, '_cake_routes_')`
            ->add(new RoutingMiddleware($this))

            // I really want to comment out and delete this bottom.
            ->add(new AuthenticationMiddleware($this));

        return $middlewareQueue;
    }

    /**
     * Bootrapping for CLI application.
     *
     * That is when running commands.
     *
     * @return void
     */
    protected function bootstrapCli(): void
    {
        try {
            $this->addPlugin('Bake');
        } catch (MissingPluginException $e) {
            // Do not halt if the plugin is missing
        }

        $this->addPlugin('Migrations');

        // Load more plugins here
    }

    // I really want to comment out and delete this bottom.
    public function getAuthenticationService(ServerRequestInterface $request): AuthenticationServiceInterface
    {
        $authenticationService = new AuthenticationService([
            'unauthenticatedRedirect' => '/normal/users/login',
            'queryParam' => 'redirect',
        ]);

 
        $authenticationService->loadIdentifier('Authentication.Password', [
            'fields' => [
                'username' => 'mail',
                'password' => 'password',
            ]
        ]);


        $authenticationService->loadAuthenticator('Authentication.Session');

        $authenticationService->loadAuthenticator('Authentication.Form', [
            'fields' => [
                'username' => 'mail',
                'password' => 'password',
            ],
            'loginUrl' => '/normal/users/login',
        ]);

        return $authenticationService;
    }
}

As this is my first time using "stackoverflow", I'm not sure how to ask a good question. I'd appreciate it if you could help me out.

I would appreciate it if you could point it out to me.

thank you.

Edward Barnard
  • 346
  • 3
  • 17
IjIj
  • 23
  • 4
  • 1
    That's usually done not with two separate logins, but by having roles attached to the users and only allowing access to certain things based on the user's role. – Greg Schmidt Aug 06 '20 at 13:08
  • 1
    Thank you for contacting me. Sorry, CakePHP is my first book and I was wondering if you could tell me more about it. – IjIj Aug 06 '20 at 15:09
  • 1
    Did I refer you to this web page, or did I make a difference? [link](https://github.com/cakephp/authentication/issues/352) Does that mean there should be one table? What if I really want to use two tables? – IjIj Aug 06 '20 at 15:12
  • 1
    That page is about something else, I think. Not running two separate authentications, but running a standard CakePHP application in a subfolder of a site. – Greg Schmidt Aug 06 '20 at 15:52
  • 1
    If you really want to use two tables, I don't have any help for you. I can tell you that it's likely to complicate things quite a bit compared to the role-based method. It will be harder to find help for what you're doing, because it's uncommon. Your code may end up more fragile as a result of it all. If you ever need to change things about your authentication, there may be two places you need to make all those changes. Of course, you can go ahead with that path if you want. Just ask yourself if you really need to do it that way, or if you are trying to make a round peg fit a square hole. – Greg Schmidt Aug 06 '20 at 15:55
  • 1
    Thank you Greg Schmidt.I appreciate your contacting me. I've decided to switch to a single table with flags. I've also decided to create an operational-only application eventually. – IjIj Aug 07 '20 at 02:23

1 Answers1

0

I believe the answer is to load the correct middleware authenticator based on the current request (admin or regular user). But I have not multiple authentications yet so might not be correct. In Application.php method getAuthenticationService(), where you call loadIdentifier(), specify one "resolver" or the other depending on the current request URL (or however you distinguish an admin url).

The CakePHP 4.x documentation has a section on multiple authentication schemes. I believe you can use two different tables. Configuring Multiple Authentication Setups

This forum item may have the answer you need (the answer below the question): Multiple Authentication Setups

Edward Barnard
  • 346
  • 3
  • 17
  • 1
    Sorry for the delay in thanking you. I implemented it based on the link that Edward Barnard gave me yesterday, and I successfully used the Authentication plugin with two separate Tables. Thank you Edward Barnard, I really appreciate it. Thank you from the bottom of my heart. I'm Japanese and haven't mastered my English skills yet, so I apologize for the fuzziness of my words. It was very helpful! – IjIj Sep 07 '20 at 01:29
  • @IjIj Welcome to Stack Overflow. I'm glad that worked for you. I will be doing something similar in my own project. – Edward Barnard Sep 10 '20 at 13:20