| Code Coverage
| Lines | Functions and Methods | Classes and Traits
Total | 50.00% covered (danger) | 50.00% | 8 / 16 | 42.86% covered (danger) | 42.86% | 3 / 7 | CRAP | 0.00% covered (danger) | 0.00% | 0 / 1
Auth | 50.00% covered (danger) | 50.00% | 8 / 16 | 42.86% covered (danger) | 42.86% | 3 / 7 | 16.00 | 0.00% covered (danger) | 0.00% | 0 / 1
loginRedirect | 100.00% covered (success) | 100.00% | 3 / 3 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
logoutRedirect | 100.00% covered (success) | 100.00% | 2 / 2 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
registerRedirect | 0.00% covered (danger) | 0.00% | 0 / 2 | 0.00% covered (danger) | 0.00% | 0 / 1 | 2 |
forcePasswordResetRedirect | 0.00% covered (danger) | 0.00% | 0 / 2 | 0.00% covered (danger) | 0.00% | 0 / 1 | 2 |
permissionDeniedRedirect | 0.00% covered (danger) | 0.00% | 0 / 2 | 0.00% covered (danger) | 0.00% | 0 / 1 | 2 |
groupDeniedRedirect | 0.00% covered (danger) | 0.00% | 0 / 2 | 0.00% covered (danger) | 0.00% | 0 / 1 | 2 |
getUrl | 100.00% covered (success) | 100.00% | 3 / 3 | 100.00% covered (success) | 100.00% | 1 / 1 | 2 |

