Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 366
0.00% covered (danger)
0.00%
0 / 24
CRAP
0.00% covered (danger)
0.00%
0 / 1
LoginController
0.00% covered (danger)
0.00%
0 / 366
0.00% covered (danger)
0.00%
0 / 24
4422
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 login
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 attemptLogin
0.00% covered (danger)
0.00%
0 / 70
0.00% covered (danger)
0.00%
0 / 1
210
 generateJwt
0.00% covered (danger)
0.00%
0 / 28
0.00% covered (danger)
0.00%
0 / 1
2
 base64UrlEncode
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 getUser
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 validateCompanyLicense
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 getLoginMeta
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 sendLockoutResponse
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
2
 logout
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
6
 getOAuthClient
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 refresh
0.00% covered (danger)
0.00%
0 / 64
0.00% covered (danger)
0.00%
0 / 1
72
 getSubscriptionsData
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
6
 getSubscriptionsStatus
0.00% covered (danger)
0.00%
0 / 32
0.00% covered (danger)
0.00%
0 / 1
20
 getProductById
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 getProductByName
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
12
 getInvoiceById
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 setNewSubscription
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
6
 setCancelSubscription
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 storeSubscription
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
6
 extensionSync
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
12
 decodeJwt
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
 loginExtension
0.00% covered (danger)
0.00%
0 / 37
0.00% covered (danger)
0.00%
0 / 1
72
 masqueradeUser
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace App\Http\Controllers\v1\Auth;
4
5use App\Http\Models\RefreshTokenTemp;
6use Illuminate\Support\Facades\Cache;
7use Stripe\StripeClient;
8use Illuminate\Http\Request;
9use App\Events\User\LoggedIn;
10use Illuminate\Http\Response;
11use App\Events\User\LoggedOut;
12use App\Http\Models\Auth\User;
13use App\Http\Models\Subscription;
14use Illuminate\Http\JsonResponse;
15use App\Http\Controllers\Controller;
16use App\Http\Models\Passport\Client;
17use Illuminate\Support\Facades\Lang;
18use Illuminate\Support\Facades\Config;
19use App\Http\Models\Admin\AdminUserInvitation;
20use App\Http\Models\Admin\CompanyLicenses;
21use App\Http\Models\UserInfo;
22use App\Http\Requests\MasqueradeUserFormRequest;
23use App\Http\Services\Admin\Users\AdminUsersService;
24use App\Services\UserInfo\UserInfoService;
25use Illuminate\Foundation\Auth\AuthenticatesUsers;
26use Carbon\Carbon;
27use Illuminate\Support\Facades\Auth;
28use Illuminate\Support\Facades\Log;
29
30class LoginController extends Controller
31{
32    use AuthenticatesUsers;
33
34    public function __construct(
35        private AdminUsersService $adminUsersService,
36        private UserInfoService $userInfoService
37    ) {}
38
39    /**
40     * @OA\Post  (
41     *     tags={"FlyMSG Project"},
42     *     path="/romeo/api/v1/user/auth/login",
43     *     summary="user login",
44     *
45     *     @OA\RequestBody(
46     *         required=true,
47     *
48     *         @OA\MediaType(
49     *             mediaType="application/json",
50     *
51     *             @OA\Schema(
52     *                 type="object",
53     *
54     *                 @OA\Property(
55     *                     property="email",
56     *                     description="User email",
57     *                     type="string",
58     *                     example="ihamzehald@gmail.com"
59     *                 ),
60     *                 @OA\Property(
61     *                     property="password",
62     *                     description="User password",
63     *                     type="string",
64     *                     example="larapoints123"
65     *                 ),
66     *             )
67     *         )
68     *     ),
69     *
70     *     @OA\Response(
71     *          response="401",
72     *          description="Unauthorized",
73     *          content={
74     *
75     *             @OA\MediaType(
76     *                 mediaType="application/json",
77     *
78     *                 @OA\Schema(
79     *
80     *                     @OA\Property(
81     *                         property="error",
82     *                         type="string",
83     *                         description="Error message",
84     *                     ),
85     *                     @OA\Property(
86     *                         property="max_attempts",
87     *                         type="string",
88     *                         description="Max attempts"
89     *                     ),
90     *                     @OA\Property(
91     *                         property="failed_attempts",
92     *                         type="string",
93     *                         description="Failed attempts",
94     *                     ),
95     *                     example={
96     *                          "error": "These credentials do not match our records.",
97     *                          "max_attempts": 5,
98     *                          "failed_attempts": "1"
99     *                     }
100     *                 )
101     *             )
102     *         }
103     *     ),
104     *
105     *  @OA\Response(
106     *         response="200",
107     *         description="ok",
108     *         content={
109     *
110     *             @OA\MediaType(
111     *                 mediaType="application/json",
112     *
113     *                 @OA\Schema(
114     *
115     *                     @OA\Property(
116     *                         property="access_token",
117     *                         type="string",
118     *                         description="JWT access token"
119     *                     ),
120     *                     @OA\Property(
121     *                         property="token_type",
122     *                         type="string",
123     *                         description="Token type"
124     *                     ),
125     *                     @OA\Property(
126     *                         property="expires_in",
127     *                         type="array",
128     *                         description="Token expiration in miliseconds",
129     *
130     *                         @OA\Items
131     *                     ),
132     *                     example={
133     *                          "result": {
134     *                              "token_type": "Bearer",
135     *                              "expires_in": 86399,
136     *                              "access_token": "eyJ...",
137     *                              "session_expires_in": 172800,
138     *                              "user_details": {
139     *                                  "first_name": "JorgeOscar",
140     *                                   "last_name": "Abrehu",
141     *                                   "email": "jabrehu@gmail.com",
142     *                                   "signup_source": "email",
143     *                                   "updated_at": 1647998250,
144     *                                   "created_at": 1647266869,
145     *                                   "last_login": "623a752a9b3ba409cd44d934",
146     *                                   "id": "622f4c3517e1a60b263ef7d2"
147     *                              }
148     *                          },
149     *                          "subscription_details": null
150     *                     }
151     *                 )
152     *             )
153     *         }
154     *      ),
155     * )
156     */
157    public function login(Request $request)
158    {
159        $this->validateLogin($request);
160        if (
161            method_exists($this, 'hasTooManyLoginAttempts') &&
162            $this->hasTooManyLoginAttempts($request)
163        ) {
164            $this->fireLockoutEvent($request);
165
166            return $this->sendLockoutResponse($request);
167        }
168
169        return $this->attemptLogin($request);
170    }
171
172    public function attemptLogin(Request $request): JsonResponse
173    {
174        $username = $request->get($this->username());
175        $password = $request->get('password');
176
177        $user = $this->getUser($username);
178
179        if (filled($user?->company_id) && !$this->validateCompanyLicense($user->company_id)) {
180            return response()->json([
181                "code" => "NO_ACTIVE_LICENSE",
182                "error" => "The company doesn't have any active license.",
183                "message" => "The company doesn't have any active license. Please contact the administrator."
184            ], Response::HTTP_UNPROCESSABLE_ENTITY);
185        }
186
187        if (filled($user?->deleted_at) || filled($user?->deactivated_at)) {
188            return response()->json([
189                "code" => "DEACTIVATED",
190                "error" => "Account is deactivated",
191                "message" => "Account is deactivated",
192                "admin_email" =>  $user?->company?->pocs()?->first()?->email
193            ], Response::HTTP_UNPROCESSABLE_ENTITY);
194        }
195
196        $invitation = AdminUserInvitation::firstWhere('email', $username);
197
198        //If user is invited, hasn't reset password and temporary password is expired
199        if (filled($invitation) && (now()->greaterThan($user?->temp_password_expiry) || now()->greaterThan($invitation?->temp_password_expiry))) {
200            return response()->json([
201                "code" => "EXPIRED_LOGIN",
202                "error" => "The invitation has expired. Please generate a new one.",
203                "message" => "The invitation has expired. Please generate a new one."
204            ], Response::HTTP_UNPROCESSABLE_ENTITY);
205        }
206
207        if (!$user && $invitation) {
208            $user = $this->adminUsersService->createUserForInvitationLogin($invitation);
209        }
210
211        $client = $this->getOAuthClient();
212        $authRequest = Request::create(route('passport.token'), 'POST', [
213            'grant_type' => 'password',
214            'client_id' => $client->id,
215            'client_secret' => $client->secret,
216            'username' => $username,
217            'password' => $password,
218            'scope' => '',
219        ]);
220
221        $response = retry(3, function () use ($authRequest) {
222            return app()->handle($authRequest);
223        }, 100);
224        // $response = app()->handle($authRequest);
225        $data = json_decode($response->getContent(), true);
226
227        if (empty($data) || isset($data['error']) || !isset($data['access_token'])) {
228            $this->incrementLoginAttempts($request);
229
230            $data = array_merge($data ?? [], [
231                'error' => $data['error'] ?? trans('auth.failed'),
232                'max_attempts' => $this->maxAttempts(),
233                'failed_attempts' => $this->limiter()->attempts($this->throttleKey($request)),
234                'data' => $data,
235                'response' => $response
236            ]);
237
238            return response()->json($data, Response::HTTP_UNPROCESSABLE_ENTITY);
239        }
240
241        $this->clearLoginAttempts($request);
242
243        $user->is_poc = $user->isPOC();
244
245        $data = array_merge($data, [
246            'session_expires_in' => intval(
247                config('auth.passport.refresh_token_expiry')
248            ),
249            'user_details' => $user,
250            'company' => $user?->company?->slug
251        ]);
252
253        $requireExtension = $request->get('include_extension');
254        if (!$requireExtension) {
255            $request = Request::create(config('app.url') . '/romeo/api/v1/user/auth/login', 'POST', [
256                'email' => $request->email,
257                'password' => $request->password,
258                'include_extension' => true
259            ]);
260
261            $response = app()->handle($request);
262            $extensionData = json_decode($response->getContent(), true);
263
264            $data['extension'] = $extensionData['result'];
265        }
266
267        LoggedIn::dispatch($user, ['email' => $user['email'], 'signin_source' => 'email']);
268
269        Auth::guard('web')->login($user);
270
271        return response()->json($data);
272    }
273
274    protected function generateJwt($user)
275    {
276        // Header
277        $header = json_encode([
278            'typ' => 'JWT',
279            'alg' => 'HS256',
280        ]);
281        $base64UrlHeader = $this->base64UrlEncode($header);
282
283        // Payload
284        $now = time();
285        $common = [
286            'iat' => $now,
287            'jti' => md5($now . rand()),
288        ];
289
290        $secret = 'GPn5ch5eLuyKXrtwDcesqkY8NQzYzDHm3HNdmTHAZ08=';
291        $companyName = 'vengreso';
292
293        $user_attributes = [
294            'user_external_id' => $user['id'],
295            'user_email' => $user['email'],
296            'user_first_name' => $user['first_name'],
297            'user_last_name' => $user['last_name'],
298            'company_external_id' => $user['id'],
299            'company_name' => $companyName . '_' . $user['email'],
300            'company_website' => 'https://' . $companyName . $user['email'] . '.com',
301        ];
302
303        $payload = json_encode(array_merge($common, $user_attributes));
304        $base64UrlPayload = $this->base64UrlEncode($payload);
305
306        // JWT
307        $message = $base64UrlHeader . '.' . $base64UrlPayload;
308        $signature = hash_hmac('sha256', $message, $secret, true);
309        $base64UrlSignature = $this->base64UrlEncode($signature);
310        $jwt = $base64UrlHeader . '.' . $base64UrlPayload . '.' . $base64UrlSignature;
311
312        return $jwt;
313    }
314
315    protected function base64UrlEncode($text)
316    {
317        return str_replace(
318            ['+', '/', '='],
319            ['-', '_', ''],
320            base64_encode($text)
321        );
322    }
323
324    protected function getUser($username)
325    {
326        return User::withTrashed()->firstWhere($this->username(), $username);
327    }
328
329    private function validateCompanyLicense(string $companyId): Bool
330    {
331        $companyLicense = CompanyLicenses::where('company_id', $companyId)->active()->first();
332
333        return !$companyLicense ? false : true;
334    }
335
336    protected function getLoginMeta()
337    {
338        return [];
339    }
340
341    protected function sendLockoutResponse(Request $request)
342    {
343        $seconds = $this->limiter()->availableIn(
344            $this->throttleKey($request)
345        );
346
347        $response = [
348            'error' => Lang::get('auth.throttle', [
349                'seconds' => $seconds,
350                'minutes' => ceil($seconds / 60),
351            ]),
352        ];
353
354        return response()->json($response, Response::HTTP_TOO_MANY_REQUESTS);
355    }
356
357    public function logout(Request $request): JsonResponse
358    {
359        $user = $request->user();
360        $user->token()->revoke();
361        $user->token()->delete();
362
363        $data = $request->validate([
364            'description' => 'sometimes',
365            'selectedOption' => 'sometimes'
366        ]);
367
368        LoggedOut::dispatch(
369            $user,
370            $request->has(['description', 'selectedOption']) ? $data : null
371        );
372
373        Auth::guard('web')->logout();
374
375        return response()->json(['message' => 'User has been logged out.']);
376    }
377
378    protected function getOAuthClient()
379    {
380        return Client::firstWhere('password_client', true);
381    }
382
383    public function refresh(Request $request): JsonResponse
384    {
385        $method = $request->method();
386        $url = $request->url();
387        $headers = $request->headers->all();
388        $params = $request->all();
389
390        if ($method != 'GET') {
391            $headers = json_encode($request->headers->all());
392            $params = json_encode($request->all());
393        }
394
395        Log::info('HTTP Request: ' . $method . ' ' . $url, [
396            'headers' => $headers,
397            'params' => $params,
398            'user' => $request->user()
399        ]);
400
401        $request->validate([
402            'refresh_token' => 'required|string',
403        ]);
404
405        $refreshToken = $request->get('refresh_token');
406
407        $data = Cache::get($refreshToken);
408        $exists = !empty($data);
409
410        if (empty($data)) {
411            // wait 3 seconds and try again
412            sleep(3);
413
414            $data = Cache::get($refreshToken);
415            $exists = !empty($data);
416
417            if (empty($data)) {
418                $client = $this->getOAuthClient();
419                $authData = [
420                    'grant_type' => 'refresh_token',
421                    'client_id' => $client->id,
422                    'client_secret' => $client->secret,
423                    'refresh_token' => $refreshToken,
424                    'scope' => '',
425                ];
426
427                $authRequest = Request::create(route('passport.token'), 'POST', $authData);
428                $response = app()->handle($authRequest);
429                $data = json_decode($response->getContent(), true);
430
431                Cache::set($refreshToken, $data, now()->addDays(15));
432            }
433        }
434
435        if (!empty($data)) {
436            $sessionExpiry = Config::get('auth.passport.refresh_token_expiry');
437            $data['session_expires_in'] = intval($sessionExpiry);
438
439            if ($exists) {
440                $dbRefreshToken = RefreshTokenTemp::where('refresh_token', $refreshToken)->first();
441                if ($dbRefreshToken) {
442                    $dbRefreshToken->times_used = $dbRefreshToken->times_used + 1;
443                    $dbRefreshToken->save();
444                } else {
445                    RefreshTokenTemp::create([
446                        'refresh_token' => $refreshToken,
447                        'data' => $data,
448                        'times_used' => 1,
449                    ]);
450                }
451            } else {
452                RefreshTokenTemp::create([
453                    'refresh_token' => $refreshToken,
454                    'data' => $data,
455                    'times_used' => 1,
456                ]);
457            }
458
459            return response()->json($data);
460        } else {
461            $dbRefreshToken = RefreshTokenTemp::where('refresh_token', $refreshToken)->first();
462            if ($dbRefreshToken) {
463                $dbRefreshToken->times_error = $dbRefreshToken->times_error + 1;
464                $dbRefreshToken->save();
465            } else {
466                RefreshTokenTemp::create([
467                    'refresh_token' => $refreshToken,
468                    'data' => $data,
469                    'times_error' => 1,
470                ]);
471            }
472
473            return response()->json($data, Response::HTTP_UNAUTHORIZED);
474        }
475    }
476
477    public function getSubscriptionsData($user_id)
478    {
479        $result = null;
480        $record = Subscription::where(['user_id' => $user_id])->orderBy('created_at', 'desc')->limit(1)->first();
481        if ($record) {
482            $status_plan = $this->getSubscriptionsStatus($record, $user_id);
483            $result['subscription_id'] = $record['subscription_id'];
484            $result['custormer_id'] = $record['custormer_id'];
485            $result['price_id'] = $record['price_id'];
486            $result['status_plan'] = $status_plan;
487            $result['subscription_data'] = json_decode($record['subscription_data'], false);
488        }
489
490        return $result;
491    }
492
493    public function getSubscriptionsStatus($subscription, $user_id): array
494    {
495        $result = [];
496        $stripe_secret = config('services.stripe.secret');
497        $stripe = new StripeClient($stripe_secret);
498        $subscription_details = $stripe->subscriptions->retrieve($subscription['subscription_id']);
499        $current_period_end = $subscription_details['current_period_end'];
500        $latest_invoice = $subscription_details['latest_invoice'];
501        $status = $subscription_details['status'];
502        $product_id = $subscription_details['items']['data'][0]['plan']['product'];
503        $product = $this->getProductById($product_id);
504        $invoice = $this->getInvoiceById($latest_invoice);
505        $freeProduct = $this->getProductByName('Free');
506        $result = [
507            'plan' => $product['name'],
508            'status' => $status,
509            'last_payment_date' => $invoice['created'],
510            'current_period_end' => $current_period_end,
511            'older_plan' => '',
512            'log_message' => $freeProduct,
513        ];
514        if ($status === 'canceled' or $status === 'unpaid' or $status === 'incomplete_expired') {
515            // create new subscription
516            $newSubscription = $this->setNewSubscription($subscription_details, $freeProduct, $user_id);
517            // cancel current subscription
518            $isCancel = $this->setCancelSubscription($subscription_details);
519            $current_period_end = $newSubscription['current_period_end'];
520            $result = [
521                'plan' => $freeProduct['name'],
522                'status' => $status,
523                'last_payment_date' => null,
524                'current_period_end' => $current_period_end,
525                'older_plan' => $product['name'],
526                'log_message' => 'Downgrade to the free plan.',
527            ];
528        }
529
530        return $result;
531    }
532
533    public function getProductById($product_id): \Stripe\Product
534    {
535        $stripe_secret = config('services.stripe.secret');
536        $stripe = new StripeClient($stripe_secret);
537
538        return $stripe->products->retrieve($product_id);
539    }
540
541    public function getProductByName($product_name)
542    {
543        $result = [];
544        $stripe_secret = config('services.stripe.secret');
545        $stripe = new StripeClient($stripe_secret);
546        $products = $stripe->products->all(['limit' => 10]);
547        foreach ($products as $product) {
548            if ($product['name'] === 'Free') {
549                $price = $stripe->prices->all(['limit' => 1, 'product' => $product['id']]);
550                $result = $product;
551                $result['price'] = $price;
552            }
553        }
554
555        return $result;
556    }
557
558    public function getInvoiceById($invoice_id): \Stripe\Invoice
559    {
560        $stripe_secret = config('services.stripe.secret');
561        $stripe = new StripeClient($stripe_secret);
562
563        return $stripe->invoices->retrieve($invoice_id);
564    }
565
566    private function setNewSubscription(\Stripe\Subscription $subscription_details, \Stripe\Product $freeProduct, $user_id): \Stripe\Subscription
567    {
568        try {
569            $stripe_secret = config('services.stripe.secret');
570            $stripe = new StripeClient($stripe_secret);
571            $data = [
572                'customer' => $subscription_details['customer'],
573                'items' => [
574                    ['price' => $freeProduct['price']['data'][0]['id']],
575                ],
576            ];
577            $newSubscription = $stripe->subscriptions->create($data);
578            $this->storeSubscription($newSubscription, $subscription_details['customer'], $user_id);
579            $result = $newSubscription;
580        } catch (\Exception $e) {
581            $result = [];
582        }
583
584        return $result;
585    }
586
587    private function setCancelSubscription(\Stripe\Subscription $subscription_details): bool
588    {
589        $result = true;
590        try {
591            $stripe_secret = config('services.stripe.secret');
592            $stripe = new StripeClient($stripe_secret);
593            $stripe->subscriptions->cancel($subscription_details['id'], []);
594        } catch (\Exception $e) {
595            $result = false;
596        }
597
598        return $result;
599    }
600
601    private function storeSubscription($subscription, $customer, $user_id): bool
602    {
603        try {
604            $subscriptionToString = json_encode($subscription);
605            $customerToString = json_encode($customer);
606            $newSubscription = new Subscription();
607            $newSubscription->subscription_id = $subscription['id'];
608            $newSubscription->user_id = $user_id;
609            $newSubscription->custormer_id = $customer;
610            $newSubscription->price_id = $subscription['items']['data'][0]['price']['id'];
611            $newSubscription->subscription_data = $subscriptionToString;
612            $newSubscription->customer_data = $customerToString;
613            $result = $newSubscription->save();
614        } catch (\Exception $e) {
615            $result = false;
616        }
617
618        return $result;
619    }
620
621    public function extensionSync(Request $request): JsonResponse
622    {
623        $access_token = $request->get('accessToken');
624        $refresh_token = $request->get('refreshToken');
625        $email = $request->get('email');
626        $hubspot_id = $request->get('hubspot_id');
627
628        if ($access_token && $refresh_token) {
629            $data['access_token'] = $access_token;
630            $data['refresh_token'] = $refresh_token;
631            $data['user_details']['email'] = $email;
632            $data['user_details']['hubspot_id'] = $hubspot_id;
633
634            return response()->json($data);
635        }
636
637        return response()->json(['error' => true]);
638    }
639
640    private function decodeJwt($jwt)
641    {
642        $tokenParts = explode('.', $jwt);
643        $header = $tokenParts[0];
644        $payload = $tokenParts[1];
645
646        $decodedHeader = json_decode(base64_decode(strtr($header, '-_', '+/')), true);
647        $decodedPayload = json_decode(base64_decode(strtr($payload, '-_', '+/')), true);
648
649        return [
650            'header' => $decodedHeader,
651            'payload' => $decodedPayload,
652        ];
653    }
654
655    public function loginExtension(Request $request)
656    {
657        $user = $request->user();
658        $tokenResult = $user->createToken('AdminGeneratedToken');
659        $tokenResult->token->expires_at = Carbon::now()->addYear();
660        $tokenResult->token->save();
661        $user['is_poc'] = $user->isPOC();
662
663        $data = [
664            'token_type' => 'Bearer',
665            'access_token' => $tokenResult->accessToken,
666            'expires_in' => $tokenResult->token->expires_at->diffInSeconds(Carbon::now()),
667            'session_expires_in' => intval(
668                config('auth.passport.refresh_token_expiry')
669            ),
670            'user_details' => $user,
671            'company' => $user?->company?->slug
672        ];
673
674        if ($request->header('Browser-type')) {
675            $browser_type = strtolower($request->header('Browser-type'));
676        }
677        if ($request->header('browser-type')) {
678            $browser_type = strtolower($request->header('browser-type'));
679        }
680        if ($request['browser']) {
681            $browser_type = strtolower($request['browser']);
682        }
683
684        if (!empty($browser_type)) {
685            $browser_type = strtolower($browser_type);
686
687            $userInfo = UserInfo::where('email', $user->email)->first();
688            if ($userInfo) {
689                $props = $this->userInfoService->addExtension($userInfo, $browser_type, now());
690
691                $chromeVersion = $request->header('flymsg_extension_version_for_chrome') ?? $request->header('Flymsg_extension_version_for_chrome') ?? $request->header('flymsgextensionversionforchrome') ?? $request->header('flymsgExtensionVersionForChrome');
692                $edgeVersion = $request->header('flymsg_extension_version_for_edge') ?? $request->header('Flymsg_extension_version_for_edge') ?? $request->header('flymsgextensionversionforedge') ?? $request->header('flymsgExtensionVersionForEdge');
693
694                if (empty($chromeVersion) && empty($edgeVersion)) {
695                    Log::info('Extension version not found in headers. User: ' . $user->email, [
696                        'user' => $user,
697                        'headers' => $request->header()
698                    ]);
699                }
700
701                $props['flymsg_extension_version_for_edge'] = $edgeVersion;
702                $props['flymsg_extension_version_for_chrome'] = $chromeVersion;
703
704                $userInfo->update($props);
705            }
706        }
707
708        return response()->json($data);
709    }
710
711    public function masqueradeUser(MasqueradeUserFormRequest $request, User $user)
712    {
713        $tokenResult = $user->createToken('AdminGeneratedToken');
714        $tokenResult->token->expires_at = Carbon::now()->addDay();
715        $tokenResult->token->save();
716        $user['is_poc'] = $user->isPOC();
717
718        $data = [
719            'token_type' => 'Bearer',
720            'access_token' => $tokenResult->accessToken,
721            'expires_in' => $tokenResult->token->expires_at->diffInSeconds(Carbon::now()),
722            'session_expires_in' => intval(
723                config('auth.passport.refresh_token_expiry')
724            ),
725            'user_details' => $user,
726            'company' => $user?->company?->slug
727        ];
728
729        return response()->json($data);
730    }
731}