Completed
Push — master ( 5c8721...fc356f )
by Antonio Carlos
02:14
created

Authenticator::getRequest()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
namespace PragmaRX\Google2FALaravel\Support;
4
5
use Carbon\Carbon;
6
use Google2FA;
7
use Illuminate\Http\Request as IlluminateRequest;
8
use PragmaRX\Google2FALaravel\Exceptions\InvalidOneTimePassword;
9
use PragmaRX\Google2FALaravel\Exceptions\InvalidSecretKey;
10
11
class Authenticator
12
{
13
    use Config, Response, Request, Session, ErrorBag, Input;
14
15
    /**
16
     * The auth instance.
17
     *
18
     * @var
19
     */
20
    protected $auth;
21
22
    /**
23
     * The current password.
24
     *
25
     * @var
26
     */
27
    protected $password;
28
29
    /**
30
     * Authenticator constructor.
31
     *
32
     * @param IlluminateRequest $request
33
     */
34
    public function __construct(IlluminateRequest $request)
35
    {
36
        $this->setRequest($request);
37
    }
38
39
    /**
40
     * Authenticator boot.
41
     *
42
     * @param $request
43
     *
44
     * @return Authenticator
45
     */
46
    public function boot($request)
47
    {
48
        $this->setRequest($request);
49
50
        return $this;
51
    }
52
53
    /**
54
     * Check if it is already logged in or passable without checking for an OTP.
55
     *
56
     * @return bool
57
     */
58
    protected function canPassWithoutCheckingOTP()
59
    {
60
        return
61
            !$this->isEnabled() ||
62
            $this->noUserIsAuthenticated() ||
63
            $this->twoFactorAuthStillValid();
64
    }
65
66
    /**
67
     * Get the user Google2FA secret.
68
     *
69
     * @throws InvalidSecretKey
70
     *
71
     * @return mixed
72
     */
73
    protected function getGoogle2FASecretKey()
74
    {
75
        $secret = $this->getUser()->{$this->config('otp_secret_column')};
76
77
        if (is_null($secret) || empty($secret)) {
78
            throw new InvalidSecretKey('Secret key cannot be empty.');
79
        }
80
81
        return $secret;
82
    }
83
84
    /**
85
     * Get the previous OTP.
86
     *
87
     * @return null|void
88
     */
89
    protected function getOldOneTimePassword()
90
    {
91
        $oldPassword = $this->config('forbid_old_passwords') === true
92
            ? $this->sessionGet(Constants::SESSION_OTP_TIMESTAMP)
93
            : null;
94
95
        return $oldPassword;
96
    }
97
98
    /**
99
     * Get the OTP from user input.
100
     *
101
     * @throws InvalidOneTimePassword
102
     *
103
     * @return mixed
104
     */
105
    protected function getOneTimePassword()
106
    {
107
        if (!is_null($this->password)) {
108
            return $this->password;
109
        }
110
111
        $this->password = $this->input($this->config('otp_input'));
112
113
        if (is_null($this->password) || empty($this->password)) {
114
            throw new InvalidOneTimePassword('One Time Password cannot be empty.');
115
        }
116
117
        return $this->password;
118
    }
119
120
    /**
121
     * Keep this OTP session alive.
122
     */
123
    protected function keepAlive()
124
    {
125
        if ($this->config('keep_alive')) {
126
            $this->updateCurrentAuthTime();
127
        }
128
    }
129
130
    /**
131
     * Get minutes since last activity.
132
     *
133
     * @return int
134
     */
135
    protected function minutesSinceLastActivity()
136
    {
137
        return Carbon::now()->diffInMinutes(
138
            $this->sessionGet(Constants::SESSION_AUTH_TIME)
139
        );
140
    }
141
142
    /**
143
     * Check if no user is authenticated using OTP.
144
     *
145
     * @return bool
146
     */
147
    protected function noUserIsAuthenticated()
148
    {
149
        return is_null($this->getUser());
150
    }
151
152
    /**
153
     * Check if OTP has expired.
154
     *
155
     * @return bool
156
     */
157
    protected function passwordExpired()
158
    {
159
        if (($minutes = $this->config('lifetime')) == 0 && $this->minutesSinceLastActivity() > $minutes) {
160
            $this->logout();
161
162
            return true;
163
        }
164
165
        $this->keepAlive();
166
167
        return false;
168
    }
169
170
    /**
171
     * Set current auth as valid.
172
     */
173
    protected function storeAuthPassed()
174
    {
175
        $this->sessionPut(Constants::SESSION_AUTH_PASSED, true);
176
177
        $this->updateCurrentAuthTime();
178
    }
179
180
    /**
181
     * Store the old OTP.
182
     *
183
     * @param $key
184
     *
185
     * @return mixed
186
     */
187
    protected function storeOldOneTimePassord($key)
188
    {
189
        return $this->sessionPut(Constants::SESSION_OTP_TIMESTAMP, $key);
190
    }
191
192
    /**
193
     * Verifies, in the current session, if a 2fa check has already passed.
194
     *
195
     * @return bool
196
     */
197
    protected function twoFactorAuthStillValid()
198
    {
199
        return
200
            (bool) $this->sessionGet(Constants::SESSION_AUTH_PASSED, false) &&
201
            !$this->passwordExpired();
202
    }
203
204
    /**
205
     * Get the current user.
206
     *
207
     * @return mixed
208
     */
209
    protected function getUser()
210
    {
211
        return $this->getAuth()->user();
212
    }
213
214
    /**
215
     * Check if the current use is authenticated via OTP.
216
     *
217
     * @return bool
218
     */
219
    public function isAuthenticated()
220
    {
221
        return
222
            $this->canPassWithoutCheckingOTP()
223
                ? true
224
                : $this->checkOTP();
225
    }
226
227
    /**
228
     * Check if the module is enabled.
229
     *
230
     * @return mixed
231
     */
232
    protected function isEnabled()
233
    {
234
        return $this->config('enabled');
235
    }
236
237
    /**
238
     * Check if the input OTP is valid.
239
     *
240
     * @return bool
241
     */
242
    protected function checkOTP()
243
    {
244
        if (!$this->inputHasOneTimePassword()) {
245
            return false;
246
        }
247
248
        if ($isValid = $this->verifyGoogle2FA()) {
249
            $this->storeAuthPassed();
250
        }
251
252
        return $isValid;
253
    }
254
255
    /**
256
     * OTP logout.
257
     */
258
    public function logout()
259
    {
260
        $this->sessionForget();
261
    }
262
263
    /**
264
     * Update the current auth time.
265
     */
266
    protected function updateCurrentAuthTime()
267
    {
268
        $this->sessionPut(Constants::SESSION_AUTH_TIME, Carbon::now());
269
    }
270
271
    /**
272
     * Verify the OTP.
273
     *
274
     * @return mixed
275
     */
276
    protected function verifyGoogle2FA()
277
    {
278
        return $this->storeOldOneTimePassord(
279
            Google2Fa::verifyKey(
280
                $this->getGoogle2FASecretKey(),
281
                $this->getOneTimePassword(),
282
                $this->config('window'),
283
                null, // $timestamp
284
                $this->getOldOneTimePassword()
285
            )
286
        );
287
    }
288
}
289