Skip to content

TinyAuth Authentication

The fast and easy way for user authentication in CakePHP applications using INI-based configuration.

Overview

TinyAuth's Authentication component wraps the official CakePHP Authentication plugin and adds INI-based configuration for public actions, eliminating the need for code changes in every controller.

What TinyAuth Adds:

  • INI-based action whitelisting - Define public actions in auth_allow.ini instead of code
  • Zero controller modifications - No need to call allowUnauthenticated() in every controller
  • Plugin compatibility - You can still use all the core plugin functionality.

When to Use:

  • ✅ You want centralized authentication configuration
  • ✅ You need simple public action whitelisting
  • ✅ You want to avoid scattering auth logic across controllers

When NOT to Use:

  • ❌ You need to dynamically adjust public pages inside controllers
  • ❌ You prefer code-based configuration over INI files

Part 1: Official Plugin Setup

Before using TinyAuth features, you must set up the official CakePHP Authentication plugin.

Step 1: Install the Official Plugin

bash
composer require cakephp/authentication

📖 Official Documentation: book.cakephp.org/authentication/3

GitHub Repository: github.com/cakephp/authentication

Key Official Resources:

Step 2: Implement Authentication Interface

Your Application class must implement AuthenticationServiceProviderInterface:

php
// src/Application.php
use Authentication\AuthenticationServiceProviderInterface;
use Authentication\AuthenticationServiceInterface;
use Psr\Http\Message\ServerRequestInterface;

class Application extends BaseApplication implements AuthenticationServiceProviderInterface
{
    // ...
}

📖 See: Application Setup in official docs

Step 3: Configure Middleware

Add the authentication middleware to your Application class:

php
// src/Application.php
use Authentication\Middleware\AuthenticationMiddleware;

public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue
{
    // ... other middleware

    $middlewareQueue->add(new AuthenticationMiddleware($this));

    // ... other middleware

    return $middlewareQueue;
}

📖 See: Middleware Setup in official docs

Step 4: Configure Authentication Service

Implement getAuthenticationService() in your Application class to configure how users authenticate:

php
// src/Application.php
use Authentication\AuthenticationService;
use Authentication\AuthenticationServiceInterface;
use Authentication\Identifier\AbstractIdentifier;
use Cake\Routing\Router;
use Psr\Http\Message\ServerRequestInterface;

public function getAuthenticationService(ServerRequestInterface $request): AuthenticationServiceInterface
{
    $service = new AuthenticationService();

    // Define where users should be redirected to when they are not authenticated
    $service->setConfig([
        'unauthenticatedRedirect' => Router::url([
            'prefix' => false,
            'plugin' => false,
            'controller' => 'Users',
            'action' => 'login',
        ]),
        'queryParam' => 'redirect',
    ]);

    $fields = [
        AbstractIdentifier::CREDENTIAL_USERNAME => 'email',
        AbstractIdentifier::CREDENTIAL_PASSWORD => 'password',
    ];

    // Identifier configuration for password-based authentication
    $passwordIdentifier = [
        'Authentication.Password' => [
            'fields' => $fields,
            'resolver' => [
                'className' => 'Authentication.Orm',
                // Optional: Use custom finder for active users only
                // 'finder' => 'active',
            ],
        ],
    ];

    // Load the authenticators. Session should be first.
    $service->loadAuthenticator('Authentication.Session');

    $service->loadAuthenticator('Authentication.Form', [
        'identifier' => $passwordIdentifier,
        'fields' => $fields,
        'urlChecker' => 'Authentication.CakeRouter',
        'loginUrl' => Router::url([
            'prefix' => false,
            'plugin' => false,
            'controller' => 'Users',
            'action' => 'login',
        ]),
    ]);

    // Optional: Cookie authenticator for "remember me" functionality
    $service->loadAuthenticator('Authentication.Cookie', [
        'identifier' => $passwordIdentifier,
        'rememberMeField' => 'remember_me',
        'fields' => $fields,
        'urlChecker' => 'Authentication.CakeRouter',
        'loginUrl' => Router::url([
            'prefix' => false,
            'plugin' => false,
            'controller' => 'Users',
            'action' => 'login',
        ]),
    ]);

    return $service;
}

Note: Each authenticator has its own identifier configuration. The Session authenticator doesn't need one as it uses the stored identity.

📖 See: Authentication Service in official docs


Part 2: TinyAuth Features

Step 5: Load TinyAuth Component

Now load the TinyAuth Authentication component in your controller:

php
// src/Controller/AppController.php

public function initialize(): void
{
    parent::initialize();

    $this->loadComponent('TinyAuth.Authentication', [
        // TinyAuth-specific options (see Configuration section below)
    ]);
}

That's it for code changes! Now configure your public actions via INI file.

Step 6: Configure auth_allow.ini

