Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
50.00% |
8 / 16 |
|
42.86% |
3 / 7 |
CRAP | |
0.00% |
0 / 1 |
| Auth | |
50.00% |
8 / 16 |
|
42.86% |
3 / 7 |
16.00 | |
0.00% |
0 / 1 |
| loginRedirect | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
| logoutRedirect | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
| registerRedirect | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
| forcePasswordResetRedirect | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
| permissionDeniedRedirect | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
| groupDeniedRedirect | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
| getUrl | |
100.00% |
3 / 3 |
|
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 | } |