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
3declare(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
14namespace Config;
15
16use CodeIgniter\Shield\Config\Auth as ShieldAuth;
17use CodeIgniter\Shield\Authentication\Actions\ActionInterface;
18use CodeIgniter\Shield\Authentication\AuthenticatorInterface;
19use CodeIgniter\Shield\Authentication\Authenticators\AccessTokens;
20use CodeIgniter\Shield\Authentication\Authenticators\HmacSha256;
21use CodeIgniter\Shield\Authentication\Authenticators\JWT;
22use CodeIgniter\Shield\Authentication\Authenticators\Session;
23use CodeIgniter\Shield\Authentication\Passwords\CompositionValidator;
24use CodeIgniter\Shield\Authentication\Passwords\DictionaryValidator;
25use CodeIgniter\Shield\Authentication\Passwords\NothingPersonalValidator;
26use CodeIgniter\Shield\Authentication\Passwords\PwnedValidator;
27use CodeIgniter\Shield\Authentication\Passwords\ValidatorInterface;
28use CodeIgniter\Shield\Models\UserModel;
29
30class 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}