TinyAuth expects an auth_allow.ini file in your config directory. Use it to specify what actions are not protected by authentication.

Basic Syntax

The section key syntax follows the CakePHP naming convention:

PluginName.MyPrefix/MyController

Create an entry for each action you want to expose:

  • one or more action names
  • the * wildcard to grant access to all actions of that controller
  • use "!actionName" (quotes are important) to deny certain actions

Example Configuration

ini
; ----------------------------------------------------------
; UsersController
; ----------------------------------------------------------
Users = index

; ----------------------------------------------------------
; UsersController using /api prefixed route
; ----------------------------------------------------------
Api/Users = index, view, edit

; ----------------------------------------------------------
; UsersController using /admin prefixed route
; ----------------------------------------------------------
Admin/Users = *

; ----------------------------------------------------------
; AccountsController in plugin named Accounts
; ----------------------------------------------------------
Accounts.Accounts = view, edit

; ----------------------------------------------------------
; AccountsController in plugin named Accounts using /my-admin
; prefixed route (assuming you are using recommended DashedRoute class)
; ----------------------------------------------------------
Accounts.MyAdmin/Accounts = index

Note: Prefixes are always CamelCased. The route inflects to the final casing if needed. Nested prefixes are joined using /, e.g. MyAdmin/Nested.

Deny Rules (Use with Caution)

Using only "granting" is recommended for security reasons. Careful with denying, as this can accidentally open up more than desired actions:

ini
Users = "!secret",*

Meaning: Grant public access to all "Users" controller actions by default, but keep authentication required for "secret" action.

Note: Denying always trumps granting, if both are declared for an action.

Multiple Files and Merging

You can specify multiple paths in your config, e.g. when you have plugins and separated the definitions across them. Make sure you are using each key only once, though. The first definition will be kept and all others for the same key are ignored.

Template with Defaults

See the config/ folder and the default template for popular plugins. You can copy out any default rules you want to use in your project.

Quick Setups

TinyAuth offers a few quick setup options for common scenarios:

Allow Non-Prefixed Actions

If you have non-prefixed controllers that you want to make public and keep prefixed ones protected:

php
$this->loadComponent('TinyAuth.Authentication', [
    'allowNonPrefixed' => true,
]);

Any non-prefixed action will now be public by default. You can always set up "deny" rules for any action to protect a specific one from public access.

Prefix-Based Allow

If you want to allow certain prefixes:

php
$this->loadComponent('TinyAuth.Authentication', [
    'allowPrefixes' => [
        'MyPrefix',
        'Nested/Prefix',
    ],
]);

Note: Prefixes are always CamelCased (even if routing makes them to dashed-ones in the URL).

Careful: Nested prefixes currently also match (and inherit) by parent. So if MyPrefix is allowed, MyPrefix/Sub and other nested ones would also automatically be allowed. You would need to explicitly set up deny rules here if needed.

Mixing with Code

It is possible to have mixed INI and code rules. Those will get merged prior to authentication. So in case any of your controllers (or plugin controllers) contain such a statement, it will merge itself with your INI whitelist:

php
// In your controller
use Cake\Event\EventInterface;

public function beforeFilter(EventInterface $event): void
{
    parent::beforeFilter($event);

    $this->Authentication->allowUnauthenticated(['index', 'view']);
}

This can be useful when migrating slowly to TinyAuth. Once you move such a code based rule into the INI file, you can safely remove those lines of code in your controller.

allow() vs deny()

Since 1.11.0 you can also mix it with deny() calls. From how the Authentication component works, all allow() calls need be done before calling deny(). As such TinyAuth injects its list now before Controller::beforeFilter() gets called.

Note: It is advised to move away from these controller calls when possible.

Caching

TinyAuth makes heavy use of caching to achieve optimal performance. By default, it will not use caching in debug mode.

To modify the caching behavior set the autoClearCache configuration option:

php
$this->loadComponent('TinyAuth.Authentication', [
    'autoClearCache' => true|false,
]);

Authentication Helper

The Authentication helper provides template-level methods for checking if URLs are public.

Setup

Load the helper in your AppView:

php
// src/View/AppView.php

public function initialize(): void
{
    parent::initialize();

    $this->loadHelper('TinyAuth.Authentication');
}

Available Methods

isPublic(array $url = [])

Check if a given URL is public (allowed in auth_allow.ini):

php
// Check if current page is public
<?php if ($this->Authentication->isPublic()): ?>
    <p>This page is public</p>
<?php endif; ?>

// Check if a specific URL is public
<?php if ($this->Authentication->isPublic(['controller' => 'Users', 'action' => 'register'])): ?>
    <?= $this->Html->link('Register', ['controller' => 'Users', 'action' => 'register']); ?>
<?php endif; ?>

Named Routes Support

The helper also supports named routes:

php
<?php if ($this->Authentication->isPublic(['_name' => 'users:register'])): ?>
    <p>Registration is public</p>
