1010use CodeIgniter \Shield \Authentication \AuthenticatorInterface ;
1111use CodeIgniter \Shield \Authentication \Passwords ;
1212use CodeIgniter \Shield \Entities \User ;
13+ use CodeIgniter \Shield \Exceptions \LogicException ;
1314use CodeIgniter \Shield \Models \LoginModel ;
1415use CodeIgniter \Shield \Models \RememberModel ;
16+ use CodeIgniter \Shield \Models \UserIdentityModel ;
1517use CodeIgniter \Shield \Models \UserModel ;
1618use CodeIgniter \Shield \Result ;
1719use Exception ;
@@ -25,19 +27,23 @@ class Session implements AuthenticatorInterface
2527 */
2628 protected UserModel $ provider ;
2729
30+ /**
31+ * The user logged in
32+ */
2833 protected ?User $ user = null ;
29- protected LoginModel $ loginModel ;
3034
3135 /**
3236 * Should the user be remembered?
3337 */
3438 protected bool $ shouldRemember = false ;
3539
40+ protected LoginModel $ loginModel ;
3641 protected RememberModel $ rememberModel ;
3742
3843 public function __construct (UserModel $ provider )
3944 {
4045 helper ('setting ' );
46+
4147 $ this ->provider = $ provider ;
4248 $ this ->loginModel = model (LoginModel::class); // @phpstan-ignore-line
4349 $ this ->rememberModel = model (RememberModel::class); // @phpstan-ignore-line
@@ -58,6 +64,8 @@ public function remember(bool $shouldRemember = true): self
5864 /**
5965 * Attempts to authenticate a user with the given $credentials.
6066 * Logs the user in with a successful check.
67+ *
68+ * @phpstan-param array{email?: string, username?: string, password?: string} $credentials
6169 */
6270 public function attempt (array $ credentials ): Result
6371 {
@@ -76,7 +84,8 @@ public function attempt(array $credentials): Result
7684
7785 // Fire an event on failure so devs have the chance to
7886 // let them know someone attempted to login to their account
79- Events::trigger ('failedLoginAttempt ' , $ credentials );
87+ unset($ credentials ['password ' ]);
88+ Events::trigger ('failedLogin ' , $ credentials );
8089
8190 return $ result ;
8291 }
@@ -88,9 +97,67 @@ public function attempt(array $credentials): Result
8897
8998 $ this ->recordLoginAttempt ($ credentials , true , $ ipAddress , $ userAgent , $ user ->getAuthId ());
9099
100+ // If an action has been defined for login, start it up.
101+ $ actionClass = setting ('Auth.actions ' )['login ' ] ?? null ;
102+ if (! empty ($ actionClass )) {
103+ session ()->set ('auth_action ' , $ actionClass );
104+ } else {
105+ $ this ->completeLogin ($ user );
106+ }
107+
91108 return $ result ;
92109 }
93110
111+ /**
112+ * Check token in Action
113+ *
114+ * @param string $type Action type. 'email_2fa' or 'email_activate'
115+ * @param string $token Token to check
116+ */
117+ public function checkAction (string $ type , string $ token ): bool
118+ {
119+ $ user = $ this ->loggedIn () ? $ this ->getUser () : null ;
120+
121+ if ($ user === null ) {
122+ throw new LogicException ('Cannot get the User. ' );
123+ }
124+
125+ $ identity = $ user ->getIdentity ($ type );
126+
127+ if (empty ($ token ) || $ token !== $ identity ->secret ) {
128+ return false ;
129+ }
130+
131+ /** @var UserIdentityModel $identityModel */
132+ $ identityModel = model (UserIdentityModel::class);
133+
134+ // On success - remove the identity and clean up session
135+ $ identityModel ->deleteIdentitiesByType ($ user ->getAuthId (), $ type );
136+
137+ // Clean up our session
138+ session ()->remove ('auth_action ' );
139+
140+ $ this ->user = $ user ;
141+
142+ $ this ->completeLogin ($ user );
143+
144+ return true ;
145+ }
146+
147+ private function completeLogin (User $ user ): void
148+ {
149+ // a successful login
150+ Events::trigger ('login ' , $ user );
151+ }
152+
153+ /**
154+ * Activate a User
155+ */
156+ public function activateUser (User $ user ): void
157+ {
158+ $ this ->provider ->activate ($ user );
159+ }
160+
94161 /**
95162 * @param int|string|null $userId
96163 */
@@ -113,6 +180,8 @@ private function recordLoginAttempt(
113180 /**
114181 * Checks a user's $credentials to see if they match an
115182 * existing user.
183+ *
184+ * @phpstan-param array{email?: string, username?: string, password?: string} $credentials
116185 */
117186 public function check (array $ credentials ): Result
118187 {
@@ -126,7 +195,7 @@ public function check(array $credentials): Result
126195
127196 // Remove the password from credentials so we can
128197 // check afterword.
129- $ givenPassword = $ credentials ['password ' ] ?? null ;
198+ $ givenPassword = $ credentials ['password ' ];
130199 unset($ credentials ['password ' ]);
131200
132201 // Find the existing user
@@ -243,30 +312,40 @@ private function checkRememberMeToken(string $remember)
243312 return $ token ;
244313 }
245314
246- /**
247- * Logs the given user in.
248- */
249- public function login (User $ user ): bool
315+ private function startLogin (User $ user ): void
250316 {
251- $ this ->user = $ user ;
252-
253317 // Update the user's last used date on their password identity.
254- $ this -> user ->touchIdentity ($ this -> user ->getEmailIdentity ());
318+ $ user ->touchIdentity ($ user ->getEmailIdentity ());
255319
256320 // Regenerate the session ID to help protect against session fixation
257321 if (ENVIRONMENT !== 'testing ' ) {
258322 session ()->regenerate ();
259323 }
260324
261325 // Let the session know we're logged in
262- session ()->set (setting ('Auth.sessionConfig ' )['field ' ], $ this -> user ->getAuthId ());
326+ session ()->set (setting ('Auth.sessionConfig ' )['field ' ], $ user ->getAuthId ());
263327
264328 /** @var Response $response */
265329 $ response = service ('response ' );
266330
267331 // When logged in, ensure cache control headers are in place
268332 $ response ->noCache ();
333+ }
269334
335+ /**
336+ * Logs the given user in.
337+ */
338+ public function login (User $ user ): void
339+ {
340+ $ this ->user = $ user ;
341+
342+ $ this ->startLogin ($ user );
343+
344+ $ this ->processRemember ();
345+ }
346+
347+ private function processRemember ()
348+ {
270349 if ($ this ->shouldRemember && setting ('Auth.sessionConfig ' )['allowRemembering ' ]) {
271350 $ this ->rememberUser ($ this ->user ->getAuthId ());
272351
@@ -285,9 +364,6 @@ public function login(User $user): bool
285364 if (random_int (1 , 100 ) <= 20 ) {
286365 $ this ->rememberModel ->purgeOldRememberTokens ();
287366 }
288-
289- // Trigger login event, in case anyone cares
290- return Events::trigger ('login ' , $ user );
291367 }
292368
293369 /**
0 commit comments