Passed
Push — master ( cf4fbb...361e7c )
by Artem
02:40
created

TokenGuard::user()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 20
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 8
nc 5
nop 0
dl 0
loc 20
rs 9.6111
c 0
b 0
f 0
1
<?php
2
3
namespace Slides\Connector\Auth;
4
5
use Illuminate\Http\Request;
6
use Illuminate\Auth\GuardHelpers;
7
use Illuminate\Support\Facades\Cookie;
8
use Illuminate\Contracts\Auth\UserProvider;
9
10
/**
11
 * Class TokenGuard
12
 *
13
 * @package App\Auth
14
 */
15
class TokenGuard implements \Illuminate\Contracts\Auth\Guard
16
{
17
    use GuardHelpers;
18
19
    /**
20
     * @var Request
21
     */
22
    protected $request;
23
24
    /**
25
     * @var Client
26
     */
27
    protected $client;
28
29
    /**
30
     * @var AuthService
31
     */
32
    protected $authService;
33
34
    /**
35
     * The name of cookie parameter where bearer token stores.
36
     *
37
     * @var string
38
     */
39
    protected $authCookie = 'authKey';
40
41
    /**
42
     * The JWT token
43
     *
44
     * @var string
45
     */
46
    protected $token;
47
48
    /**
49
     * TokenGuard constructor.
50
     *
51
     * @param UserProvider $provider
52
     * @param Request $request
53
     * @param AuthService $authService
54
     * @param Client|null $client
55
     */
56
    public function __construct(
57
        UserProvider $provider,
58
        Request $request,
59
        AuthService $authService,
60
        Client $client = null
61
    )
62
    {
63
        $this->provider = $provider;
64
        $this->request = $request;
65
        $this->authService = $authService;
66
67
        $this->client = $client ?? new Client();
68
    }
69
70
    /**
71
     * Authenticate a user.
72
     *
73
     * @param string $email
74
     * @param string $password
75
     * @param bool $remember
76
     *
77
     * @return mixed
78
     *
79
     * @throws
80
     */
81
    public function login(string $email, string $password, bool $remember = false)
82
    {
83
        $this->client->request('login', compact('email', 'password', 'remember'));
84
85
        if(!$this->client->success()) {
86
            return false;
87
        }
88
89
        if(!$this->token = $this->client->getToken()) {
90
            return false;
91
        }
92
93
        $this->storeToken($this->token);
94
95
        return $this->token;
96
    }
97
98
    /**
99
     * Authenticate a user without the password.
100
     *
101
     * Warning! This method has implemented temporarily to make able to login users
102
     * who use Social Auth on 24Templates. MUST NOT be used in any other cases.
103
     *
104
     * @param string $email
105
     * @param string $password
106
     * @param bool $remember
107
     *
108
     * @return mixed
109
     *
110
     * @throws
111
     */
112
    public function unsafeLogin(string $email, bool $remember = false)
113
    {
114
        $this->client->request('unsafeLogin', compact('email', 'remember'));
115
116
        if(!$this->client->success()) {
117
            return false;
118
        }
119
120
        if(!$this->token = $this->client->getToken()) {
121
            return false;
122
        }
123
124
        $this->storeToken($this->token);
125
126
        return $this->token;
127
    }
128
129
    /**
130
     * Get the currently authenticated user.
131
     *
132
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
133
     */
134
    public function user()
135
    {
136
        // If we've already retrieved the user for the current request we can just
137
        // return it back immediately. We do not want to fetch the user data on
138
        // every call to this method because that would be tremendously slow.
139
        if (! is_null($this->user)) {
140
            return $this->user;
141
        }
142
143
        $user = null;
144
145
        if($token = $this->token()) {
146
            if(!$user = $this->retrieveUserFromToken($token)) {
147
                if(!$user = $this->syncInstantly()) {
148
                    $this->logout();
149
                }
150
            }
151
        }
152
153
        return $this->user = $user;
154
    }
155
156
    /**
157
     * Get the token for the current request.
158
     *
159
     * @return string|null
160
     */
161
    public function token()
162
    {
163
        if($this->token) {
164
            return $this->token;
165
        }
166
167
        $token = $this->request->cookie($this->authCookie);
168
169
        if (empty($token)) {
170
            $token = $this->request->bearerToken();
171
        }
172
173
        return $this->token = $token;
174
    }
175
176
    /**
177
     * Validate a user's credentials.
178
     *
179
     * @param  array  $credentials
180
     * @return bool
181
     */
182
    public function validate(array $credentials = [])
183
    {
184
        return true;
185
    }
186
187
    /**
188
     * Store a token
189
     *
190
     * @param string $token
191
     */
192
    private function storeToken(string $token)
193
    {
194
        $cookie = cookie($this->authCookie, $token);
195
196
        Cookie::queue($cookie);
197
    }
198
199
    /**
200
     * Retrieve a user model from the parsed JWT token.
201
     *
202
     * @param string $token
203
     *
204
     * @return \Illuminate\Contracts\Auth\Authenticatable|\Illuminate\Database\Query\Builder|null
205
     */
206
    private function retrieveUserFromToken(string $token)
207
    {
208
        try
209
        {
210
            $data = (array) \Firebase\JWT\JWT::decode($token, env('JWT_SECRET'), ['HS256']);
211
        }
212
        catch(\RuntimeException $e) {
213
            $this->logout();
214
215
            return null;
216
        }
217
218
        if(!$userId = array_get($data, 'userId')) {
219
            return null;
220
        }
221
222
        return $this->provider->retrieveByCredentials(['remote_id' => $userId]);
223
    }
224
225
    /**
226
     * Trying to synchronize a user instantly by requesting using a token.
227
     *
228
     * If a user exists remotely, creates locally.
229
     *
230
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
231
     */
232
    private function syncInstantly()
233
    {
234
        if($user = $this->authService->retrieveByToken()) {
235
            return $this->authService->handle('sync.create', ['remote' => $user]);
236
        }
237
238
        return null;
239
    }
240
241
    /**
242
     * Invalidate a cookie
243
     *
244
     * @return void
245
     */
246
    public function logout()
247
    {
248
        Cookie::queue(Cookie::forget($this->authCookie));
249
    }
250
}