AuthService.php 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. <?php
  2. namespace App\Services;
  3. use App\Models\User;
  4. use App\Models\PersonalAccessToken;
  5. use App\Enums\UserTypeEnum;
  6. use App\Enums\ApprovalStatusEnum;
  7. use App\Models\Provider;
  8. use Carbon\Carbon;
  9. use Illuminate\Support\Facades\Auth;
  10. use Illuminate\Support\Facades\DB;
  11. use Illuminate\Support\Facades\Log;
  12. use Illuminate\Support\Str;
  13. class AuthService
  14. {
  15. public function __construct(
  16. private readonly EmailService $emailService,
  17. ) {}
  18. public function login(string $email, string $password): ?array
  19. {
  20. $user = User::where('email', $email)->first();
  21. if (!$user || !in_array($user->type, [UserTypeEnum::ADMIN, UserTypeEnum::USER])) {
  22. return null;
  23. }
  24. if (!Auth::attempt(['email' => $email, 'password' => $password])) {
  25. return null;
  26. }
  27. // $user = User::where('email', $email)->first();
  28. $deviceId = Str::uuid()->toString();
  29. $accessToken = $user->createAccessToken($deviceId);
  30. $refreshToken = $user->createRefreshToken($deviceId);
  31. return [
  32. 'payload' => [
  33. 'access_token' => $accessToken,
  34. 'user' => $user,
  35. ],
  36. 'refreshToken' => $refreshToken,
  37. ];
  38. }
  39. public function refresh(string $refreshToken): ?array
  40. {
  41. if (!$refreshToken) {
  42. return null;
  43. }
  44. $tokenModel = PersonalAccessToken::findToken($refreshToken);
  45. if (
  46. !$tokenModel ||
  47. !in_array("refresh", $tokenModel->abilities) ||
  48. $tokenModel->expires_at < now()
  49. ) {
  50. return null;
  51. }
  52. $user = $tokenModel->tokenable;
  53. if (!$user) {
  54. return null;
  55. }
  56. $deviceId = Str::afterLast($tokenModel->name, "_");
  57. $tokens = $this->refreshTokenTransaction($tokenModel, $user, $deviceId);
  58. return [
  59. "payload" => [
  60. "access_token" => $tokens["access_token"],
  61. "user" => $user,
  62. ],
  63. "refreshToken" => $tokens["refresh_token"],
  64. ];
  65. }
  66. public function logout(): void
  67. {
  68. $user = Auth::user();
  69. if (!$user) {
  70. return;
  71. }
  72. $tokenName = $user->currentAccessToken()->name;
  73. $deviceId = Str::afterLast($tokenName, "_");
  74. $user
  75. ->tokens()
  76. ->where("name", "like", "%_{$deviceId}")
  77. ->delete();
  78. }
  79. protected function refreshTokenTransaction(
  80. PersonalAccessToken $tokenModel,
  81. User $user,
  82. string $deviceId,
  83. ): array {
  84. return DB::transaction(function () use (
  85. $tokenModel,
  86. $user,
  87. $deviceId,
  88. ): array {
  89. $tokenModel->update(["expires_at" => Carbon::now()]);
  90. $accessToken = $user->createAccessToken($deviceId);
  91. $refreshToken = $user->createRefreshToken($deviceId);
  92. return [
  93. "access_token" => $accessToken,
  94. "refresh_token" => $refreshToken,
  95. ];
  96. });
  97. }
  98. public function clientSendCode(array $data): bool|array|null
  99. {
  100. try {
  101. DB::beginTransaction();
  102. $code = str_pad((string) random_int(0, 999999), 6, '0', STR_PAD_LEFT);
  103. $user = User::where(function ($query) use ($data) {
  104. $query->when(!empty($data['email']), function ($q) use ($data) {
  105. $q->where('email', $data['email']);
  106. })
  107. ->when(!empty($data['phone']), function ($q) use ($data) {
  108. $q->where('phone', $data['phone']);
  109. });
  110. })
  111. ->first();
  112. $isLogin = false;
  113. if ($user) {
  114. if ($user->type->value !== UserTypeEnum::CLIENT->value) {
  115. DB::rollBack();
  116. return ['error' => 'wrong_user_type'];
  117. }
  118. $user->code = $code;
  119. $user->validated_code = false;
  120. $user->save();
  121. $isLogin = true;
  122. } else {
  123. $user = new User();
  124. $user->fill($data);
  125. $user->code = $code;
  126. $user->name = $data['name'] ?? 'Usuário';
  127. $user->type = $data['type'] ?? UserTypeEnum::CLIENT->value;
  128. $user->save();
  129. }
  130. if (!empty($data['email'])) {
  131. $this->emailService->sendVerificationCode(
  132. email: $data['email'],
  133. code: $code,
  134. recipientName: $data['name'] ?? '',
  135. );
  136. } elseif (!empty($data['phone'])) {
  137. Log::info('SMS: envio de código por telefone ainda não implementado.', [
  138. 'phone' => $data['phone'],
  139. ]);
  140. }
  141. DB::commit();
  142. return $isLogin;
  143. } catch (\Exception $e) {
  144. DB::rollBack();
  145. Log::error('Erro ao enviar código de verificação.', [
  146. 'error' => $e->getMessage(),
  147. 'data' => $data,
  148. ]);
  149. return false;
  150. }
  151. }
  152. public function providerSendCode(array $data): bool|array|null
  153. {
  154. try {
  155. DB::beginTransaction();
  156. $code = str_pad((string) random_int(0, 999999), 6, '0', STR_PAD_LEFT);
  157. $user = User::where(function ($query) use ($data) {
  158. $query->when(!empty($data['email']), function ($q) use ($data) {
  159. $q->where('email', $data['email']);
  160. })
  161. ->when(!empty($data['phone']), function ($q) use ($data) {
  162. $q->where('phone', $data['phone']);
  163. });
  164. })
  165. ->first();
  166. $isLogin = false;
  167. if ($user) {
  168. if ($user->type->value !== UserTypeEnum::PROVIDER->value) {
  169. DB::rollBack();
  170. return ['error' => 'wrong_user_type'];
  171. }
  172. $provider = Provider::where('user_id', $user->id)->first();
  173. if($provider && $provider->approval_status->value !== ApprovalStatusEnum::ACCEPTED->value) {
  174. DB::rollBack();
  175. return ['error' => 'provider_not_accepted'];
  176. }
  177. $user->code = $code;
  178. $user->validated_code = false;
  179. $user->save();
  180. $isLogin = true;
  181. } else {
  182. $user = new User();
  183. $user->fill($data);
  184. $user->code = $code;
  185. $user->name = $data['name'] ?? 'Usuário';
  186. $user->type = $data['type'] ?? UserTypeEnum::PROVIDER->value;
  187. $user->save();
  188. }
  189. if (!empty($data['email'])) {
  190. $this->emailService->sendVerificationCode(
  191. email: $data['email'],
  192. code: $code,
  193. recipientName: $data['name'] ?? '',
  194. );
  195. } elseif (!empty($data['phone'])) {
  196. Log::info('SMS: envio de código por telefone ainda não implementado.', [
  197. 'phone' => $data['phone'],
  198. ]);
  199. }
  200. DB::commit();
  201. return $isLogin;
  202. } catch (\Exception $e) {
  203. DB::rollBack();
  204. Log::error('Erro ao enviar código de verificação.', [
  205. 'error' => $e->getMessage(),
  206. 'data' => $data,
  207. ]);
  208. return false;
  209. }
  210. }
  211. public function validateCodeClient(array $data, bool $isLogin): bool|array
  212. {
  213. $email = $data['email'] ?? null;
  214. $phone = $data['phone'] ?? null;
  215. $code = $data['code'] ?? '';
  216. $user = User::where(function ($query) use ($email, $phone) {
  217. $query->when($email, fn($q) => $q->where('email', $email))
  218. ->when($phone, fn($q) => $q->where('phone', $phone));
  219. })
  220. ->where('code', $code)
  221. ->first();
  222. if (!$user) {
  223. return false;
  224. }
  225. if ($isLogin) {
  226. return $this->loginWithEmail($user->email, $code);
  227. }
  228. return true;
  229. }
  230. public function validateCodeProvider(array $data, bool $isLogin): bool|array
  231. {
  232. $email = $data['email'] ?? null;
  233. $phone = $data['phone'] ?? null;
  234. $code = $data['code'] ?? '';
  235. $user = User::where(function ($query) use ($email, $phone) {
  236. $query->when($email, fn($q) => $q->where('email', $email))
  237. ->when($phone, fn($q) => $q->where('phone', $phone));
  238. })
  239. ->where('code', $code)
  240. ->first();
  241. if (!$user) {
  242. return false;
  243. }
  244. if ($isLogin) {
  245. $user->load('provider');
  246. $provider = $user->provider ?? null;
  247. if ($provider && $provider->approval_status === ApprovalStatusEnum::PENDING->value) {
  248. return ['error' => 'provider_pending'];
  249. }
  250. if ($provider && $provider->approval_status === ApprovalStatusEnum::REJECTED->value) {
  251. return ['error' => 'provider_rejected'];
  252. }
  253. return $this->loginWithEmail($user->email, $code);
  254. }
  255. return true;
  256. }
  257. public function validateCode(array $data, bool $isLogin): bool|array
  258. {
  259. $email = $data['email'] ?? null;
  260. $phone = $data['phone'] ?? null;
  261. $code = $data['code'] ?? '';
  262. $user = User::where(function ($query) use ($email, $phone) {
  263. $query->when($email, function ($q) use ($email) {
  264. $q->where('email', $email);
  265. })
  266. ->when($phone, function ($q) use ($phone) {
  267. $q->where('phone', $phone);
  268. });
  269. })
  270. ->where('code', $code)
  271. ->first();
  272. if (!$user) {
  273. return false;
  274. }
  275. if($isLogin) {
  276. $resultLogin = $this->loginWithEmail($user->email, $code);
  277. return $resultLogin;
  278. }
  279. return true;
  280. }
  281. public function loginWithEmail(string $email, string $code): ?array
  282. {
  283. $user = User::where('email', $email)
  284. ->where('code', $code)
  285. ->first();
  286. if (!$user) {
  287. return null;
  288. }
  289. $deviceId = Str::uuid()->toString();
  290. $accessToken = $user->createAccessTokenApp($deviceId);
  291. $refreshToken = $user->createRefreshTokenApp($deviceId);
  292. $user->validated_code = true;
  293. $user->code = null;
  294. $user->save();
  295. return [
  296. "payload" => [
  297. "access_token" => $accessToken,
  298. "user" => $user,
  299. ],
  300. "refreshToken" => $refreshToken,
  301. ];
  302. }
  303. }