1 | <?php
2 |
3 | declare(strict_types=1);
4 |
5 | /**
6 | * This file is part of CodeIgniter Shield.
7 | *
8 | * (c) CodeIgniter Foundation <admin@codeigniter.com>
9 | *
10 | * For the full copyright and license information, please view
11 | * the LICENSE file that was distributed with this source code.
12 | */
13 |
14 | namespace Config;
15 |
16 | use CodeIgniter\Shield\Config\Auth as ShieldAuth;
17 | use CodeIgniter\Shield\Authentication\Actions\ActionInterface;
18 | use CodeIgniter\Shield\Authentication\AuthenticatorInterface;
19 | use CodeIgniter\Shield\Authentication\Authenticators\AccessTokens;
20 | use CodeIgniter\Shield\Authentication\Authenticators\HmacSha256;
21 | use CodeIgniter\Shield\Authentication\Authenticators\JWT;
22 | use CodeIgniter\Shield\Authentication\Authenticators\Session;
23 | use CodeIgniter\Shield\Authentication\Passwords\CompositionValidator;
24 | use CodeIgniter\Shield\Authentication\Passwords\DictionaryValidator;
25 | use CodeIgniter\Shield\Authentication\Passwords\NothingPersonalValidator;
26 | use CodeIgniter\Shield\Authentication\Passwords\PwnedValidator;
27 | use CodeIgniter\Shield\Authentication\Passwords\ValidatorInterface;
28 | use CodeIgniter\Shield\Models\UserModel;
29 |
30 | class Auth extends ShieldAuth
31 | {
32 | /**
33 | * ////////////////////////////////////////////////////////////////////
34 | * AUTHENTICATION
35 | * ////////////////////////////////////////////////////////////////////
36 | */
37 |
38 | // Constants for Record Login Attempts. Do not change.
39 | public const RECORD_LOGIN_ATTEMPT_NONE = 0; // Do not record at all
40 | public const RECORD_LOGIN_ATTEMPT_FAILURE = 1; // Record only failures
41 | public const RECORD_LOGIN_ATTEMPT_ALL = 2; // Record all login attempts
42 |
43 | /**
44 | * --------------------------------------------------------------------
45 | * View files
46 | * --------------------------------------------------------------------
47 | */
48 | public array $views = [
49 | 'login' => 'auth/login',
50 | 'register' => '\CodeIgniter\Shield\Views\register',
51 | 'layout' => 'layouts/auth',
52 | 'action_email_2fa' => '\CodeIgniter\Shield\Views\email_2fa_show',
53 | 'action_email_2fa_verify' => '\CodeIgniter\Shield\Views\email_2fa_verify',
54 | 'action_email_2fa_email' => '\CodeIgniter\Shield\Views\Email\email_2fa_email',
55 | 'action_email_activate_show' => '\CodeIgniter\Shield\Views\email_activate_show',
56 | 'action_email_activate_email' => '\CodeIgniter\Shield\Views\Email\email_activate_email',
57 | 'magic-link-login' => '\CodeIgniter\Shield\Views\magic_link_form',
58 | 'magic-link-message' => '\CodeIgniter\Shield\Views\magic_link_message',
59 | 'magic-link-email' => '\CodeIgniter\Shield\Views\Email\magic_link_email',
60 | ];
61 |
62 | /**
63 | * --------------------------------------------------------------------
64 | * Redirect URLs
65 | * --------------------------------------------------------------------
66 | * The default URL that a user will be redirected to after various auth
67 | * actions. This can be either of the following:
68 | *
69 | * 1. An absolute URL. E.g. http://example.com OR https://example.com
70 | * 2. A named route that can be accessed using `route_to()` or `url_to()`
71 | * 3. A URI path within the application. e.g 'admin', 'login', 'expath'
72 | *
73 | * If you need more flexibility you can override the `getUrl()` method
74 | * to apply any logic you may need.
75 | */
76 | public array $redirects = [
77 | 'register' => 'dashboard',
78 | 'login' => 'dashboard',
79 | 'logout' => 'login',
80 | 'force_reset' => 'dashboard',
81 | 'permission_denied' => 'dashboard',
82 | 'group_denied' => 'dashboard',
83 | ];
84 |
85 | /**
86 | * --------------------------------------------------------------------
87 | * Authentication Actions
88 | * --------------------------------------------------------------------
89 | * Specifies the class that represents an action to take after
90 | * the user logs in or registers a new account at the site.
91 | *
92 | * You must register actions in the order of the actions to be performed.
93 | *
94 | * Available actions with Shield:
95 | * - register: \CodeIgniter\Shield\Authentication\Actions\EmailActivator::class
96 | * - login: \CodeIgniter\Shield\Authentication\Actions\Email2FA::class
97 | *
98 | * Custom Actions and Requirements:
99 | *
100 | * - All actions must implement \CodeIgniter\Shield\Authentication\Actions\ActionInterface.
101 | * - Custom actions for "register" must have a class name that ends with the suffix "Activator" (e.g., `CustomSmsActivator`) ensure proper functionality.
102 | *
103 | * @var array<string, class-string<ActionInterface>|null>
104 | */
105 | public array $actions = [
106 | 'register' => null,
107 | 'login' => null,
108 | ];
109 |
110 | /**
111 | * --------------------------------------------------------------------
112 | * Authenticators
113 | * --------------------------------------------------------------------
114 | * The available authentication systems, listed
115 | * with alias and class name. These can be referenced
116 | * by alias in the auth helper:
117 | * auth('tokens')->attempt($credentials);
118 | *
119 | * @var array<string, class-string<AuthenticatorInterface>>
120 | */
121 | public array $authenticators = [
122 | 'tokens' => AccessTokens::class,
123 | 'session' => Session::class,
124 | 'hmac' => HmacSha256::class,
125 | // 'jwt' => JWT::class,
126 | ];
127 |
128 | /**
129 | * --------------------------------------------------------------------
130 | * Default Authenticator
131 | * --------------------------------------------------------------------
132 | * The Authenticator to use when none is specified.
133 | * Uses the $key from the $authenticators array above.
134 | */
135 | public string $defaultAuthenticator = 'session';
136 |
137 | /**
138 | * --------------------------------------------------------------------
139 | * Authentication Chain
140 | * --------------------------------------------------------------------
141 | * The Authenticators to test logged in status against
142 | * when using the 'chain' filter. Each Authenticator listed will be checked.
143 | * If no match is found, then the next in the chain will be checked.
144 | *
145 | * @var list<string>
146 | */
147 | public array $authenticationChain = [
148 | 'session',
149 | 'tokens',
150 | 'hmac',
151 | // 'jwt',
152 | ];
153 |
154 | /**
155 | * --------------------------------------------------------------------
156 | * Allow Registration
157 | * --------------------------------------------------------------------
158 | * Determines whether users can register for the site.
159 | */
160 | public bool $allowRegistration = false;
161 |
162 | /**
163 | * --------------------------------------------------------------------
164 | * Record Last Active Date
165 | * --------------------------------------------------------------------
166 | * If true, will always update the `last_active` datetime for the
167 | * logged-in user on every page request.
168 | * This feature only works when session/tokens/hmac/chain/jwt filter is active.
169 | *
170 | * @see https://codeigniter4.github.io/shield/quick_start_guide/using_session_auth/#protecting-pages for set filters.
171 | */
172 | public bool $recordActiveDate = true;
173 |
174 | /**
175 | * --------------------------------------------------------------------
176 | * Allow Magic Link Logins
177 | * --------------------------------------------------------------------
178 | * If true, will allow the use of "magic links" sent via the email
179 | * as a way to log a user in without the need for a password.
180 | * By default, this is used in place of a password reset flow, but
181 | * could be modified as the only method of login once an account
182 | * has been set up.
183 | */
184 | public bool $allowMagicLinkLogins = false;
185 |
186 | /**
187 | * --------------------------------------------------------------------
188 | * Magic Link Lifetime
189 | * --------------------------------------------------------------------
190 | * Specifies the amount of time, in seconds, that a magic link is valid.
191 | * You can use Time Constants or any desired number.
192 | */
193 | public int $magicLinkLifetime = HOUR;
194 |
195 | /**
196 | * --------------------------------------------------------------------
197 | * Session Authenticator Configuration
198 | * --------------------------------------------------------------------
199 | * These settings only apply if you are using the Session Authenticator
200 | * for authentication.
201 | *
202 | * - field The name of the key the current user info is stored in session
203 | * - allowRemembering Does the system allow use of "remember-me"
204 | * - rememberCookieName The name of the cookie to use for "remember-me"
205 | * - rememberLength The length of time, in seconds, to remember a user.
206 | *
207 | * @var array<string, bool|int|string>
208 | */
209 | public array $sessionConfig = [
210 | 'field' => 'user',
211 | 'allowRemembering' => true,
212 | 'rememberCookieName' => 'remember',
213 | 'rememberLength' => 30 * DAY,
214 | ];
215 |
216 | /**
217 | * --------------------------------------------------------------------
218 | * The validation rules for username
219 | * --------------------------------------------------------------------
220 | *
221 | * Do not use string rules like `required|valid_email`.
222 | *
223 | * @var array<string, array<int, string>|string>
224 | */
225 | public array $usernameValidationRules = [
226 | 'label' => 'Auth.username',
227 | 'rules' => [
228 | 'required',
229 | 'max_length[30]',
230 | 'min_length[3]',
231 | 'regex_match[/\A[a-zA-Z0-9\.]+\z/]',
232 | ],
233 | ];
234 |
235 | /**
236 | * --------------------------------------------------------------------
237 | * The validation rules for email
238 | * --------------------------------------------------------------------
239 | *
240 | * Do not use string rules like `required|valid_email`.
241 | *
242 | * @var array<string, array<int, string>|string>
243 | */
244 | public array $emailValidationRules = [
245 | 'label' => 'Auth.email',
246 | 'rules' => [
247 | 'required',
248 | 'max_length[254]',
249 | 'valid_email',
250 | ],
251 | ];
252 |
253 | /**
254 | * --------------------------------------------------------------------
255 | * Minimum Password Length
256 | * --------------------------------------------------------------------
257 | * The minimum length that a password must be to be accepted.
258 | * Recommended minimum value by NIST = 8 characters.
259 | */
260 | public int $minimumPasswordLength = 8;
261 |
262 | /**
263 | * --------------------------------------------------------------------
264 | * Password Check Helpers
265 | * --------------------------------------------------------------------
266 | * The PasswordValidator class runs the password through all of these
267 | * classes, each getting the opportunity to pass/fail the password.
268 | * You can add custom classes as long as they adhere to the
269 | * CodeIgniter\Shield\Authentication\Passwords\ValidatorInterface.
270 | *
271 | * @var list<class-string<ValidatorInterface>>
272 | */
273 | public array $passwordValidators = [
274 | CompositionValidator::class,
275 | NothingPersonalValidator::class,
276 | DictionaryValidator::class,
277 | // PwnedValidator::class,
278 | ];
279 |
280 | /**
281 | * --------------------------------------------------------------------
282 | * Valid login fields
283 | * --------------------------------------------------------------------
284 | * Fields that are available to be used as credentials for login.
285 | */
286 | public array $validFields = [
287 | 'email',
288 | // 'username',
289 | ];
290 |
291 | /**
292 | * --------------------------------------------------------------------
293 | * Additional Fields for "Nothing Personal"
294 | * --------------------------------------------------------------------
295 | * The NothingPersonalValidator prevents personal information from
296 | * being used in passwords. The email and username fields are always
297 | * considered by the validator. Do not enter those field names here.
298 | *
299 | * An extended User Entity might include other personal info such as
300 | * first and/or last names. $personalFields is where you can add
301 | * fields to be considered as "personal" by the NothingPersonalValidator.
302 | * For example:
303 | * $personalFields = ['firstname', 'lastname'];
304 | */
305 | public array $personalFields = [];
306 |
307 | /**
308 | * --------------------------------------------------------------------
309 | * Password / Username Similarity
310 | * --------------------------------------------------------------------
311 | * Among other things, the NothingPersonalValidator checks the
312 | * amount of sameness between the password and username.
313 | * Passwords that are too much like the username are invalid.
314 | *
315 | * The value set for $maxSimilarity represents the maximum percentage
316 | * of similarity at which the password will be accepted. In other words, any
317 | * calculated similarity equal to, or greater than $maxSimilarity
318 | * is rejected.
319 | *
320 | * The accepted range is 0-100, with 0 (zero) meaning don't check similarity.
321 | * Using values at either extreme of the *working range* (1-100) is
322 | * not advised. The low end is too restrictive and the high end is too permissive.
323 | * The suggested value for $maxSimilarity is 50.
324 | *
325 | * You may be thinking that a value of 100 should have the effect of accepting
326 | * everything like a value of 0 does. That's logical and probably true,
327 | * but is unproven and untested. Besides, 0 skips the work involved
328 | * making the calculation unlike when using 100.
329 | *
330 | * The (admittedly limited) testing that's been done suggests a useful working range
331 | * of 50 to 60. You can set it lower than 50, but site users will probably start
332 | * to complain about the large number of proposed passwords getting rejected.
333 | * At around 60 or more it starts to see pairs like 'captain joe' and 'joe*captain' as
334 | * perfectly acceptable which clearly they are not.
335 | *
336 | * To disable similarity checking set the value to 0.
337 | * public $maxSimilarity = 0;
338 | */
339 | public int $maxSimilarity = 50;
340 |
341 | /**
342 | * --------------------------------------------------------------------
343 | * Hashing Algorithm to use
344 | * --------------------------------------------------------------------
345 | * Valid values are
346 | * - PASSWORD_DEFAULT (default)
347 | * - PASSWORD_BCRYPT
348 | * - PASSWORD_ARGON2I - As of PHP 7.2 only if compiled with support for it
349 | * - PASSWORD_ARGON2ID - As of PHP 7.3 only if compiled with support for it
350 | */
351 | public string $hashAlgorithm = PASSWORD_DEFAULT;
352 |
353 | /**
354 | * --------------------------------------------------------------------
355 | * ARGON2I/ARGON2ID Algorithm options
356 | * --------------------------------------------------------------------
357 | * The ARGON2I method of hashing allows you to define the "memory_cost",
358 | * the "time_cost" and the number of "threads", whenever a password hash is
359 | * created.
360 | */
361 | public int $hashMemoryCost = 65536; // PASSWORD_ARGON2_DEFAULT_MEMORY_COST;
362 |
363 | public int $hashTimeCost = 4; // PASSWORD_ARGON2_DEFAULT_TIME_COST;
364 | public int $hashThreads = 1; // PASSWORD_ARGON2_DEFAULT_THREADS;
365 |
366 | /**
367 | * --------------------------------------------------------------------
368 | * BCRYPT Algorithm options
369 | * --------------------------------------------------------------------
370 | * The BCRYPT method of hashing allows you to define the "cost"
371 | * or number of iterations made, whenever a password hash is created.
372 | * This defaults to a value of 12 which is an acceptable number.
373 | * However, depending on the security needs of your application
374 | * and the power of your hardware, you might want to increase the
375 | * cost. This makes the hashing process takes longer.
376 | *
377 | * Valid range is between 4 - 31.
378 | */
379 | public int $hashCost = 12;
380 |
381 | /**
382 | * ////////////////////////////////////////////////////////////////////
383 | * OTHER SETTINGS
384 | * ////////////////////////////////////////////////////////////////////
385 | */
386 |
387 | /**
388 | * --------------------------------------------------------------------
389 | * Customize the DB group used for each model
390 | * --------------------------------------------------------------------
391 | */
392 | public ?string $DBGroup = null;
393 |
394 | /**
395 | * --------------------------------------------------------------------
396 | * Customize Name of Shield Tables
397 | * --------------------------------------------------------------------
398 | * Only change if you want to rename the default Shield table names
399 | *
400 | * It may be necessary to change the names of the tables for
401 | * security reasons, to prevent the conflict of table names,
402 | * the internal policy of the companies or any other reason.
403 | *
404 | * - users Auth Users Table, the users info is stored.
405 | * - auth_identities Auth Identities Table, Used for storage of passwords, access tokens, social login identities, etc.
406 | * - auth_logins Auth Login Attempts, Table records login attempts.
407 | * - auth_token_logins Auth Token Login Attempts Table, Records Bearer Token type login attempts.
408 | * - auth_remember_tokens Auth Remember Tokens (remember-me) Table.
409 | * - auth_groups_users Groups Users Table.
410 | * - auth_permissions_users Users Permissions Table.
411 | *
412 | * @var array<string, string>
413 | */
414 | public array $tables = [
415 | 'users' => 'users',
416 | 'identities' => 'auth_identities',
417 | 'logins' => 'auth_logins',
418 | 'token_logins' => 'auth_token_logins',
419 | 'remember_tokens' => 'auth_remember_tokens',
420 | 'groups_users' => 'auth_groups_users',
421 | 'permissions_users' => 'auth_permissions_users',
422 | ];
423 |
424 | /**
425 | * --------------------------------------------------------------------
426 | * User Provider
427 | * --------------------------------------------------------------------
428 | * The name of the class that handles user persistence.
429 | * By default, this is the included UserModel, which
430 | * works with any of the database engines supported by CodeIgniter.
431 | * You can change it as long as they adhere to the
432 | * CodeIgniter\Shield\Models\UserModel.
433 | *
434 | * @var class-string<UserModel>
435 | */
436 | public string $userProvider = UserModel::class;
437 |
438 | /**
439 | * Returns the URL that a user should be redirected
440 | * to after a successful login.
441 | */
442 | public function loginRedirect(): string
443 | {
444 | $session = session();
445 | $url = $session->getTempdata('beforeLoginUrl') ?? setting('Auth.redirects')['login'];
446 |
447 | return $this->getUrl($url);
448 | }
449 |
450 | /**
451 | * Returns the URL that a user should be redirected
452 | * to after they are logged out.
453 | */
454 | public function logoutRedirect(): string
455 | {
456 | $url = setting('Auth.redirects')['logout'];
457 |
458 | return $this->getUrl($url);
459 | }
460 |
461 | /**
462 | * Returns the URL the user should be redirected to
463 | * after a successful registration.
464 | */
465 | public function registerRedirect(): string
466 | {
467 | $url = setting('Auth.redirects')['register'];
468 |
469 | return $this->getUrl($url);
470 | }
471 |
472 | /**
473 | * Returns the URL the user should be redirected to
474 | * if force_reset identity is set to true.
475 | */
476 | public function forcePasswordResetRedirect(): string
477 | {
478 | $url = setting('Auth.redirects')['force_reset'];
479 |
480 | return $this->getUrl($url);
481 | }
482 |
483 | /**
484 | * Returns the URL the user should be redirected to
485 | * if permission denied.
486 | */
487 | public function permissionDeniedRedirect(): string
488 | {
489 | $url = setting('Auth.redirects')['permission_denied'];
490 |
491 | return $this->getUrl($url);
492 | }
493 |
494 | /**
495 | * Returns the URL the user should be redirected to
496 | * if group denied.
497 | */
498 | public function groupDeniedRedirect(): string
499 | {
500 | $url = setting('Auth.redirects')['group_denied'];
501 |
502 | return $this->getUrl($url);
503 | }
504 |
505 | /**
506 | * Accepts a string which can be an absolute URL or
507 | * a named route or just a URI path, and returns the
508 | * full path.
509 | *
510 | * @param string $url an absolute URL or a named route or just URI path
511 | */
512 | protected function getUrl(string $url): string
513 | {
514 | return match (true) {
515 | str_starts_with($url, 'http://') || str_starts_with($url, 'https://') => $url,
516 | route_to($url) !== false => rtrim(url_to($url), '/ '),
517 | default => rtrim(site_url($url), '/ '),
518 | };
519 | }
520 | }