Skip to content

Commit 971966b

Browse files
authored
Merge pull request #171 from kenjis/add-magic-link-event-logging
feat: Magic Link Login events and logging
2 parents 3259519 + 07d3b2a commit 971966b

File tree

2 files changed

+48
-4
lines changed

2 files changed

+48
-4
lines changed

docs/5 - events.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ Events::on('failedLogin', function($credentials) {
4545
['email' => 'foo@example.com'];
4646
```
4747

48+
When the magic link login fails, the following array will be provided:
49+
50+
```php
51+
['magicLinkToken' => 'the token value used']
52+
```
53+
4854
#### logout
4955

5056
Fired immediately after a successful logout. The only argument is the `User` entity.

src/Controllers/MagicLinkController.php

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
namespace CodeIgniter\Shield\Controllers;
44

55
use App\Controllers\BaseController;
6+
use CodeIgniter\Events\Events;
67
use CodeIgniter\HTTP\RedirectResponse;
78
use CodeIgniter\I18n\Time;
8-
use CodeIgniter\Shield\Auth;
9+
use CodeIgniter\Shield\Authentication\Authenticators\Session;
10+
use CodeIgniter\Shield\Models\LoginModel;
911
use CodeIgniter\Shield\Models\UserIdentityModel;
1012
use CodeIgniter\Shield\Models\UserModel;
1113

@@ -105,8 +107,15 @@ public function verify(): RedirectResponse
105107

106108
$identity = $identityModel->getIdentityBySecret('magic-link', $token);
107109

110+
$identifier = 'magic-link: ' . $token;
111+
108112
// No token found?
109113
if ($identity === null) {
114+
$this->recordLoginAttempt($identifier, false);
115+
116+
$credentials = ['magicLinkToken' => $token];
117+
Events::trigger('failedLogin', $credentials);
118+
110119
return redirect()->route('magic-link')->with('error', lang('Auth.magicTokenNotFound'));
111120
}
112121

@@ -115,16 +124,45 @@ public function verify(): RedirectResponse
115124

116125
// Token expired?
117126
if (Time::now()->isAfter($identity->expires)) {
127+
$this->recordLoginAttempt($identifier, false);
128+
129+
$credentials = ['magicLinkToken' => $token];
130+
Events::trigger('failedLogin', $credentials);
131+
118132
return redirect()->route('magic-link')->with('error', lang('Auth.magicLinkExpired'));
119133
}
120134

121-
/** @var Auth $auth */
122-
$auth = service('auth');
135+
/** @var Session $authenticator */
136+
$authenticator = auth('session')->getAuthenticator();
123137

124138
// Log the user in
125-
$auth->loginById($identity->user_id);
139+
$authenticator->loginById($identity->user_id);
140+
141+
$user = $authenticator->getUser();
142+
143+
$this->recordLoginAttempt($identifier, true, $user->id);
126144

127145
// Get our login redirect url
128146
return redirect()->to(config('Auth')->loginRedirect());
129147
}
148+
149+
/**
150+
* @param int|string|null $userId
151+
*/
152+
private function recordLoginAttempt(
153+
string $identifier,
154+
bool $success,
155+
$userId = null
156+
): void {
157+
/** @var LoginModel $loginModel */
158+
$loginModel = model(LoginModel::class);
159+
160+
$loginModel->recordLoginAttempt(
161+
$identifier,
162+
$success,
163+
$this->request->getIPAddress(),
164+
$this->request->getUserAgent(),
165+
$userId
166+
);
167+
}
130168
}

0 commit comments

Comments
 (0)