<?php endif; ?>

Example Usage

Conditional Navigation

php
<nav>
    <ul>
        <?php if ($this->Authentication->isPublic(['action' => 'index'])): ?>
            <li><?= $this->Html->link('Home', ['action' => 'index']); ?></li>
        <?php endif; ?>

        <?php if ($this->Authentication->isPublic(['controller' => 'Articles', 'action' => 'view'])): ?>
            <li><?= $this->Html->link('Articles', ['controller' => 'Articles']); ?></li>
        <?php endif; ?>
    </ul>
</nav>
php
<?php if ($this->Authentication->isPublic() && !$this->AuthUser->id()): ?>
    <div class="login-prompt">
        <?= $this->Html->link('Login', ['controller' => 'Users', 'action' => 'login']); ?>
    </div>
<?php endif; ?>

Configuration

TinyAuth Authentication component supports the following configuration options:

OptionTypeDescription
autoClearCacheboolTrue will generate a new ACL cache file every time.
allowFilePathstringFull path to the INI file. Can also be an array of paths.
Defaults to ROOT . DS . 'config' . DS.
allowFilestringName of the INI file. Defaults to auth_allow.ini.
allowAdapterstringClass name, defaults to IniAllowAdapter::class.

Advanced Topics

TinyAuth-Specific Enhancements

TinyAuth provides optional enhanced components you can use instead of the official ones:

Enhanced Session Authenticator

TinyAuth.PrimaryKeySession (extends Authentication.Session):

  • Stores only the user ID in session (not full user data)
  • Fetches fresh user data from database on each request
  • Keeps user data always up to date without re-login

Usage in your getAuthenticationService() method:

php
// Use TinyAuth's enhanced PrimaryKeySession authenticator
// This stores only user ID in session and fetches fresh data from DB
$service->loadAuthenticator('TinyAuth.PrimaryKeySession', [
    'identifier' => [
        'Authentication.Token' => [
            'tokenField' => 'id',
            'dataField' => 'key',
            'resolver' => [
                'className' => 'Authentication.Orm',
                'finder' => 'active',
            ],
        ],
    ],
    'urlChecker' => 'Authentication.CakeRouter',
]);

📖 See: Session Authenticator in official docs for base functionality

Session Caching with PrimaryKeySession

When using PrimaryKeySession authenticator (which fetches user data from DB on each request), consider adding caching:

php
// After user updates their profile data
use TinyAuth\Utility\SessionCache;

SessionCache::delete($userId);

This invalidates the cache so fresh data is fetched on the next request.

Enhanced Redirect Handlers

TinyAuth provides redirect handlers with flash message support:

TinyAuth.ForbiddenCakeRedirect - Works with CakePHP routing:

php
$service->setConfig([
    'unauthenticatedRedirect' => ['controller' => 'Users', 'action' => 'login'],
    'unauthorizedHandler' => [
        'className' => 'TinyAuth.ForbiddenCakeRedirect',
        'unauthorizedMessage' => 'Please login to continue.',
    ],
]);

TinyAuth.ForbiddenRedirect - Uses direct URLs:

php
$service->setConfig([
    'unauthenticatedRedirect' => '/users/login',
    'unauthorizedHandler' => [
        'className' => 'TinyAuth.ForbiddenRedirect',
        'unauthorizedMessage' => 'Access denied.',
    ],
]);

Features:

  • Automatically sets flash error messages
  • Skips JSON/XML requests (throws exception instead)
  • Preserves target URL in query parameter for post-login redirect

You can use the official handlers instead if you prefer.

For one-click token login (e.g., email verification, password reset), you can add the LoginLink authenticator from the dereuromark/cakephp-tools plugin:

php
// Add to your getAuthenticationService() method after other authenticators

// This is a one-click token login as optional addition
$service->loadAuthenticator('Tools.LoginLink', [
    'identifier' => [
        'Tools.LoginLink' => [
            'resolver' => [
                'className' => 'Authentication.Orm',
                'finder' => 'active',
            ],
            'preCallback' => function (int $id) {
                // Optional: Perform action on successful login (e.g., confirm email)
                TableRegistry::getTableLocator()->get('Users')->confirmEmail($id);
            },
        ],
    ],
    'urlChecker' => 'Authentication.CakeRouter',
    'loginUrl' => Router::url([
        'prefix' => false,
        'plugin' => false,
        'controller' => 'Users',
        'action' => 'login',
    ]),
]);

Note: Requires composer require dereuromark/cakephp-tools

📖 See: Tools Plugin LoginLink

Accessing User Identity

Get the authenticated user from the AuthUser component or helper:

php
// In controller
$user = $this->AuthUser->identity();
$userId = $this->AuthUser->id();

// In template
$user = $this->AuthUser->identity();

Additional Resources

Released under the MIT License.