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.

313 lines
9.9 KiB

4 years ago
4 years ago
4 years ago
  1. <?php
  2. namespace App\Http\Controllers;
  3. use App\Http\Resources\UserResource;
  4. use App\Models\Business;
  5. use App\Models\User;
  6. use App\Notifications\DBNotification;
  7. use App\Notifications\MailNotification;
  8. use Illuminate\Http\JsonResponse;
  9. use Illuminate\Http\Request;
  10. use Illuminate\Support\Facades\Auth;
  11. use Illuminate\Support\Facades\Cache;
  12. use Illuminate\Support\Facades\Hash;
  13. use Illuminate\Support\Facades\Notification;
  14. use Illuminate\Support\Str;
  15. use Illuminate\Validation\Rule;
  16. use Laravel\Socialite\Facades\Socialite;
  17. use Symfony\Component\HttpFoundation\Response;
  18. class AuthController extends Controller
  19. {
  20. public function redirectToGoogle()
  21. {
  22. return Socialite::driver('google')->stateless()->redirect();
  23. }
  24. public function handleGoogleCallback(Request $request)
  25. {
  26. try {
  27. $user = Socialite::driver('google')->stateless()->user();
  28. $find_user = User::where('email', $user->email)->first();
  29. if (!$find_user)
  30. {
  31. $find_user = User::create($user->user + [
  32. 'password' => Hash::make(Str::random(8)),
  33. 'username' => $user->email,
  34. 'active' => true,
  35. 'has_password' => false
  36. ]);
  37. }
  38. Auth::setUser($find_user);
  39. $finger_print = $this->createFingerPrint();
  40. return redirect('http://localhost:3000/login?token='.$finger_print->token);
  41. } catch (Exception $e) {
  42. dd($e->getMessage());
  43. }
  44. }
  45. public function emailChecking(Request $request)
  46. {
  47. $this->validate($request, [
  48. 'email' => 'required|email',
  49. ]);
  50. $user = User::where('email', $request->email)->first();
  51. if ($user && $user->has_password) {
  52. // email exists in db
  53. // user before set a password
  54. return response()->json(['message' => 'User exists must be login'], 200);
  55. }
  56. if ($user && !$user->has_password) {
  57. // email exists in db
  58. // user hasn't password (we set password for user)
  59. $this->sendVerification($request->email, 'google');
  60. return response()->json(['message' => 'Send email for validation'], 200);
  61. }
  62. if (Cache::has($request->email)) {
  63. // email exists in cache
  64. $this->sendVerification($request->email, Cache::get($request->email)['type']);
  65. return response()->json(['message' => 'Send email for validation'], 200);
  66. }
  67. if (!$user && !Cache::has($request->email)) {
  68. // user not exists in db and cache
  69. $this->sendVerification($request->email, 'register');
  70. return response()->json(['message' => 'Send email for validation'], 200);
  71. }
  72. }
  73. public function login(Request $request)
  74. {
  75. // todo: Logging in from a new device will result in sending a notification
  76. $this->validate($request, [
  77. 'email' => 'required|email|exists:users,email',
  78. 'password' => 'required|string|min:6'
  79. ]);
  80. $user = User::where('email', $request->email)->first();
  81. if ($user && Hash::check($request->password, $user->password)) {
  82. Auth::setUser($user);
  83. // for new device login
  84. $this->loginNotif($this->firstOrNot());
  85. return [
  86. 'auth' => $this->createFingerPrint(),
  87. 'businesses' => Auth::user()->businesses->keyBy('id')->map(fn($b, $bid) => Business::info($bid))
  88. ];
  89. }
  90. return new JsonResponse([
  91. 'message' => trans('auth.failed'),
  92. 'status' => Response::HTTP_NOT_FOUND,
  93. ], Response::HTTP_NOT_FOUND);
  94. }
  95. public function verification(Request $request)
  96. {
  97. $this->validate($request, [
  98. 'email' => 'required|email',
  99. 'signature' => 'required|string',
  100. ]);
  101. $this->checkValidation($request->email, 'google', $request->signature);
  102. Auth::setUser(User::where('email', $request->email)->first());
  103. return [
  104. 'auth' => $this->createFingerPrint(),
  105. 'businesses' => Auth::user()->businesses->keyBy('id')->map(fn($b, $bid) => Business::info($bid))
  106. ];
  107. }
  108. public function sendVerification($email, $type)
  109. {
  110. $signature = Str::random(30);
  111. Cache::put($email, ['type' => $type, 'signature' => $signature], 3600);
  112. Notification::route('mail', $email)->notify( new MailNotification([
  113. 'greeting' => __('notification.auth.verification.greeting'),
  114. 'subject' => __('notification.auth.verification.subject'),
  115. 'body' => __('notification.auth.verification.new_body'),
  116. 'link' => __('notification.auth.verification.link', [
  117. 'email' => $email,
  118. 'type' => $type,
  119. 'signature' => $signature
  120. ])
  121. ]));
  122. }
  123. public function checkValidation($email, $type, $signature)
  124. {
  125. if (!Cache::has($email) || Cache::get($email)['type'] !== $type || Cache::get($email)['signature'] != $signature)
  126. {
  127. abort(403, 'Validation failed');
  128. }
  129. Cache::forget($email);
  130. }
  131. public function forgetPassword(Request $request)
  132. {
  133. $this->validate($request, [
  134. 'email' => 'required|email|exists:users,email'
  135. ]);
  136. $this->sendVerification($request->email, 'forget');
  137. return response()->json(['message' => 'Send email for validation'], 200);
  138. }
  139. public function updatePassword(Request $request)
  140. {
  141. $this->validate($request, [
  142. 'email' => 'required|email',
  143. 'password' => 'required|string|min:8|confirmed',
  144. 'signature' => 'required|string'
  145. ]);
  146. $this->checkValidation($request->email, 'forget', $request->signature);
  147. $user = User::where('email', $request->email)->first();
  148. $user->update([
  149. 'password' => Hash::make($request->password),
  150. 'has_password' => true
  151. ]);
  152. Auth::setUser($user);
  153. $this->createFingerPrint();
  154. return response()->json(['message' => 'Update successfully you must be login.'], 200);
  155. }
  156. public function register(Request $request)
  157. {
  158. $this->validate($request, [
  159. 'name' => 'required|string|max:225|min:2',
  160. 'username' => ['required', Rule::unique('users', 'username')],
  161. 'email' => ['required', 'email', Rule::unique('users', 'email')],
  162. 'password' => 'required|string|min:6',
  163. 'signature' => 'required|string'
  164. ]);
  165. $this->checkValidation($request->email, 'register', $request->signature);
  166. $request->merge(['password' => Hash::make($request->password)]);
  167. $user = User::create($request->all()+ [
  168. 'has_password' => true
  169. ]);
  170. Auth::setUser($user);
  171. $this->createFingerPrint();
  172. return response()->json(['message' => 'Register successfully you must be login.'], 200);
  173. }
  174. public function resendLink(Request $request)
  175. {
  176. $this->validate($request, [
  177. 'email' => 'required|email',
  178. 'type' => 'required|string'
  179. ]);
  180. $user_db = User::where('email', $request->email)->first();
  181. $user_cache = Cache::get($request->email);
  182. if ($user_db || $user_cache) {
  183. $this->sendVerification($request->email, $request->type);
  184. return response()->json(['message' => 'Link resend successfully'], 200);
  185. }
  186. abort(403);
  187. }
  188. public function createFingerPrint()
  189. {
  190. $attributes = [
  191. 'agent' => request()->getAgent(),
  192. 'ip' => request()->getClientIp(),
  193. 'os' => request()->getOS(),
  194. 'latitude' => \request()->getLocation()->getAttribute('lat'),
  195. 'longitude' => \request()->getLocation()->getAttribute('lon'),
  196. ];
  197. $values = [
  198. 'token' => Str::random(60)
  199. ];
  200. return Auth::user()->fingerprints()->firstOrCreate($attributes, $attributes + $values);
  201. }
  202. public function firstOrNot()
  203. {
  204. return Auth::user()->fingerprints()->where([
  205. ['agent', '!=',request()->getAgent()],
  206. ['ip', '!=',request()->getClientIp()],
  207. ['os', '!=',request()->getOS()],
  208. ['latitude', '!=',\request()->getLocation()->getAttribute('lat')],
  209. ['longitude', '!=',\request()->getLocation()->getAttribute('lon')],
  210. ])->exists();
  211. }
  212. public function loginNotif($send)
  213. {
  214. if ($send) {
  215. Notification::send(Auth::user(), new MailNotification([
  216. 'greeting' => 'hi',
  217. 'subject' => 'login with another device',
  218. 'body' => 'Warning someone login to your account with new device. check it and dont worry',
  219. ]));
  220. Notification::send(Auth::user(), new DBNotification([
  221. 'body' => 'Warning someone login to your account with new device. check it and dont worry',
  222. ]));
  223. }
  224. }
  225. public function auth()
  226. {
  227. return new UserResource(Auth::user());
  228. }
  229. public function authWithInfo()
  230. {
  231. return [
  232. 'auth' => new UserResource(Auth::user()),
  233. 'businesses' => Auth::user()->businesses->keyBy('id') ->map(fn($b, $bid) => Business::info($bid))
  234. ];
  235. }
  236. public function updateFcmToken(Request $request)
  237. {
  238. Auth::user()->fingerprints()->where(
  239. [
  240. ['agent', request()->getAgent()],
  241. ['ip', request()->getClientIp()],
  242. ['os', request()->getOS()],
  243. ['latitude', \request()->getLocation()->getAttribute('lat')],
  244. ['longitude', \request()->getLocation()->getAttribute('lon')],
  245. ]
  246. )->firstOrFail()->update([
  247. 'fcm_token' => $request->fcm_token
  248. ]);
  249. return $this->authWithInfo();
  250. }
  251. }