-
trunky
Junior Member
-
Posts: 12
Threads: 5
Joined: Aug 2023
Reputation:
0
Hi everyone,
I am not sure, either my problem is very special or I am just dumb. At least I tried something with Shield, it worked all fine, had some testing and just after putting it online it broke...
So I managed to adjust Shield to accept UUID as field for login. It sends an email to this uuid (done by the Email activator. It's some magic, I'll leave this as blackbox here) and waits for a confirmation code, so far so good. The url for this is auth/a/show as expected. First thing I notice, once one tries to reload this, it gives a 404 error.
Next thing, once one enters the code it should redirect to auth/a/verify with the post request and the confirmation code. But it always gets a 404 error as well!
"php spark routes" shows everything fine and in order
PHP Code: | GET | / | » | \App\Controllers\Home::index | | toolbar | | GET | register | » | \CodeIgniter\Shield\Controllers\RegisterController::registerView | | toolbar | | GET | login | » | \CodeIgniter\Shield\Controllers\LoginController::loginView | | toolbar | | GET | login/magic-link | magic-link | \CodeIgniter\Shield\Controllers\MagicLinkController::loginView | | toolbar | | GET | login/verify-magic-link | verify-magic-link | \CodeIgniter\Shield\Controllers\MagicLinkController::verify | | toolbar | | GET | logout | » | \CodeIgniter\Shield\Controllers\LoginController::logoutAction | | toolbar | | GET | auth/a/show | auth-action-show | \CodeIgniter\Shield\Controllers\ActionController::show | | toolbar | | GET | about-us | » | \App\Controllers\Home::page/about-us | | toolbar | | GET | links | » | \App\Controllers\Home::page/links | | toolbar | | POST | register | » | \CodeIgniter\Shield\Controllers\RegisterController::registerAction | | toolbar | | POST | login | » | \CodeIgniter\Shield\Controllers\LoginController::loginAction | | toolbar | | POST | login/magic-link | » | \CodeIgniter\Shield\Controllers\MagicLinkController::loginAction | | toolbar | | POST | auth/a/handle | auth-action-handle | \CodeIgniter\Shield\Controllers\ActionController::handle | | toolbar | | POST | auth/a/verify | auth-action-verify | \CodeIgniter\Shield\Controllers\ActionController::verify | | toolbar
If anyone has any idea or how to help, I'll be very happy. If a demo url can help, I can provide one.
-
trunky
Junior Member
-
Posts: 12
Threads: 5
Joined: Aug 2023
Reputation:
0
Alright, let me see what I've modified....
app/Actions/EmailActivator.php (copied the one from Shield and edited only show() )
PHP Code: <?php
declare(strict_types=1);
/** * This file is part of CodeIgniter Shield. * * (c) CodeIgniter Foundation <[email protected]> * * For the full copyright and license information, please view * the LICENSE file that was distributed with this source code. */
namespace CodeIgniter\Shield\Authentication\Actions;
use CodeIgniter\Exceptions\PageNotFoundException; use CodeIgniter\HTTP\IncomingRequest; use CodeIgniter\HTTP\RedirectResponse; use CodeIgniter\HTTP\Response; use CodeIgniter\I18n\Time; use CodeIgniter\Shield\Authentication\Authenticators\Session; use CodeIgniter\Shield\Entities\User; use CodeIgniter\Shield\Entities\UserIdentity; use CodeIgniter\Shield\Exceptions\LogicException; use CodeIgniter\Shield\Exceptions\RuntimeException; use CodeIgniter\Shield\Models\UserIdentityModel; use CodeIgniter\Shield\Traits\Viewable;
class EmailActivator implements ActionInterface { use Viewable;
private string $type = Session::ID_TYPE_EMAIL_ACTIVATE;
/** * Shows the initial screen to the user telling them * that an email was just sent to them with a link * to confirm their email address. */ public function show(): string { /** @var Session $authenticator */ $authenticator = auth('session')->getAuthenticator();
$user = $authenticator->getPendingUser(); if ($user === null) { throw new RuntimeException('Cannot get the pending login User.'); }
$userEmail = $user->email;
if ($userEmail === null) { throw new LogicException('Email Activation needs user email address. user_id: ' . $user->id); }
$code = $this->createIdentity($user);
/** @var IncomingRequest $request */ $request = service('request');
$ipAddress = $request->getIPAddress(); $userAgent = (string)$request->getUserAgent(); $date = Time::now()->toDateTimeString();
// Send the email helper('email'); $email = emailer()->setFrom(setting('Email.fromEmail'), setting('Email.fromName') ?? ''); $email->setTo($userEmail); $email->setSubject('slponyplay'); $email->setMessage('signup|'.$user->uuid . '|' . $code);
if ($email->send(false) === false) { throw new RuntimeException('Cannot send email for user: ' . $user->email . "\n" . $email->printDebugger(['headers'])); }
// Clear the email $email->clear();
// force to reset password //$user->forcePasswordReset(); // did work at the beginning, lately gives error
auth()->logout();
// Display the info page return $this->view(setting('Auth.views')['action_email_activate_show'], ['user' => $user]); }
/** * This method is unused. * * @return Response|string */ public function handle(IncomingRequest $request) { throw new PageNotFoundException(); }
/** * Verifies the email address and code matches an * identity we have for that user. * * @return RedirectResponse|string */ public function verify(IncomingRequest $request) { /** @var Session $authenticator */ $authenticator = auth('session')->getAuthenticator();
$postedToken = $request->getVar('token');
$user = $authenticator->getPendingUser(); if ($user === null) { throw new RuntimeException('Cannot get the pending login User.'); }
$identity = $this->getIdentity($user);
// No match - let them try again. if (!$authenticator->checkAction($identity, $postedToken)) { session()->setFlashdata('error', lang('Auth.invalidActivateToken'));
return $this->view(setting('Auth.views')['action_email_activate_show']); }
$user = $authenticator->getUser();
// Set the user active now $user->activate();
// Success! return redirect()->to(config('Auth')->registerRedirect()) ->with('message', lang('Auth.registerSuccess')); }
/** * Creates an identity for the action of the user. * * @return string secret */ public function createIdentity(User $user): string { /** @var UserIdentityModel $identityModel */ $identityModel = model(UserIdentityModel::class);
// Delete any previous identities for action $identityModel->deleteIdentitiesByType($user, $this->type);
$generator = static fn(): string => random_string('nozero', 6);
return $identityModel->createCodeIdentity( $user, [ 'type' => $this->type, 'name' => 'register', 'extra' => lang('Auth.needVerification'), ], $generator ); }
/** * Returns an identity for the action of the user. */ private function getIdentity(User $user): ?UserIdentity { /** @var UserIdentityModel $identityModel */ $identityModel = model(UserIdentityModel::class);
return $identityModel->getIdentityByType( $user, $this->type ); }
/** * Returns the string type of the action class. */ public function getType(): string { return $this->type; } }
config/Auth.php
PHP Code: <?php
declare(strict_types=1);
/** * This file is part of CodeIgniter Shield. * * (c) CodeIgniter Foundation <[email protected]> * * For the full copyright and license information, please view * the LICENSE file that was distributed with this source code. */
namespace Config;
use CodeIgniter\Shield\Config\Auth as ShieldAuth; use CodeIgniter\Shield\Authentication\Actions\ActionInterface; use CodeIgniter\Shield\Authentication\AuthenticatorInterface; use CodeIgniter\Shield\Authentication\Authenticators\AccessTokens; use CodeIgniter\Shield\Authentication\Authenticators\HmacSha256; use CodeIgniter\Shield\Authentication\Authenticators\JWT; use CodeIgniter\Shield\Authentication\Authenticators\Session; use CodeIgniter\Shield\Authentication\Passwords\CompositionValidator; use CodeIgniter\Shield\Authentication\Passwords\DictionaryValidator; use CodeIgniter\Shield\Authentication\Passwords\NothingPersonalValidator; use CodeIgniter\Shield\Authentication\Passwords\PwnedValidator; use CodeIgniter\Shield\Authentication\Passwords\ValidatorInterface; use CodeIgniter\Shield\Models\UserModel;
class Auth extends ShieldAuth { /** * //////////////////////////////////////////////////////////////////// * AUTHENTICATION * //////////////////////////////////////////////////////////////////// */
// Constants for Record Login Attempts. Do not change. public const RECORD_LOGIN_ATTEMPT_NONE = 0; // Do not record at all public const RECORD_LOGIN_ATTEMPT_FAILURE = 1; // Record only failures public const RECORD_LOGIN_ATTEMPT_ALL = 2; // Record all login attempts
/** * -------------------------------------------------------------------- * View files * -------------------------------------------------------------------- */ public array $views = [ 'login' => 'App\Views\Shield\login', 'register' => 'App\Views\Shield\register', 'layout' => 'App\Views\templates\layout', 'action_email_2fa' => '\CodeIgniter\Shield\Views\email_2fa_show', 'action_email_2fa_verify' => '\CodeIgniter\Shield\Views\email_2fa_verify', 'action_email_2fa_email' => '\CodeIgniter\Shield\Views\Email\email_2fa_email', 'action_email_activate_show' => '\CodeIgniter\Shield\Views\email_activate_show', 'action_email_activate_email' => '\CodeIgniter\Shield\Views\Email\email_activate_email', 'magic-link-login' => 'App\Views\Shield\magic_link_form', 'magic-link-message' => '\CodeIgniter\Shield\Views\magic_link_message', 'magic-link-email' => '\CodeIgniter\Shield\Views\Email\magic_link_email', ];
/** * -------------------------------------------------------------------- * Redirect URLs * -------------------------------------------------------------------- * The default URL that a user will be redirected to after various auth * actions. This can be either of the following: * * 1. An absolute URL. E.g. http://example.com OR https://example.com * 2. A named route that can be accessed using `route_to()` or `url_to()` * 3. A URI path within the application. e.g 'admin', 'login', 'expath' * * If you need more flexibility you can override the `getUrl()` method * to apply any logic you may need. */ public array $redirects = [ 'register' => '/', 'login' => '/', 'logout' => '/', 'force_reset' => '/change-password', 'permission_denied' => '/', 'group_denied' => '/', ];
/** * -------------------------------------------------------------------- * Authentication Actions * -------------------------------------------------------------------- * Specifies the class that represents an action to take after * the user logs in or registers a new account at the site. * * You must register actions in the order of the actions to be performed. * * Available actions with Shield: * - register: \CodeIgniter\Shield\Authentication\Actions\EmailActivator::class * - login: \CodeIgniter\Shield\Authentication\Actions\Email2FA::class * * @var array<string, class-string<ActionInterface>|null> */ public array $actions = [ 'register' => \App\Actions\EmailActivator::class, 'login' => null, ];
/** * -------------------------------------------------------------------- * Authenticators * -------------------------------------------------------------------- * The available authentication systems, listed * with alias and class name. These can be referenced * by alias in the auth helper: * auth('tokens')->attempt($credentials); * * @var array<string, class-string<AuthenticatorInterface>> */ public array $authenticators = [ 'tokens' => AccessTokens::class, 'session' => Session::class, 'hmac' => HmacSha256::class, // 'jwt' => JWT::class, ];
/** * -------------------------------------------------------------------- * Default Authenticator * -------------------------------------------------------------------- * The Authenticator to use when none is specified. * Uses the $key from the $authenticators array above. */ public string $defaultAuthenticator = 'session';
/** * -------------------------------------------------------------------- * Authentication Chain * -------------------------------------------------------------------- * The Authenticators to test logged in status against * when using the 'chain' filter. Each Authenticator listed will be checked. * If no match is found, then the next in the chain will be checked. * * @var string[] * @phpstan-var list<string> */ public array $authenticationChain = [ 'session', 'tokens', 'hmac', // 'jwt', ];
/** * -------------------------------------------------------------------- * Allow Registration * -------------------------------------------------------------------- * Determines whether users can register for the site. */ public bool $allowRegistration = true;
/** * -------------------------------------------------------------------- * Record Last Active Date * -------------------------------------------------------------------- * If true, will always update the `last_active` datetime for the * logged-in user on every page request. * This feature only works when session/tokens filter is active. * * @see https://codeigniter4.github.io/shield/quick_start_guide/using_session_auth/#protecting-pages for set filters. */ public bool $recordActiveDate = true;
/** * -------------------------------------------------------------------- * Allow Magic Link Logins * -------------------------------------------------------------------- * If true, will allow the use of "magic links" sent via the email * as a way to log a user in without the need for a password. * By default, this is used in place of a password reset flow, but * could be modified as the only method of login once an account * has been set up. */ public bool $allowMagicLinkLogins = true;
/** * -------------------------------------------------------------------- * Magic Link Lifetime * -------------------------------------------------------------------- * Specifies the amount of time, in seconds, that a magic link is valid. * You can use Time Constants or any desired number. */ public int $magicLinkLifetime = HOUR;
/** * -------------------------------------------------------------------- * Session Authenticator Configuration * -------------------------------------------------------------------- * These settings only apply if you are using the Session Authenticator * for authentication. * * - field The name of the key the current user info is stored in session * - allowRemembering Does the system allow use of "remember-me" * - rememberCookieName The name of the cookie to use for "remember-me" * - rememberLength The length of time, in seconds, to remember a user. * * @var array<string, bool|int|string> */ public array $sessionConfig = [ 'field' => 'user', 'allowRemembering' => true, 'rememberCookieName' => 'remember', 'rememberLength' => 30 * DAY, ];
/** * -------------------------------------------------------------------- * The validation rules for username * -------------------------------------------------------------------- * * Do not use string rules like `required|valid_email`. * * @var array<string, array<int, string>|string> */ public array $usernameValidationRules = [ 'label' => 'Auth.username', 'rules' => [ 'required', 'max_length[30]', 'min_length[3]', 'regex_match[/\A[a-zA-Z0-9\.]+\z/]', ], ];
/** * -------------------------------------------------------------------- * The validation rules for email * -------------------------------------------------------------------- * * Do not use string rules like `required|valid_email`. * * @var array<string, array<int, string>|string> */ public array $emailValidationRules = [ 'label' => 'Auth.email', 'rules' => [ 'required', 'max_length[254]', 'valid_email', ], ];
/** * -------------------------------------------------------------------- * The validation rules for uuid * -------------------------------------------------------------------- * * Do not use string rules like `required|valid_email`. * * @var array<string, array<int, string>|string> */ public array $uuidValidationRules = [ 'label' => 'Auth.uuid', 'rules' => [ 'required', 'min_length[36]', 'max_length[36]', 'regex_match[/^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$/]', ], ];
/** * -------------------------------------------------------------------- * Minimum Password Length * -------------------------------------------------------------------- * The minimum length that a password must be to be accepted. * Recommended minimum value by NIST = 8 characters. */ public int $minimumPasswordLength = 8;
/** * -------------------------------------------------------------------- * Password Check Helpers * -------------------------------------------------------------------- * The PasswordValidator class runs the password through all of these * classes, each getting the opportunity to pass/fail the password. * You can add custom classes as long as they adhere to the * CodeIgniter\Shield\Authentication\Passwords\ValidatorInterface. * * @var class-string<ValidatorInterface>[] */ public array $passwordValidators = [ CompositionValidator::class, NothingPersonalValidator::class, DictionaryValidator::class, // PwnedValidator::class, ];
/** * -------------------------------------------------------------------- * Valid login fields * -------------------------------------------------------------------- * Fields that are available to be used as credentials for login. */ public array $validFields = [ 'uuid', 'username', ];
/** * -------------------------------------------------------------------- * Additional Fields for "Nothing Personal" * -------------------------------------------------------------------- * The NothingPersonalValidator prevents personal information from * being used in passwords. The email and username fields are always * considered by the validator. Do not enter those field names here. * * An extended User Entity might include other personal info such as * first and/or last names. $personalFields is where you can add * fields to be considered as "personal" by the NothingPersonalValidator. * For example: * $personalFields = ['firstname', 'lastname']; */ public array $personalFields = [];
/** * -------------------------------------------------------------------- * Password / Username Similarity * -------------------------------------------------------------------- * Among other things, the NothingPersonalValidator checks the * amount of sameness between the password and username. * Passwords that are too much like the username are invalid. * * The value set for $maxSimilarity represents the maximum percentage * of similarity at which the password will be accepted. In other words, any * calculated similarity equal to, or greater than $maxSimilarity * is rejected. * * The accepted range is 0-100, with 0 (zero) meaning don't check similarity. * Using values at either extreme of the *working range* (1-100) is * not advised. The low end is too restrictive and the high end is too permissive. * The suggested value for $maxSimilarity is 50. * * You may be thinking that a value of 100 should have the effect of accepting * everything like a value of 0 does. That's logical and probably true, * but is unproven and untested. Besides, 0 skips the work involved * making the calculation unlike when using 100. * * The (admittedly limited) testing that's been done suggests a useful working range * of 50 to 60. You can set it lower than 50, but site users will probably start * to complain about the large number of proposed passwords getting rejected. * At around 60 or more it starts to see pairs like 'captain joe' and 'joe*captain' as * perfectly acceptable which clearly they are not. * * To disable similarity checking set the value to 0. * public $maxSimilarity = 0; */ public int $maxSimilarity = 50;
/** * -------------------------------------------------------------------- * Hashing Algorithm to use * -------------------------------------------------------------------- * Valid values are * - PASSWORD_DEFAULT (default) * - PASSWORD_BCRYPT * - PASSWORD_ARGON2I - As of PHP 7.2 only if compiled with support for it * - PASSWORD_ARGON2ID - As of PHP 7.3 only if compiled with support for it */ public string $hashAlgorithm = PASSWORD_DEFAULT;
/** * -------------------------------------------------------------------- * ARGON2I/ARGON2ID Algorithm options * -------------------------------------------------------------------- * The ARGON2I method of hashing allows you to define the "memory_cost", * the "time_cost" and the number of "threads", whenever a password hash is * created. */ public int $hashMemoryCost = 65536; // PASSWORD_ARGON2_DEFAULT_MEMORY_COST;
public int $hashTimeCost = 4; // PASSWORD_ARGON2_DEFAULT_TIME_COST; public int $hashThreads = 1; // PASSWORD_ARGON2_DEFAULT_THREADS;
/** * -------------------------------------------------------------------- * BCRYPT Algorithm options * -------------------------------------------------------------------- * The BCRYPT method of hashing allows you to define the "cost" * or number of iterations made, whenever a password hash is created. * This defaults to a value of 12 which is an acceptable number. * However, depending on the security needs of your application * and the power of your hardware, you might want to increase the * cost. This makes the hashing process takes longer. * * Valid range is between 4 - 31. */ public int $hashCost = 12;
/** * //////////////////////////////////////////////////////////////////// * OTHER SETTINGS * //////////////////////////////////////////////////////////////////// */
/** * -------------------------------------------------------------------- * Customize the DB group used for each model * -------------------------------------------------------------------- */ public ?string $DBGroup = null;
/** * -------------------------------------------------------------------- * Customize Name of Shield Tables * -------------------------------------------------------------------- * Only change if you want to rename the default Shield table names * * It may be necessary to change the names of the tables for * security reasons, to prevent the conflict of table names, * the internal policy of the companies or any other reason. * * - users Auth Users Table, the users info is stored. * - auth_identities Auth Identities Table, Used for storage of passwords, access tokens, social login identities, etc. * - auth_logins Auth Login Attempts, Table records login attempts. * - auth_token_logins Auth Token Login Attempts Table, Records Bearer Token type login attempts. * - auth_remember_tokens Auth Remember Tokens (remember-me) Table. * - auth_groups_users Groups Users Table. * - auth_permissions_users Users Permissions Table. * * @var array<string, string> */ public array $tables = [ 'users' => 'users', 'identities' => 'auth_identities', 'logins' => 'auth_logins', 'token_logins' => 'auth_token_logins', 'remember_tokens' => 'auth_remember_tokens', 'groups_users' => 'auth_groups_users', 'permissions_users' => 'auth_permissions_users', ];
/** * -------------------------------------------------------------------- * User Provider * -------------------------------------------------------------------- * The name of the class that handles user persistence. * By default, this is the included UserModel, which * works with any of the database engines supported by CodeIgniter. * You can change it as long as they adhere to the * CodeIgniter\Shield\Models\UserModel. * * @var class-string<UserModel> */ // public string $userProvider = UserModel::class; public string $userProvider = \App\Models\UserModel::class;
/** * Returns the URL that a user should be redirected * to after a successful login. */ public function loginRedirect(): string { $session = session(); $url = $session->getTempdata('beforeLoginUrl') ?? setting('Auth.redirects')['login'];
return $this->getUrl($url); }
/** * Returns the URL that a user should be redirected * to after they are logged out. */ public function logoutRedirect(): string { $url = setting('Auth.redirects')['logout'];
return $this->getUrl($url); }
/** * Returns the URL the user should be redirected to * after a successful registration. */ public function registerRedirect(): string { $url = setting('Auth.redirects')['register'];
return $this->getUrl($url); }
/** * Returns the URL the user should be redirected to * if force_reset identity is set to true. */ public function forcePasswordResetRedirect(): string { $url = setting('Auth.redirects')['force_reset'];
return $this->getUrl($url); }
/** * Returns the URL the user should be redirected to * if permission denied. */ public function permissionDeniedRedirect(): string { $url = setting('Auth.redirects')['permission_denied'];
return $this->getUrl($url); }
/** * Returns the URL the user should be redirected to * if group denied. */ public function groupDeniedRedirect(): string { $url = setting('Auth.redirects')['group_denied'];
return $this->getUrl($url); }
/** * Accepts a string which can be an absolute URL or * a named route or just a URI path, and returns the * full path. * * @param string $url an absolute URL or a named route or just URI path */ protected function getUrl(string $url): string { // To accommodate all url patterns $final_url = '';
switch (true) { case strpos($url, 'http://') === 0 || strpos($url, 'https://') === 0: // URL begins with 'http' or 'https'. E.g. http://example.com $final_url = $url; break;
case route_to($url) !== false: // URL is a named-route $final_url = rtrim(url_to($url), '/ '); break;
default: // URL is a route (URI path) $final_url = rtrim(site_url($url), '/ '); break; }
return $final_url; } }
config/Routes.php
PHP Code: <?php
use CodeIgniter\Router\RouteCollection;
/** * @var RouteCollection $routes */ $routes->get('/', 'Home::index');
# Stables $routes->get('stables', 'Stables::list'); $routes->get('stable/(:num)', 'Stables::view/$1'); $routes->get('stable/new', 'Stables::edit', ['filter' => 'session']); $routes->get('stable/edit/(:num)', 'Stables::edit/$1', ['filter' => 'session']); $routes->post('stable/save', 'Stables::save', ['filter' => 'session']);
# Profiles $routes->get('profiles', 'Profiles::list'); $routes->get('u/(:segment)', 'Profiles::view/$1'); $routes->get('edit-profile', 'Profiles::edit', ['filter' => 'session']); $routes->post('profile/save', 'Profiles::save', ['filter' => 'session']); $routes->post('profile/delete', 'Profiles::delete', ['filter' => 'session']);
# Events $routes->get('events', 'Events::list'); $routes->get('event/(:num)', 'Events::view/$1'); $routes->get('event/new', 'Events::edit', ['filter' => 'session']); $routes->get('event/edit/(:num)', 'Events::edit/$1', ['filter' => 'session']); $routes->post('event/save', 'Events::save', ['filter' => 'session']);
# Results $routes->get('results', 'Results::list'); $routes->get('results/(:num)', 'Results::list/$1'); // for year filter $routes->get('result/(:num)', 'Results::view/$1'); $routes->match(['get', 'post'], 'result/new', 'Results::edit', ['filter' => 'session']); $routes->post('result/save', 'Results::save', ['filter' => 'session']);
# Forum $routes->get('forum', 'Forums::show'); $routes->get('forum/(:num)', 'Forums::show/$1'); $routes->post('forum/save', 'Forums::save', ['filter' => 'session']);
# Insights $routes->post('insights/save', 'Insights::save', ['filter' => 'session']);
# Shield, Login, Registration etc $routes->post('login/magic-link', 'MagicLinkController::loginAction'); service('auth')->routes($routes); $routes->match(['get', 'post'], 'change-password', 'Home::changePassword', ['filter' => 'session']);
# static pages $routes->get('polo-league', 'Home::page/polo-league'); $routes->get('about-us', 'Home::page/about-us'); $routes->get('links', 'Home::page/links');
# API $routes->post('api/signup/', 'API::signup'); $routes->post('api/events', 'API::events'); $routes->post('api/autocomplete', 'API::autocomplete', ['filter' => 'session']);
and Controllers/MagicLinkController.php
PHP Code: <?php
namespace App\Controllers;
use CodeIgniter\Shield\Controllers\MagicLinkController as ShieldMagicLinkController; use CodeIgniter\HTTP\RedirectResponse; use CodeIgniter\I18n\Time; use CodeIgniter\Shield\Authentication\Authenticators\Session; use CodeIgniter\Shield\Models\UserIdentityModel;
class MagicLinkController extends ShieldMagicLinkController { public function loginAction(): string|RedirectResponse { if (!setting('Auth.allowMagicLinkLogins')) { return redirect()->route('login')->with('error', lang('Auth.magicLinkDisabled')); }
// Validate email format $rules = $this->getValidationRules(); if (!$this->validateData($this->request->getPost(), $rules, [], config('Auth')->DBGroup)) { return redirect()->route('magic-link')->with('errors', $this->validator->getErrors()); }
// Check if the user exists $uuid = $this->request->getPost('uuid'); $user = $this->provider->findByCredentials(['uuid' => $uuid]);
if ($user === null) { return redirect()->route('magic-link')->with('error', lang('Auth.invalidEmail')); }
/** @var UserIdentityModel $identityModel */ $identityModel = model(UserIdentityModel::class);
// Delete any previous magic-link identities $identityModel->deleteIdentitiesByType($user, Session::ID_TYPE_MAGIC_LINK);
// Generate the code and save it as an identity helper('text'); $token = random_string('crypto', 20);
$identityModel->insert([ 'user_id' => $user->id, 'type' => Session::ID_TYPE_MAGIC_LINK, 'secret' => $token, 'expires' => Time::now()->addSeconds(setting('Auth.magicLinkLifetime'))->format('Y-m-d H:i:s'), ]);
/** @var IncomingRequest $request */ $request = service('request');
$ipAddress = $request->getIPAddress(); $userAgent = (string)$request->getUserAgent(); $date = Time::now()->toDateTimeString();
$userEmail = '[email protected]'; // only for posting at forum
// Send the user an email with the code helper('email'); $email = emailer()->setFrom(setting('Email.fromEmail'), setting('Email.fromName') ?? ''); $email->setTo($userEmail); $email->setSubject('slponyplay'); $email->setMessage('login|' . $uuid . '|' . url_to('verify-magic-link') . '?token=' . $token);
if ($email->send(false) === false) { log_message('error', $email->printDebugger(['headers']));
return redirect()->route('magic-link')->with('error', lang('Auth.unableSendEmailToUser', [$user->email])); }
// Clear the email $email->clear();
$user->forcePasswordReset();
return $this->displayMessage(); }
protected function getValidationRules(): array { return [ 'uuid' => config('Auth')->uuidValidationRules, ]; } }
This should be all. I'd started it with Shield 1.0.0 when it came out and tested it the whole registration process and magic link thoroughly. And when the site was finished, it was broken. Updates for Shield has been done, tested it with Shield 1.0.0 again but no success.
Also it seems to revert to email still at times, when I enter a UUID the form responds it can't send an email to xyz (i only use fake emails on all users)
|