Completed
Push — master ( 614feb...ebc245 )
by Antonio Carlos
01:33
created

Authenticator::getOldOneTimePassword()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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