From 4f0b6578b751abf154dd8af6c821a11ff30d4742 Mon Sep 17 00:00:00 2001 From: Dimitri Sitchet Tomkeu Date: Mon, 20 Apr 2026 20:16:25 +0100 Subject: [PATCH 1/7] feat: add `prefix` option for authentication routes --- src/Auth.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Auth.php b/src/Auth.php index fedee8a97..560994505 100644 --- a/src/Auth.php +++ b/src/Auth.php @@ -131,14 +131,16 @@ public function authenticate(array $credentials): Result * Usage (in Config/Routes.php): * - auth()->routes($routes); * - auth()->routes($routes, ['except' => ['login', 'register']]) + * - auth()->routes($routes, ['prefix' => 'auth']) */ public function routes(RouteCollection &$routes, array $config = []): void { $authRoutes = config('AuthRoutes')->routes; $namespace = $config['namespace'] ?? 'CodeIgniter\Shield\Controllers'; + $prefix = $config['prefix'] ?? '/'; - $routes->group('/', ['namespace' => $namespace], static function (RouteCollection $routes) use ($authRoutes, $config): void { + $routes->group($prefix, ['namespace' => $namespace], static function (RouteCollection $routes) use ($authRoutes, $config): void { foreach ($authRoutes as $name => $row) { if (! isset($config['except']) || ! in_array($name, $config['except'], true)) { foreach ($row as $params) { From 9efef88761b04f68da49d4e9a891df9f9e4b2c46 Mon Sep 17 00:00:00 2001 From: Dimitri Sitchet Tomkeu Date: Mon, 20 Apr 2026 20:17:12 +0100 Subject: [PATCH 2/7] test: test for `prefix` option in authentication routes --- tests/Unit/AuthRoutesTest.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/Unit/AuthRoutesTest.php b/tests/Unit/AuthRoutesTest.php index d4e568121..271164dd7 100644 --- a/tests/Unit/AuthRoutesTest.php +++ b/tests/Unit/AuthRoutesTest.php @@ -80,4 +80,24 @@ public function testRoutesCustomNamespace(): void $this->assertSame('\Auth\RegisterController::registerView', $routes['register']); } + + public function testRoutesCustomPrefix(): void + { + $collection = single_service('routes'); + $auth = service('auth'); + + $auth->routes($collection, ['prefix' => 'auth']); + + if (version_compare(CodeIgniter::CI_VERSION, '4.5') >= 0) { + $routes = $collection->getRoutes('GET'); + } else { + $routes = $collection->getRoutes('get'); + } + + $this->assertArrayHasKey('auth/register', $routes); + $this->assertArrayHasKey('auth/login', $routes); + $this->assertArrayHasKey('auth/login/magic-link', $routes); + $this->assertArrayHasKey('auth/logout', $routes); + $this->assertArrayHasKey('auth/auth/a/show', $routes); + } } From ded8c193a4537d1e1f6a86f57c9399a2ef664b6f Mon Sep 17 00:00:00 2001 From: Dimitri Sitchet Tomkeu Date: Mon, 20 Apr 2026 20:17:55 +0100 Subject: [PATCH 3/7] docs: add docs for `prefix` option in authentication routes --- docs/customization/route_config.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/customization/route_config.md b/docs/customization/route_config.md index 7b06c7d70..552c8732d 100644 --- a/docs/customization/route_config.md +++ b/docs/customization/route_config.md @@ -29,6 +29,16 @@ service('auth')->routes($routes, ['namespace' => '\App\Controllers\Auth']); This will generate the routes with the specified namespace instead of the default Shield namespace. This can be combined with any other options, like `except`. +## Change Prefix + +If you wish, you can prefix all defined authentication routes using the `prefix` option. This is particularly useful if you want all your routes to be under the same root (for example, `auth/login`, `auth/register`). + +```php +service('auth')->routes($routes, ['prefix' => 'auth']); +``` + +This generates routes whose paths are all prefixed with `auth`. + ## Use Locale Routes You can use the `{locale}` placeholder in your routes From f52ab9d90ca7b2adc15471757fda5941d9b8080b Mon Sep 17 00:00:00 2001 From: Dimitri Sitchet Tomkeu Date: Tue, 21 Apr 2026 20:29:26 +0100 Subject: [PATCH 4/7] patch: change `prefix` option to `group` in routes configuration --- docs/customization/route_config.md | 4 ++-- src/Auth.php | 6 +++--- tests/Unit/AuthRoutesTest.php | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/customization/route_config.md b/docs/customization/route_config.md index 552c8732d..8596e41b6 100644 --- a/docs/customization/route_config.md +++ b/docs/customization/route_config.md @@ -31,10 +31,10 @@ This will generate the routes with the specified namespace instead of the defaul ## Change Prefix -If you wish, you can prefix all defined authentication routes using the `prefix` option. This is particularly useful if you want all your routes to be under the same root (for example, `auth/login`, `auth/register`). +If you wish, you can prefix all defined authentication routes using the `group` option. This is particularly useful if you want all your routes to be under the same root (for example, `auth/login`, `auth/register`). ```php -service('auth')->routes($routes, ['prefix' => 'auth']); +service('auth')->routes($routes, ['group' => 'auth']); ``` This generates routes whose paths are all prefixed with `auth`. diff --git a/src/Auth.php b/src/Auth.php index 560994505..d56d287b8 100644 --- a/src/Auth.php +++ b/src/Auth.php @@ -131,16 +131,16 @@ public function authenticate(array $credentials): Result * Usage (in Config/Routes.php): * - auth()->routes($routes); * - auth()->routes($routes, ['except' => ['login', 'register']]) - * - auth()->routes($routes, ['prefix' => 'auth']) + * - auth()->routes($routes, ['group' => 'auth']) */ public function routes(RouteCollection &$routes, array $config = []): void { $authRoutes = config('AuthRoutes')->routes; $namespace = $config['namespace'] ?? 'CodeIgniter\Shield\Controllers'; - $prefix = $config['prefix'] ?? '/'; + $group = $config['group'] ?? '/'; - $routes->group($prefix, ['namespace' => $namespace], static function (RouteCollection $routes) use ($authRoutes, $config): void { + $routes->group($group, ['namespace' => $namespace], static function (RouteCollection $routes) use ($authRoutes, $config): void { foreach ($authRoutes as $name => $row) { if (! isset($config['except']) || ! in_array($name, $config['except'], true)) { foreach ($row as $params) { diff --git a/tests/Unit/AuthRoutesTest.php b/tests/Unit/AuthRoutesTest.php index 271164dd7..b8b5747f6 100644 --- a/tests/Unit/AuthRoutesTest.php +++ b/tests/Unit/AuthRoutesTest.php @@ -86,7 +86,7 @@ public function testRoutesCustomPrefix(): void $collection = single_service('routes'); $auth = service('auth'); - $auth->routes($collection, ['prefix' => 'auth']); + $auth->routes($collection, ['group' => 'auth']); if (version_compare(CodeIgniter::CI_VERSION, '4.5') >= 0) { $routes = $collection->getRoutes('GET'); From 892081756075376850a3ff4296b8c8f8206989b6 Mon Sep 17 00:00:00 2001 From: John Paul E Balandan Date: Wed, 22 Apr 2026 00:31:36 +0800 Subject: [PATCH 5/7] chore: fix code styles (#1324) --- src/Config/Auth.php | 4 ++-- tests/Authentication/MagicLinkTest.php | 9 +++------ tests/Language/AbstractTranslationTestCase.php | 3 +-- tests/Unit/UserIdentityModelTest.php | 1 - 4 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/Config/Auth.php b/src/Config/Auth.php index b4f007b2c..c6c27bf1e 100644 --- a/src/Config/Auth.php +++ b/src/Config/Auth.php @@ -29,7 +29,7 @@ class Auth extends BaseConfig { - /** + /* * //////////////////////////////////////////////////////////////////// * AUTHENTICATION * //////////////////////////////////////////////////////////////////// @@ -378,7 +378,7 @@ class Auth extends BaseConfig */ public int $hashCost = 12; - /** + /* * //////////////////////////////////////////////////////////////////// * OTHER SETTINGS * //////////////////////////////////////////////////////////////////// diff --git a/tests/Authentication/MagicLinkTest.php b/tests/Authentication/MagicLinkTest.php index 19e5c52f8..f421a03fd 100644 --- a/tests/Authentication/MagicLinkTest.php +++ b/tests/Authentication/MagicLinkTest.php @@ -75,9 +75,7 @@ public function testMagicLinkSubmitBadEmail(): void public function testMagicLinkSubmitSuccess(): void { - /** - * @phpstan-var User - */ + /** @phpstan-var User $user */ $user = fake(UserModel::class); $user->createEmailIdentity(['email' => 'foo@example.com', 'password' => 'secret123']); @@ -105,9 +103,8 @@ public function testMagicLinkVerifyNoToken(): void public function testMagicLinkVerifyExpired(): void { $identities = new UserIdentityModel(); - /** - * @phpstan-var User - */ + + /** @phpstan-var User $user */ $user = fake(UserModel::class); $user->createEmailIdentity(['email' => 'foo@example.com', 'password' => 'secret123']); $identities->insert([ diff --git a/tests/Language/AbstractTranslationTestCase.php b/tests/Language/AbstractTranslationTestCase.php index 6085443e9..38ae4138a 100644 --- a/tests/Language/AbstractTranslationTestCase.php +++ b/tests/Language/AbstractTranslationTestCase.php @@ -220,8 +220,7 @@ final public function testAllIncludedLanguageKeysAreTranslated(string $locale): { // These keys are usually not translated because they contain either // universal abbreviations or simply combine parameters with signs. - static $excludedKeyTranslations = [ - ]; + static $excludedKeyTranslations = []; $excludedKeys = array_unique(array_merge($excludedKeyTranslations, $this->excludedLocaleKeyTranslations)); $availableSets = array_intersect($this->expectedSets(), $this->foundSets($locale)); diff --git a/tests/Unit/UserIdentityModelTest.php b/tests/Unit/UserIdentityModelTest.php index 00133a98a..e552a3b19 100644 --- a/tests/Unit/UserIdentityModelTest.php +++ b/tests/Unit/UserIdentityModelTest.php @@ -85,7 +85,6 @@ public function testCreateCodeIdentityThrowsExceptionIfUniqueCodeIsNotGot(): voi public function testForceMultiplePasswordReset(): void { - /** @var Fabricator $fabricator */ $fabricator = new Fabricator(UserIdentityModel::class); $fabricator->create(10); From 08cd364d93a17a56fdd526fc7ded056210848a2b Mon Sep 17 00:00:00 2001 From: John Paul E Balandan Date: Wed, 22 Apr 2026 00:31:58 +0800 Subject: [PATCH 6/7] chore: fix phpstan (#1325) --- phpstan-baseline.php | 12 ------------ src/Authentication/Authenticators/JWT.php | 4 +--- src/Authentication/Authenticators/Session.php | 2 +- 3 files changed, 2 insertions(+), 16 deletions(-) diff --git a/phpstan-baseline.php b/phpstan-baseline.php index 8612560f9..53ff869cc 100644 --- a/phpstan-baseline.php +++ b/phpstan-baseline.php @@ -427,18 +427,6 @@ 'count' => 1, 'path' => __DIR__ . '/tests/Authentication/Authenticators/SessionAuthenticatorTest.php', ]; -$ignoreErrors[] = [ - 'rawMessage' => 'Parameter #1 $headers of method Tests\\Authentication\\Filters\\AbstractFilterTestCase::withHeaders() expects array>, array{Authorization: non-falsy-string} given.', - 'identifier' => 'argument.type', - 'count' => 7, - 'path' => __DIR__ . '/tests/Authentication/Filters/HmacFilterTest.php', -]; -$ignoreErrors[] = [ - 'rawMessage' => 'Parameter #1 $headers of method Tests\\Authentication\\Filters\\JWTFilterTest::withHeaders() expects array>, array{Authorization: non-falsy-string} given.', - 'identifier' => 'argument.type', - 'count' => 1, - 'path' => __DIR__ . '/tests/Authentication/Filters/JWTFilterTest.php', -]; $ignoreErrors[] = [ 'rawMessage' => 'Implicit array creation is not allowed - variable $users might not exist.', 'identifier' => 'variable.implicitArray', diff --git a/src/Authentication/Authenticators/JWT.php b/src/Authentication/Authenticators/JWT.php index 61deddb20..2a4c2e721 100644 --- a/src/Authentication/Authenticators/JWT.php +++ b/src/Authentication/Authenticators/JWT.php @@ -221,9 +221,7 @@ public function getTokenFromRequest(RequestInterface $request): string /** @var AuthJWT $config */ $config = config('AuthJWT'); - $tokenHeader = $request->getHeaderLine( - $config->authenticatorHeader ?? 'Authorization', - ); + $tokenHeader = $request->getHeaderLine($config->authenticatorHeader); if (str_starts_with($tokenHeader, 'Bearer')) { return trim(substr($tokenHeader, 6)); diff --git a/src/Authentication/Authenticators/Session.php b/src/Authentication/Authenticators/Session.php index 6897854b7..2b800c2a5 100644 --- a/src/Authentication/Authenticators/Session.php +++ b/src/Authentication/Authenticators/Session.php @@ -280,7 +280,7 @@ private function recordLoginAttempt( // Determine the type of ID we're using. // Standard fields would be email, username, // but any column within config('Auth')->validFields can be used. - $field = array_intersect(config('Auth')->validFields ?? [], array_keys($credentials)); + $field = array_intersect(config('Auth')->validFields, array_keys($credentials)); if (count($field) !== 1) { throw new InvalidArgumentException('Invalid credentials passed to recordLoginAttempt.'); From abd414d7c8ade140775d8626a1048c2841b3c4c9 Mon Sep 17 00:00:00 2001 From: John Paul E Balandan Date: Wed, 22 Apr 2026 00:32:21 +0800 Subject: [PATCH 7/7] chore: remove deprecated rector class (#1326) --- rector.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/rector.php b/rector.php index d596c8712..d2d9a6e6a 100644 --- a/rector.php +++ b/rector.php @@ -45,7 +45,6 @@ use Rector\EarlyReturn\Rector\Return_\PreparedValueToEarlyReturnRector; use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector; use Rector\Php73\Rector\FuncCall\StringifyStrNeedlesRector; -use Rector\Php81\Rector\ClassMethod\NewInInitializerRector; use Rector\PHPUnit\AnnotationsToAttributes\Rector\Class_\AnnotationWithValueToAttributeRector; use Rector\PHPUnit\CodeQuality\Rector\Class_\YieldDataProviderRector; use Rector\PHPUnit\CodeQuality\Rector\MethodCall\AssertEmptyNullableObjectToAssertInstanceofRector; @@ -138,9 +137,6 @@ // Ignore some PHPUnit rules AssertEmptyNullableObjectToAssertInstanceofRector::class, - // Ignore to prevent BC break - NewInInitializerRector::class, - // Ignore for readability RemoveNullArgOnNullDefaultParamRector::class, ]);