diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php index f4cbdaa..9612844 100644 --- a/app/Http/Controllers/AuthController.php +++ b/app/Http/Controllers/AuthController.php @@ -2,22 +2,18 @@ namespace App\Http\Controllers; -use App\Models\User; use App\Models\Business; -use App\Models\Fingerprint; +use App\Models\User; use App\Notifications\MailNotification; -use Illuminate\Support\Facades\Notification; -use Illuminate\Support\Str; -use Illuminate\Http\Request; -use Illuminate\Validation\Rule; use Illuminate\Http\JsonResponse; -use App\Http\Resources\UserResource; +use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; -use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Cache; +use Illuminate\Support\Facades\Hash; +use Illuminate\Support\Facades\Notification; +use Illuminate\Support\Str; +use Illuminate\Validation\Rule; use Laravel\Socialite\Facades\Socialite; -use Illuminate\Session\TokenMismatchException; -use phpDocumentor\Reflection\Type; use Symfony\Component\HttpFoundation\Response; class AuthController extends Controller @@ -34,33 +30,63 @@ class AuthController extends Controller $user = Socialite::driver('google')->stateless()->user(); $find_user = User::where('email', $user->email)->first(); - if ($find_user) { - - $find_user->update([ - 'active' => true - ]); + if (!$find_user) + { - Auth::setUser($find_user); - - } else { - - $user = User::create($user->user + [ - 'password' => Hash::make('google-login-user'), + $find_user = User::create($user->user + [ + 'password' => Hash::make(Str::random(8)), 'username' => $user->email, - 'active' => true + 'active' => true, + 'has_password' => false ]); - Auth::setUser($user); + } + + Auth::setUser($find_user); - } - $finger_print = $this->createFingerPrint(); - return redirect('http://localhost:3000/login?token='.$finger_print->token); + $finger_print = $this->createFingerPrint(); + + return redirect('http://localhost:3000/login?token='.$finger_print->token); } catch (Exception $e) { dd($e->getMessage()); } } + public function emailChecking(Request $request) + { + $this->validate($request, [ + 'email' => 'required|email', + ]); + + $user = User::where('email', $request->email)->first(); + + if ($user && $user->has_password) { + // email exists in db + // user before set a password + return response()->json(['message' => 'User exists must be login'], 200); + } + + if ($user && !$user->has_password) { + // email exists in db + // user hasn't password (we set password for user) + $this->sendVerification($request->email, 'google'); + return response()->json(['message' => 'Send email for validation'], 200); + } + + if (Cache::has($request->email)) { + // email exists in cache + $this->sendVerification($request->email, Cache::get($request->email)['type']); + return response()->json(['message' => 'Send email for validation'], 200); + } + + if (!$user && !Cache::has($request->email)) { + // user not exists in db and cache + $this->sendVerification($request->email, 'register'); + return response()->json(['message' => 'Send email for validation'], 200); + } + } + public function login(Request $request) { // todo: Logging in from a new device will result in sending a notification @@ -85,77 +111,51 @@ class AuthController extends Controller ], Response::HTTP_NOT_FOUND); } - - public function register(Request $request) + public function verification(Request $request) { $this->validate($request, [ - 'name' => 'required|string|max:225|min:2', - 'username' => ['required', Rule::unique('users', 'username')], - 'email' => ['required', 'email', Rule::unique('users', 'email')], - 'password' => 'required|string|min:8' - ]); - - $request->merge(['password' => Hash::make($request->password)]); + 'email' => 'required|email', + 'signature' => 'required|string', + ]); - $code_data = ['verification_code' => $this->sendVerificationCode(\request('email'), 'register')]; - $method_data = ['method' => 'registerMain']; + $this->checkValidation($request->email, 'google', $request->signature); - Cache::put($request->email, $request->all() + $code_data + $method_data, 3600); // remain one hour + Auth::setUser(User::where('email', $request->email)->first()); - return \response()->json([ - 'message' => 'Code send for user and user must be verified.'], - Response::HTTP_OK); + return [ + 'auth' => $this->createFingerPrint(), + 'businesses' => Auth::user()->businesses->keyBy('id')->map(fn($b, $bid) => Business::info($bid)) + ]; } - public function registerMain($user_info) + public function sendVerification($email, $type) { - $user = User::create($user_info); - - Auth::setUser($user); + $signature = Str::random(30); - return $this->createFingerPrint(); - } + Cache::put($email, ['type' => $type, 'signature' => $signature], 3600); - public function sendVerificationCode($contact_way, $type) - { - $verification_code = rand(10001, 99999); - - Notification::route('mail', $contact_way)->notify( new MailNotification([ + Notification::route('mail', $email)->notify( new MailNotification([ 'greeting' => __('notification.auth.verification.greeting'), 'subject' => __('notification.auth.verification.subject'), - 'body' => __('notification.auth.verification.body', ['code' => $verification_code]), - 'link' => __('notification.auth.verification.link', ['email' => $contact_way, 'type' => $type]), + 'body' => __('notification.auth.verification.new_body'), + 'link' => __('notification.auth.verification.link', [ + 'email' => $email, + 'type' => $type, + 'signature' => $signature + ]) ])); - return $verification_code; +// return $verification_code; } - public function verification(Request $request) + public function checkValidation($email, $type, $signature) { - if (!Cache::has($request->email)) { - return \response()->json(['message' => 'Code expired.'], Response::HTTP_BAD_REQUEST); - } - - $user_info = Cache::get($request->email); - - $this->validate($request, [ - 'email' => 'required|email', - 'verification_code' => 'required|string|min:4|max:4|in:'.$user_info['verification_code'] - ]); - -// Cache::forget($request->email); - - if (isset($user_info['method'])) { - Cache::forget($request->email); - return call_user_func('self::'.$user_info['method'], $user_info); + if (!Cache::has($email) || Cache::get($email)['type'] !== $type || Cache::get($email)['signature'] != $signature) + { + abort(403, 'Validation failed'); } - - return \response()->json(['message' => 'Code verified successfully.'], Response::HTTP_OK,); - -// return isset($user_info['method']) ? -// call_user_func('self::'.$user_info['method'], $user_info) : -// \response()->json(['message' => 'Code verified successfully.'], Response::HTTP_OK,); + Cache::forget($email); } public function forgetPassword(Request $request) @@ -164,122 +164,58 @@ class AuthController extends Controller 'email' => 'required|email|exists:users,email' ]); - $code_data = ['verification_code' => $this->sendVerificationCode(\request('email', 'forget'))]; + $this->sendVerification($request->email, 'forget'); - Cache::put($request->email, $request->all() + $code_data, 3600); // remain one hour - - return \response()->json([ - 'message' => 'Code send for user and user must be verified.'], - Response::HTTP_OK); + return response()->json(['message' => 'Send email for validation'], 200); } public function updatePassword(Request $request) { - if (!Cache::has($request->email)) { - return \response()->json(['message' => 'Code expired.'], Response::HTTP_BAD_REQUEST); - } - $this->validate($request, [ 'email' => 'required|email', 'password' => 'required|string|min:8|confirmed', - 'verification_code' => 'required|string|min:4|max:4|in:'.Cache::get($request->email)['verification_code'] + 'signature' => 'required|string' ]); + $this->checkValidation($request->email, 'forget', $request->signature); + $user = User::where('email', $request->email)->first(); $user->update([ - 'password' => Hash::make($request->password) + 'password' => Hash::make($request->password), + 'has_password' => true ]); Auth::setUser($user); - Cache::forget($request->email); - - return $this->createFingerPrint(); - } - - /** - * @param Request $request - * @return mixed - * @throws TokenMismatchException - */ - public function logout(Request $request) - { - $token = $request->bearerToken(); - - if (blank($token)) { - return new JsonResponse([ - 'message' => 'Not authorized request.', - 'status' => Response::HTTP_UNAUTHORIZED - ]); - } - - /** @var Fingerprint $token */ - $token = Auth::user()->fingerprints()->firstWhere([ - 'token' => $token, - ]); - - if ($token) { - return $token->delete(); - } + $this->createFingerPrint(); - throw new TokenMismatchException('Invalid token!'); + return response()->json(['message' => 'Update successfully you must be login.'], 200); } - /** - * @param string $token - * @throws TokenMismatchException - */ - public function revoke(string $token) + public function register(Request $request) { - /** @var Fingerprint $token */ - $token = Fingerprint::firstWhere([ - 'token' => $token, + $this->validate($request, [ + 'name' => 'required|string|max:225|min:2', + 'username' => ['required', Rule::unique('users', 'username')], + 'email' => ['required', 'email', Rule::unique('users', 'email')], + 'password' => 'required|string|min:6', + 'signature' => 'required|string' ]); - if ($token) { - return $token->delete(); - } - - throw new TokenMismatchException(); - } + $this->checkValidation($request->email, 'register', $request->signature); - public function auth() - { - return new UserResource(Auth::user()); - } + $request->merge(['password' => Hash::make($request->password)]); - public function authWithInfo() - { - return [ - 'auth' => new UserResource(Auth::user()), - 'businesses' => Auth::user()->businesses->keyBy('id') ->map(fn($b, $bid) => Business::info($bid)) - ]; - } + $user = User::create($request->all()+ [ + 'has_password' => true + ]); - public function delete(Request $request) - { - Auth::user()->fingerprints()->delete(); - unset(Auth::user()->token); - Auth::user()->delete(); + Auth::setUser($user); - return 'success'; - } + $this->createFingerPrint(); - public function updateFcmToken(Request $request) - { - Auth::user()->fingerprints()->where( - [ - ['agent', request()->getAgent()], - ['ip', request()->getClientIp()], - ['os', request()->getOS()], - ['latitude', \request()->getLocation()->getAttribute('lat')], - ['longitude', \request()->getLocation()->getAttribute('lon')], - ] - )->firstOrFail()->update([ - 'fcm_token' => $request->fcm_token - ]); - return $this->authWithInfo(); + return response()->json(['message' => 'Register successfully you must be login.'], 200); } public function createFingerPrint() diff --git a/app/Http/Controllers/NewAuthController.php b/app/Http/Controllers/NewAuthController.php deleted file mode 100644 index 80b3a87..0000000 --- a/app/Http/Controllers/NewAuthController.php +++ /dev/null @@ -1,27 +0,0 @@ -stateless()->redirect(); + } + + public function handleGoogleCallback(Request $request) + { + try { + + $user = Socialite::driver('google')->stateless()->user(); + $find_user = User::where('email', $user->email)->first(); + + if ($find_user) { + + $find_user->update([ + 'active' => true + ]); + + Auth::setUser($find_user); + + } else { + + $user = User::create($user->user + [ + 'password' => Hash::make('google-login-user'), + 'username' => $user->email, + 'active' => true + ]); + + Auth::setUser($user); + + } + $finger_print = $this->createFingerPrint(); + return redirect('http://localhost:3000/login?token='.$finger_print->token); + + } catch (Exception $e) { + dd($e->getMessage()); + } + } + + public function login(Request $request) + { + // todo: Logging in from a new device will result in sending a notification + $this->validate($request, [ + 'email' => 'required|email|exists:users,email', + 'password' => 'required|string|min:6' + ]); + + $user = User::where('email', $request->email)->first(); + if ($user && Hash::check($request->password, $user->password)) { + Auth::setUser($user); + + return [ + 'auth' => $this->createFingerPrint(), + 'businesses' => Auth::user()->businesses->keyBy('id')->map(fn($b, $bid) => Business::info($bid)) + ]; + } + + return new JsonResponse([ + 'message' => trans('auth.failed'), + 'status' => Response::HTTP_NOT_FOUND, + ], Response::HTTP_NOT_FOUND); + } + + + public function register(Request $request) + { + $this->validate($request, [ + 'name' => 'required|string|max:225|min:2', + 'username' => ['required', Rule::unique('users', 'username')], + 'email' => ['required', 'email', Rule::unique('users', 'email')], + 'password' => 'required|string|min:8' + ]); + + $request->merge(['password' => Hash::make($request->password)]); + + $code_data = ['verification_code' => $this->sendVerificationCode(\request('email'), 'register')]; + $method_data = ['method' => 'registerMain']; + + Cache::put($request->email, $request->all() + $code_data + $method_data, 3600); // remain one hour + + return \response()->json([ + 'message' => 'Code send for user and user must be verified.'], + Response::HTTP_OK); + + } + + public function registerMain($user_info) + { + $user = User::create($user_info); + + Auth::setUser($user); + + return $this->createFingerPrint(); + } + + public function sendVerificationCode($contact_way, $type) + { + $verification_code = rand(10001, 99999); + + Notification::route('mail', $contact_way)->notify( new MailNotification([ + 'greeting' => __('notification.auth.verification.greeting'), + 'subject' => __('notification.auth.verification.subject'), + 'body' => __('notification.auth.verification.body', ['code' => $verification_code]), + 'link' => __('notification.auth.verification.link', ['email' => $contact_way, 'type' => $type]), + ])); + + return $verification_code; + } + + public function verification(Request $request) + { + if (!Cache::has($request->email)) { + return \response()->json(['message' => 'Code expired.'], Response::HTTP_BAD_REQUEST); + } + + $user_info = Cache::get($request->email); + + $this->validate($request, [ + 'email' => 'required|email', + 'verification_code' => 'required|string|min:4|max:4|in:'.$user_info['verification_code'] + ]); + +// Cache::forget($request->email); + + if (isset($user_info['method'])) { + Cache::forget($request->email); + return call_user_func('self::'.$user_info['method'], $user_info); + } + + return \response()->json(['message' => 'Code verified successfully.'], Response::HTTP_OK,); + +// return isset($user_info['method']) ? +// call_user_func('self::'.$user_info['method'], $user_info) : +// \response()->json(['message' => 'Code verified successfully.'], Response::HTTP_OK,); + } + + public function forgetPassword(Request $request) + { + $this->validate($request, [ + 'email' => 'required|email|exists:users,email' + ]); + + $code_data = ['verification_code' => $this->sendVerificationCode(\request('email', 'forget'))]; + + Cache::put($request->email, $request->all() + $code_data, 3600); // remain one hour + + return \response()->json([ + 'message' => 'Code send for user and user must be verified.'], + Response::HTTP_OK); + } + + public function updatePassword(Request $request) + { + if (!Cache::has($request->email)) { + return \response()->json(['message' => 'Code expired.'], Response::HTTP_BAD_REQUEST); + } + + $this->validate($request, [ + 'email' => 'required|email', + 'password' => 'required|string|min:8|confirmed', + 'verification_code' => 'required|string|min:4|max:4|in:'.Cache::get($request->email)['verification_code'] + ]); + + $user = User::where('email', $request->email)->first(); + + $user->update([ + 'password' => Hash::make($request->password) + ]); + + Auth::setUser($user); + + Cache::forget($request->email); + + return $this->createFingerPrint(); + } + + /** + * @param Request $request + * @return mixed + * @throws TokenMismatchException + */ + public function logout(Request $request) + { + $token = $request->bearerToken(); + + if (blank($token)) { + return new JsonResponse([ + 'message' => 'Not authorized request.', + 'status' => Response::HTTP_UNAUTHORIZED + ]); + } + + /** @var Fingerprint $token */ + $token = Auth::user()->fingerprints()->firstWhere([ + 'token' => $token, + ]); + + if ($token) { + return $token->delete(); + } + + throw new TokenMismatchException('Invalid token!'); + } + + /** + * @param string $token + * @throws TokenMismatchException + */ + public function revoke(string $token) + { + /** @var Fingerprint $token */ + $token = Fingerprint::firstWhere([ + 'token' => $token, + ]); + + if ($token) { + return $token->delete(); + } + + throw new TokenMismatchException(); + } + + public function auth() + { + return new UserResource(Auth::user()); + } + + public function authWithInfo() + { + return [ + 'auth' => new UserResource(Auth::user()), + 'businesses' => Auth::user()->businesses->keyBy('id') ->map(fn($b, $bid) => Business::info($bid)) + ]; + } + + public function delete(Request $request) + { + Auth::user()->fingerprints()->delete(); + unset(Auth::user()->token); + Auth::user()->delete(); + + return 'success'; + } + + public function updateFcmToken(Request $request) + { + Auth::user()->fingerprints()->where( + [ + ['agent', request()->getAgent()], + ['ip', request()->getClientIp()], + ['os', request()->getOS()], + ['latitude', \request()->getLocation()->getAttribute('lat')], + ['longitude', \request()->getLocation()->getAttribute('lon')], + ] + )->firstOrFail()->update([ + 'fcm_token' => $request->fcm_token + ]); + return $this->authWithInfo(); + } + + public function createFingerPrint() + { + $attributes = [ + 'agent' => request()->getAgent(), + 'ip' => request()->getClientIp(), + 'os' => request()->getOS(), + 'latitude' => \request()->getLocation()->getAttribute('lat'), + 'longitude' => \request()->getLocation()->getAttribute('lon'), + ]; + + $values = [ + 'token' => Str::random(60) + ]; + + return Auth::user()->fingerprints()->firstOrCreate($attributes, $attributes + $values); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index aa0357c..dd194f1 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -29,7 +29,7 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac 'has_avatar' => 'boolean', ]; - protected $fillable = ['name', 'email','mobile', 'username','password','active','has_avatar']; + protected $fillable = ['name', 'email','mobile', 'username','password','active','has_avatar', 'has_password']; protected $fillable_relations = ['projects']; diff --git a/database/migrations/2020_08_18_085016_create_users_table.php b/database/migrations/2020_08_18_085016_create_users_table.php index b656a71..f65fc2c 100644 --- a/database/migrations/2020_08_18_085016_create_users_table.php +++ b/database/migrations/2020_08_18_085016_create_users_table.php @@ -21,6 +21,7 @@ class CreateUsersTable extends Migration $table->string('username')->unique(); $table->string('password'); $table->boolean('active')->default(false); + $table->boolean('has_password')->default(false); $table->boolean('has_avatar')->default(false); $table->timestamp('created_at')->nullable(); $table->timestamp('updated_at')->useCurrent(); diff --git a/resources/lang/fa/notification.php b/resources/lang/fa/notification.php index 64213ab..c625810 100644 --- a/resources/lang/fa/notification.php +++ b/resources/lang/fa/notification.php @@ -88,9 +88,10 @@ return [ 'auth' => [ 'verification' => [ 'greeting' => 'سلام کاربر گرامی!', - 'subject' => 'کد تایید', + 'subject' => 'لینک احراز هویت', 'body' => 'کد تایید شما :code', - 'link' => 'http://localhost:3000/verification?email=:email&type=:type' + 'new_body' => 'برای ادامه فرایند ثبت نام لینک زیر را دنبال کنید.', + 'link' => 'http://localhost:3000/verification?email=:email&type=:type&signature=:signature' ] ], diff --git a/routes/api.php b/routes/api.php index 087aeb0..6986afd 100644 --- a/routes/api.php +++ b/routes/api.php @@ -6,12 +6,6 @@ $router->get('/lab', function () { throw new \Exception("^_^"); }); -$router->get('/ntest', function () { - $user = \App\Models\User::find(1); - \Illuminate\Support\Facades\Notification::send($user, new \App\Notifications\SmsNotification(['verification_code' => "1234"])); -// (new \App\Utilities\HelperClass\NotificationHelper()) -// ->makeSmsNotif('template_name', ['user' => 'myUser', 'business' => 'myBusiness']); -})->middleware('bindBusiness'); $router->group(['prefix' => 'actions'], function () use ($router) { $router->group(['prefix' => 'businesses'], function () use ($router) { $router->group(['prefix' => '{business}', 'middleware' => 'bindBusiness'], function () use ($router) { @@ -25,6 +19,7 @@ $router->get('/{transaction}/redirection', 'CreditController@redirection'); $router->group(['prefix' => 'auth'], function () use ($router) { $router->get('/', 'AuthController@auth')->middleware('auth:api'); + $router->post('/checking', 'AuthController@emailChecking'); $router->delete('/', 'AuthController@delete'); $router->get('/info', 'AuthController@authWithInfo')->middleware('auth:api'); $router->post('login', 'AuthController@login'); @@ -35,7 +30,7 @@ $router->group(['prefix' => 'auth'], function () use ($router) { $router->post('forget-password', 'AuthController@forgetPassword'); $router->post('update-password', 'AuthController@updatePassword'); - $router->post('verification', 'AuthController@verification'); + $router->post('verification', 'AuthController@verification')->name('verification'); $router->get('google/redirect', 'AuthController@redirectToGoogle')->name('google.redirect'); $router->get('google/callback', 'AuthController@handleGoogleCallback')->name('google.callback');