Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
96.55% covered (success)
96.55%
28 / 29
75.00% covered (warning)
75.00%
3 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
MasqueradeController
96.55% covered (success)
96.55%
28 / 29
75.00% covered (warning)
75.00%
3 / 4
10
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 generate
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
2
 generateCompanyToken
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
2
 start
90.91% covered (success)
90.91%
10 / 11
0.00% covered (danger)
0.00%
0 / 1
5.02
1<?php
2
3namespace App\Http\Controllers\v2\Admin;
4
5use App\Http\Controllers\Controller;
6use App\Http\Models\Admin\Company;
7use App\Http\Requests\v2\Masquerade\GenerateCompanyMasqueradeRequest;
8use App\Http\Requests\v2\Masquerade\GenerateMasqueradeRequest;
9use App\Http\Requests\v2\Masquerade\StartMasqueradeRequest;
10use App\Http\Services\Admin\MasqueradeService;
11use Firebase\JWT\ExpiredException;
12use Firebase\JWT\SignatureInvalidException;
13use Illuminate\Database\Eloquent\ModelNotFoundException;
14use Illuminate\Http\JsonResponse;
15
16/**
17 * Masquerade Controller
18 *
19 * Handles admin masquerade (impersonation) operations. Provides two endpoints:
20 * - generate: Creates a JWT containing the masqueraded user's access token and admin session backup
21 * - start: Decodes the JWT and returns session data for the frontend to switch contexts
22 */
23class MasqueradeController extends Controller
24{
25    public function __construct(
26        private MasqueradeService $masqueradeService
27    ) {}
28
29    /**
30     * Generate a masquerade JWT token.
31     *
32     * Creates a Passport token for the target user, bundles it with the admin's
33     * session backup into a signed JWT, and returns the token with a masquerade URL.
34     *
35     * @param  GenerateMasqueradeRequest  $request  Validated request with user_id, company_id
36     *
37     * @response 200 {
38     *   "result": {
39     *     "masquerade_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
40     *     "masquerade_url": "https://app.vengreso.com/masquerade?token=eyJ...",
41     *     "expires_at": 1642528800
42     *   }
43     * }
44     */
45    public function generate(GenerateMasqueradeRequest $request): JsonResponse
46    {
47        try {
48            $result = $this->masqueradeService->generate(
49                $request->user(),
50                $request->validated(),
51                $request
52            );
53
54            return response()->json($result);
55        } catch (ModelNotFoundException $e) {
56            return response()->json(['error' => 'User or company not found.'], 404);
57        }
58    }
59
60    /**
61     * Generate a masquerade JWT token for a company context.
62     *
63     * Creates a signed JWT with company metadata so the admin portal
64     * can switch into a company context. No access token swap is performed.
65     *
66     * @param  GenerateCompanyMasqueradeRequest  $request  Validated request (VENGRESO_ADMIN only)
67     * @param  string  $company  The target company ID (from route parameter)
68     *
69     * @response 200 {
70     *   "result": {
71     *     "masquerade_url": "https://admin.vengreso.com/masquerade/company?token=eyJ...",
72     *     "expires_at": 1642528800
73     *   }
74     * }
75     */
76    public function generateCompanyToken(GenerateCompanyMasqueradeRequest $request, string $company): JsonResponse
77    {
78        try {
79            $companyModel = Company::findOrFail($company);
80
81            $result = $this->masqueradeService->generateCompanyToken(
82                $request->user(),
83                $companyModel,
84                $request
85            );
86
87            return response()->json($result);
88        } catch (ModelNotFoundException $e) {
89            return response()->json(['error' => 'Company not found.'], 404);
90        }
91    }
92
93    /**
94     * Start a masquerade session by decoding the JWT.
95     *
96     * Validates the masquerade JWT from the Authorization Bearer header,
97     * decodes it, and returns the masqueraded user's session data along
98     * with the admin's backup session for restoration.
99     *
100     * @param  StartMasqueradeRequest  $request  Request with JWT in Authorization header
101     *
102     * @response 200 {
103     *   "result": {
104     *     "user": {
105     *       "token_type": "Bearer",
106     *       "access_token": "...",
107     *       "expires_in": 7200,
108     *       "session_expires_in": 2592000,
109     *       "user_details": {...},
110     *       "company": "company-slug"
111     *     },
112     *     "admin_backup": {
113     *       "access_token": "...",
114     *       "refresh_token": "...",
115     *       "user_id": "...",
116     *       "email": "admin@example.com"
117     *     },
118     *     "expires_at": 1642528800
119     *   }
120     * }
121     */
122    public function start(StartMasqueradeRequest $request): JsonResponse
123    {
124        $token = $request->bearerToken();
125
126        if (! $token) {
127            return response()->json(['error' => 'Masquerade token is required.'], 401);
128        }
129
130        try {
131            $result = $this->masqueradeService->start($token, $request);
132
133            return response()->json($result);
134        } catch (ExpiredException $e) {
135            return response()->json(['error' => 'Masquerade token has expired.'], 401);
136        } catch (SignatureInvalidException $e) {
137            return response()->json(['error' => 'Invalid masquerade token.'], 401);
138        } catch (\UnexpectedValueException $e) {
139            return response()->json(['error' => 'Invalid masquerade token.'], 401);
140        }
141    }
142}