Auth system laravel just with phone number and verification sms code - laravel

my web app is laravel and my users do not have email and password and they register and login with phone number and verification sms code.
My db is Mongodb.
How to I change Auth system laravel?

you will need to implement your own Auth system .First you will need to send SMS I recommend using Twilio https://www.twilio.com/blog/create-sms-portal-laravel-php-twilio
Here is some functions I made before
public function sendVerificationCode(VerificationCodeRequest $request)
{
$twilioService = new TwilioService() ;
$otp = random_int(1000, 9999);
$result = $twilioService->sendVerificationCode(request('mobile'), $otp );
if (!$result) {
return response()->json(["message"=>__('messages.wrong_number')],422);
}
}
$user = User::updateOrCreate(
['mobile' => request('mobile')],
['verification_code' => $otp]
);
return response()->json(["message"=>__('messages.otp_sent')],200);
}
public function login(MobileLoginRequest $request)
{
$user = User::where("mobile",request('mobile'))->firstOrFail();
if($user->verification_code==$otp){
if ( !$userToken=JWTAuth::fromUser($user)) {
return response()->json(['message' => __('messages.Unauth')], 401);
}
}else{
return response()->json(['message' => __('messages.invalid_otp')], 401);
}
$user->update(["verified"=>1,"login_type"=>"mobile"]);
return $this->respondWithToken($userToken,$user);
}
protected function respondWithToken($userToken,$user)
{
return response()->json([
'token' => $userToken,
'token_type' => 'bearer',
'expires_in' => JWTAuth::factory()->getTTL() * 60,
'profile' => $user,
], 200);
}
the twilio service file
<?php
namespace App\Http\Services;
use Illuminate\Support\Facades\Log;
use Twilio\Rest\Client;
class TwilioService
{
public function sendVerificationCode($number,$otp){
return $this->sendMessage("your Verification Code is : $otp ",$number);
}
public function sendNotification($recipient,$body,$title){
return $this->sendMessage($body,$recipient,$title."\n");
}
private function sendMessage($message, $recipient,$title="")
{
try {
$account_sid = getenv("TWILIO_SID");
$auth_token = getenv("TWILIO_AUTH_TOKEN");
$twilio_number = getenv("TWILIO_NUMBER");
$client = new Client($account_sid, $auth_token);
$client->messages->create("$recipient",
['from' => $twilio_number, 'body' => $title.$message] );
return true;
} catch (\Throwable $th) {
Log::error("$th");
Log::info("-------unable to send SMS to phone $recipient -------------");
return false;
}
}

Related

how to use reflash or more session to transport session

in my controllers at laravel 9 **postManageTwoFactor** method i use $request->session()->flash('phone2' , $data['phone']);
for transport session to method **getPhoneVerify** , this is ture!
then i use $request->session()->reflash(); when i want to "reflash session " to postPhoneVerify method for last time, this is null or false!
i need this session $request->session()->get('phone2') in postPhoneVerify method...\
thank for helping me
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\ActiveCode;
class ProfileController extends Controller
{
public function index()
{
return view('profile.index');
}
public function manageTwoFactor()
{
return view('profile.two-factor-auth');
}
public function postManageTwoFactor(Request $request)
{
$data = $request->validate([
'type' => 'required|in:on,off',
'phone' => 'required_unless:type,off',
]);
if($data['type'] === 'on'){
if($request->user()->phone_number !== $data['phone']){
/*create the new code for users...*/
$code = ActiveCode::generateCode($request->user());
/*
* in sessions "flash" meaning:
* validate unitl one route and not any more....
* easy: $data['phone'] store in 'phone2'/
*/
$request->session()->flash('phone2' , $data['phone']);
/*-------- send the code number to phone-----------*/
//TODO send sms for 2auth...
return redirect(route('prof.two.phone'));
}else{
/*
* put that 'on' becuse the number that entered is the same
*/
$request->user()->update([
'two_factor' => 'on'
]);
}
}
if($data['type'] === 'off'){
$request->user()->update([
'two_factor' => 'off'
]);
}
return back();
}
/*
* when this method or route , should be ruunig where users Request
* the code and fill the phone number field...
*/
public function getPhoneVerify(Request $request)
{
if (! $request->session()->has('phone2')){
return redirect(route('profile.2fa'));
}
$request->session()->reflash();
return view('profile.phone-verify');
}
public function postPhoneVerify(Request $request)
{
$request->validate([
'token' => 'required'
]);
if (! $request->session()->has('phone2')){
return redirect(route('profile.2fa'));
}
$status = ActiveCode::verifyCode($request->token , $request->user());
if($status){
/*---- after verify Code we need delete old record for each row user in active_codes table in dbase ---*/
$request->user()->activeCode()->delete();
/*--after all, with under code we can UPDATE active_codes table in database...*/
$request->user()->update([
'phone_number' => $request->session()->get('phone2'),
'two_factor' => 'on'
]);
alert()->success('احراز هویت دو مرحله ای شما انجام شد' , 'عملیات موفق آمیز بود');
}else{
alert()->success('لطفا دوباره تلاش کنید','عملیات موفق آمیز نبود');
}
return redirect(route('profile.2fa'));
}
}

Catching org_internal 403 error via Google's OAUTH?

I have google OATH setup via socialite (only for within our organisation) and everything is working fine.
One thing I'd like to try and do is catch this "error" and get redirected back to our login page with a custom message telling the user that they do not belong to our organisation.
In principle this works fine, they can just hit the back button... but for fluidity and design, I'd like to catch this and redirect back to our home page.
Is this even possible? If so, how would you recommend I go about it?
public function show()
{
return view('auth.login');
}
public function redirectToProvider($driver)
{
if( ! $this->isProviderAllowed($driver) ) {
return $this->sendFailedResponse("{$driver} is not currently supported");
}
try {
return Socialite::driver($driver)->redirect();
} catch (Exception $e) {
return $this->sendFailedResponse($e->getMessage());
}
}
public function handleProviderCallback( $driver )
{
try {
$user = Socialite::driver($driver)->user();
} catch (Exception $e) {
return $this->sendFailedResponse($e->getMessage());
}
// check for email in returned user
return empty( $user->email )
? redirect()->intended('/login?failed=1')
: $this->loginOrCreateAccount($user, $driver);
}
protected function sendSuccessResponse()
{
return redirect()->intended('/');
}
protected function sendFailedResponse($msg = null)
{
return redirect()->intended('/login?failedResponse='.$msg);
}
protected function loginOrCreateAccount($providerUser, $driver)
{
// check for already has account
$user = User::where('email', $providerUser->getEmail())->first();
// if user
if( $user ) {
// update the avatar and provider that might have changed
$user->update([
'avatar' => $providerUser->avatar,
'provider' => $driver,
'provider_id' => $providerUser->id,
'access_token' => $providerUser->token
]);
} else {
return redirect()->intended('/login?noUser=1');
}
// login the user
Auth::login($user, true);
return $this->sendSuccessResponse();
}
private function isProviderAllowed($driver)
{
return in_array($driver, $this->providers) && config()->has("services.{$driver}");
}

Socialite function getId() is working but id is not added in database phpmyadmin

I am using Laravel Socialite for google login all the scoialite functions are working fine and data is being added to the database(phpmyadmin) except for the provider_id which function is getId() which is also working fine but data is not inserted in Db.
public function handleProviderCallback()
{
try{
$socialUser = Socialite::driver('google')->user();
}catch(Exception $e){
redirect('/');
}
$socialProvider = SocialProvider::where('provider_id', $socialUser->getId())->first();
if(!$socialProvider){
//create new user
$user = User::firstOrCreate(
['email' => $socialUser->getEmail()],
['name' => $socialUser->getName()]
);
$user->socialProviders()->create(
['provider_id' => $socialUser->getId() , 'provider' => 'google']
);
}
else{
$user = $socialProvider->user;
}
auth()->login($user);
return redirect('/home');
//return $socialUser->getId();
}
'provider_id' must be the protected $fillable array on the SocialProvider model if you want to pass it in the array to SocialProvider->create(...).

Laravel Socialite Google login only with one domain

I have a Google+ login on my app with Laravel Socialite. When the login is done I have a callback to connect the user (I create her in database if necessary).
But I want to restrain the connection to only the company (email like "example#company.com", so only the email with "company.com").
Can I do it with Laravel Socialite ? I can make the verification manually in my callback but if Socialite can do it, it's better.
Thank you
My callback :
public function handleProviderCallback($provider){
$user = Socialite::driver($provider)->user();
if ($user) {
$local_user = User::whereEmail($user->getEmail())->first();
// If we don't have a user create a new user
if (!$local_user) {
$fragment = explode(' ', $user->getName());
$local_user = User::create([
'first_name' => isset($fragment[0]) ? $fragment[0] : '',
'last_name' => isset($fragment[1]) ? $fragment[1] : '',
'email' => $user->getEmail(),
'last_seen' => Carbon::now(),
'password' => ''
]);
$local_user->roles()->attach(Role::whereName('User')->first());
}
auth()->login($local_user);
}
return redirect($this->redirectTo);
}
You have a step by step guide for domain restriction.
In controller you need to specifiy these actions:
public function handleProviderCallback()
{
try {
$user = Socialite::driver('google')->user();
} catch (\Exception $e) {
return redirect('/login');
}
// only allow people with #company.com to login
if(explode("#", $user->email)[1] !== 'company.com'){
return redirect()->to('/');
}
// check if they're an existing user
$existingUser = User::where('email', $user->email)->first();
if($existingUser){
// log them in
auth()->login($existingUser, true);
} else {
// create a new user
$newUser = new User;
$newUser->name = $user->name;
$newUser->email = $user->email;
$newUser->google_id = $user->id;
$newUser->avatar = $user->avatar;
$newUser->avatar_original = $user->avatar_original;
$newUser->save();
auth()->login($newUser, true);
}
return redirect()->to('/home');
}
No, you can’t do it in Socialite itself because Socialite is just a mechanism of retrieving tokens from OAuth-compliant servers.
If you only want to accept users with a particular email suffix, then that’s business logic so something you should handle in your callback:
public function handleProviderCallback()
{
$user = Socialite::driver('google')->user();
if (Str::endsWith($user->getEmail(), '#example.com')) {
// Look up user and authenticate them
}
abort(400, 'User does not belong to organization');
}

Google Client API setAccessToken() before isAccessTokenExpired() results in invalid credentials

I am working with the Google Client API in Laravel to allow my users to sync their calendars with Google. Everything works, but the issue I am running into is when their tokens expire they are getting an "Invalid Credentials" error, in order to fix it they have to log out and log back in which I am trying to avoid.
I don't understand why setAccessToken() is to be called before isAccessTokenExpired().
I need to check if the access token is expired before I set it but if I do it this way then isAccessTokenExpired() always returns true.
Any ideas would be helpful. Thanks!
Here is my code:
GoogeServiceController.php
class GoogleServiceController extends Controller
{
protected $client;
protected $service;
public function __construct()
{
$client = new Google_Client();
$client->setAuthConfig(Config::get('google_config.web'));
$client->setAccessType('offline');
$client->addScope(Google_Service_Calendar::CALENDAR);
$service = new Google_Service_Calendar($client);
$this->client = $client;
$this->service = $service;
}
public function oauth(Request $request)
{
if (App::environment('local')) {
$this->client->setRedirectUri('http://esm.development.com/oauth');
} else {
$this->client->setRedirectUri('https://essentialstudiomanager.com/oauth');
}
if (is_null($request->user()->refresh_token)) {
$this->client->setApprovalPrompt("force");
}
if (!$request->has('code')) {
$auth_url = $this->client->createAuthUrl();
$filtered_url = filter_var($auth_url, FILTER_SANITIZE_URL);
return redirect($filtered_url);
} else {
$this->client->authenticate($request->code);
if (is_null($request->user()->refresh_token)) {
$refresh_token = $this->client->getRefreshToken();
$user = $request->user();
$user->refresh_token = $refresh_token;
$user->save();
}
$request->session()->put('access_token', $this->client->getAccessToken());
$notification = ['message' => 'Your calendar is now synced with your Google Calendar.', 'alert-type' => 'success'];
return redirect()->route('home')->with($notification);
}
}
}
GoogleEventController.php
public function updateGoogleEvent($request, $event, $title, $description, $start, $end)
{
if ($request->session()->has('access_token')) {
$this->client->setAccessToken(session('access_token'));
if ($this->client->isAccessTokenExpired()) {
$this->client->refreshToken($request->user()->refresh_token);
$request->session()->put('access_token', $this->client->getAccessToken());
$this->client->setAccessToken(session('access_token'));
}
} else {
return redirect()->route('oauthCallBack');
}
$users_calendar = $this->service->calendars->get('primary');
$get_event = $this->service->events->get('primary', $event->google_event_id);
$get_event->setSummary($title);
$get_event->setDescription($description);
$start_date = new Google_Service_Calendar_EventDateTime();
$start_date->setDateTime($start);
$start_date->setTimeZone($users_calendar->timeZone);
$get_event->setStart($start_date);
$end_date = new Google_Service_Calendar_EventDateTime();
$end_date->setDateTime($end);
$end_date->setTimeZone($users_calendar->timeZone);
$get_event->setEnd($end_date);
$updatedEvent = $this->service->events->update('primary', $get_event->getId(), $get_event);
}

Resources