You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

301 lines
9.0 KiB

  1. <?php
  2. namespace App\Http\Controllers;
  3. use App\Models\User;
  4. use App\Models\Business;
  5. use App\Models\Fingerprint;
  6. use App\Notifications\MailNotification;
  7. use Illuminate\Support\Facades\Notification;
  8. use Illuminate\Support\Str;
  9. use Illuminate\Http\Request;
  10. use Illuminate\Validation\Rule;
  11. use Illuminate\Http\JsonResponse;
  12. use App\Http\Resources\UserResource;
  13. use Illuminate\Support\Facades\Auth;
  14. use Illuminate\Support\Facades\Hash;
  15. use Illuminate\Support\Facades\Cache;
  16. use Laravel\Socialite\Facades\Socialite;
  17. use Illuminate\Session\TokenMismatchException;
  18. use phpDocumentor\Reflection\Type;
  19. use Symfony\Component\HttpFoundation\Response;
  20. class AuthController extends Controller
  21. {
  22. public function redirectToGoogle()
  23. {
  24. return Socialite::driver('google')->stateless()->redirect();
  25. }
  26. public function handleGoogleCallback(Request $request)
  27. {
  28. try {
  29. $user = Socialite::driver('google')->stateless()->user();
  30. $find_user = User::where('email', $user->email)->first();
  31. if ($find_user) {
  32. $find_user->update([
  33. 'active' => true
  34. ]);
  35. Auth::setUser($find_user);
  36. } else {
  37. $user = User::create($user->user + [
  38. 'password' => Hash::make('google-login-user'),
  39. 'username' => $user->email,
  40. 'active' => true
  41. ]);
  42. Auth::setUser($user);
  43. }
  44. $finger_print = $this->createFingerPrint();
  45. return redirect('http://localhost:3000/login?token='.$finger_print->token);
  46. } catch (Exception $e) {
  47. dd($e->getMessage());
  48. }
  49. }
  50. public function login(Request $request)
  51. {
  52. // todo: Logging in from a new device will result in sending a notification
  53. $this->validate($request, [
  54. 'email' => 'required|email|exists:users,email',
  55. 'password' => 'required|string|min:6'
  56. ]);
  57. $user = User::where('email', $request->email)->first();
  58. if ($user && Hash::check($request->password, $user->password)) {
  59. Auth::setUser($user);
  60. return [
  61. 'auth' => $this->createFingerPrint(),
  62. 'businesses' => Auth::user()->businesses->keyBy('id')->map(fn($b, $bid) => Business::info($bid))
  63. ];
  64. }
  65. return new JsonResponse([
  66. 'message' => trans('auth.failed'),
  67. 'status' => Response::HTTP_NOT_FOUND,
  68. ], Response::HTTP_NOT_FOUND);
  69. }
  70. public function register(Request $request)
  71. {
  72. $this->validate($request, [
  73. 'name' => 'required|string|max:225|min:2',
  74. 'username' => ['required', Rule::unique('users', 'username')],
  75. 'email' => ['required', 'email', Rule::unique('users', 'email')],
  76. 'password' => 'required|string|min:8'
  77. ]);
  78. $request->merge(['password' => Hash::make($request->password)]);
  79. $code_data = ['verification_code' => $this->sendVerificationCode(\request('email'), 'register')];
  80. $method_data = ['method' => 'registerMain'];
  81. Cache::put($request->email, $request->all() + $code_data + $method_data, 3600); // remain one hour
  82. return \response()->json([
  83. 'message' => 'Code send for user and user must be verified.'],
  84. Response::HTTP_OK);
  85. }
  86. public function registerMain($user_info)
  87. {
  88. $user = User::create($user_info);
  89. Auth::setUser($user);
  90. return $this->createFingerPrint();
  91. }
  92. public function sendVerificationCode($contact_way, $type)
  93. {
  94. $verification_code = rand(10001, 99999);
  95. Notification::route('mail', $contact_way)->notify( new MailNotification([
  96. 'greeting' => __('notification.auth.verification.greeting'),
  97. 'subject' => __('notification.auth.verification.subject'),
  98. 'body' => __('notification.auth.verification.body', ['code' => $verification_code]),
  99. 'link' => __('notification.auth.verification.link', ['email' => $contact_way, 'type' => $type]),
  100. ]));
  101. return $verification_code;
  102. }
  103. public function verification(Request $request)
  104. {
  105. if (!Cache::has($request->email)) {
  106. return \response()->json(['message' => 'Code expired.'], Response::HTTP_BAD_REQUEST);
  107. }
  108. $user_info = Cache::get($request->email);
  109. $this->validate($request, [
  110. 'email' => 'required|email',
  111. 'verification_code' => 'required|string|min:4|max:4|in:'.$user_info['verification_code']
  112. ]);
  113. // Cache::forget($request->email);
  114. if (isset($user_info['method'])) {
  115. Cache::forget($request->email);
  116. return call_user_func('self::'.$user_info['method'], $user_info);
  117. }
  118. return \response()->json(['message' => 'Code verified successfully.'], Response::HTTP_OK,);
  119. // return isset($user_info['method']) ?
  120. // call_user_func('self::'.$user_info['method'], $user_info) :
  121. // \response()->json(['message' => 'Code verified successfully.'], Response::HTTP_OK,);
  122. }
  123. public function forgetPassword(Request $request)
  124. {
  125. $this->validate($request, [
  126. 'email' => 'required|email|exists:users,email'
  127. ]);
  128. $code_data = ['verification_code' => $this->sendVerificationCode(\request('email', 'forget'))];
  129. Cache::put($request->email, $request->all() + $code_data, 3600); // remain one hour
  130. return \response()->json([
  131. 'message' => 'Code send for user and user must be verified.'],
  132. Response::HTTP_OK);
  133. }
  134. public function updatePassword(Request $request)
  135. {
  136. if (!Cache::has($request->email)) {
  137. return \response()->json(['message' => 'Code expired.'], Response::HTTP_BAD_REQUEST);
  138. }
  139. $this->validate($request, [
  140. 'email' => 'required|email',
  141. 'password' => 'required|string|min:8|confirmed',
  142. 'verification_code' => 'required|string|min:4|max:4|in:'.Cache::get($request->email)['verification_code']
  143. ]);
  144. $user = User::where('email', $request->email)->first();
  145. $user->update([
  146. 'password' => Hash::make($request->password)
  147. ]);
  148. Auth::setUser($user);
  149. Cache::forget($request->email);
  150. return $this->createFingerPrint();
  151. }
  152. /**
  153. * @param Request $request
  154. * @return mixed
  155. * @throws TokenMismatchException
  156. */
  157. public function logout(Request $request)
  158. {
  159. $token = $request->bearerToken();
  160. if (blank($token)) {
  161. return new JsonResponse([
  162. 'message' => 'Not authorized request.',
  163. 'status' => Response::HTTP_UNAUTHORIZED
  164. ]);
  165. }
  166. /** @var Fingerprint $token */
  167. $token = Auth::user()->fingerprints()->firstWhere([
  168. 'token' => $token,
  169. ]);
  170. if ($token) {
  171. return $token->delete();
  172. }
  173. throw new TokenMismatchException('Invalid token!');
  174. }
  175. /**
  176. * @param string $token
  177. * @throws TokenMismatchException
  178. */
  179. public function revoke(string $token)
  180. {
  181. /** @var Fingerprint $token */
  182. $token = Fingerprint::firstWhere([
  183. 'token' => $token,
  184. ]);
  185. if ($token) {
  186. return $token->delete();
  187. }
  188. throw new TokenMismatchException();
  189. }
  190. public function auth()
  191. {
  192. return new UserResource(Auth::user());
  193. }
  194. public function authWithInfo()
  195. {
  196. return [
  197. 'auth' => new UserResource(Auth::user()),
  198. 'businesses' => Auth::user()->businesses->keyBy('id') ->map(fn($b, $bid) => Business::info($bid))
  199. ];
  200. }
  201. public function delete(Request $request)
  202. {
  203. Auth::user()->fingerprints()->delete();
  204. unset(Auth::user()->token);
  205. Auth::user()->delete();
  206. return 'success';
  207. }
  208. public function updateFcmToken(Request $request)
  209. {
  210. Auth::user()->fingerprints()->where(
  211. [
  212. ['agent', request()->getAgent()],
  213. ['ip', request()->getClientIp()],
  214. ['os', request()->getOS()],
  215. ['latitude', \request()->getLocation()->getAttribute('lat')],
  216. ['longitude', \request()->getLocation()->getAttribute('lon')],
  217. ]
  218. )->firstOrFail()->update([
  219. 'fcm_token' => $request->fcm_token
  220. ]);
  221. return $this->authWithInfo();
  222. }
  223. public function createFingerPrint()
  224. {
  225. $attributes = [
  226. 'agent' => request()->getAgent(),
  227. 'ip' => request()->getClientIp(),
  228. 'os' => request()->getOS(),
  229. 'latitude' => \request()->getLocation()->getAttribute('lat'),
  230. 'longitude' => \request()->getLocation()->getAttribute('lon'),
  231. ];
  232. $values = [
  233. 'token' => Str::random(60)
  234. ];
  235. return Auth::user()->fingerprints()->firstOrCreate($attributes, $attributes + $values);
  236. }
  237. }