From 45edad9164bb4f5e3b56da3afed7aaebc5974c1c Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Sat, 11 Jun 2022 04:07:06 +0200 Subject: [PATCH 01/11] INA-12 | add microsoft provider for oauth --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 3e77c6608e99..31da6d3c0db9 100644 --- a/composer.json +++ b/composer.json @@ -77,6 +77,7 @@ "sentry/sentry-laravel": "^2", "setasign/fpdf": "^1.8", "setasign/fpdi": "^2.3", + "socialiteproviders/microsoft": "^4.1", "square/square": "13.0.0.20210721", "stripe/stripe-php": "^7.50", "symfony/http-client": "^5.2", From 26a3720390880be08c57f17063bdf171d216daf4 Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Sat, 11 Jun 2022 04:07:22 +0200 Subject: [PATCH 02/11] INA-12 | add microsoft oauth to services for socialite --- config/services.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/config/services.php b/config/services.php index 282d814c4abe..8fc958cc9773 100644 --- a/config/services.php +++ b/config/services.php @@ -80,4 +80,9 @@ return [ 'postmark' => [ 'token' => env('POSTMARK_SECRET'), ], + 'microsoft' => [ + 'client_id' => env('MICROSOFT_CLIENT_ID'), + 'client_secret' => env('MICROSOFT_CLIENT_SECRET'), + 'redirect' => env('MICROSOFT_REDIRECT_URI') + ], ]; From f6b22f8fb7bb9213cdd87bdbde91adb5cd916c13 Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Sat, 11 Jun 2022 04:07:46 +0200 Subject: [PATCH 03/11] INA-12 | Support microsoft in OAuth.php --- app/Libraries/OAuth/OAuth.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/Libraries/OAuth/OAuth.php b/app/Libraries/OAuth/OAuth.php index 6d68fa8084b4..33bec4108c0d 100644 --- a/app/Libraries/OAuth/OAuth.php +++ b/app/Libraries/OAuth/OAuth.php @@ -29,6 +29,7 @@ class OAuth const SOCIAL_LINKEDIN = 4; const SOCIAL_TWITTER = 5; const SOCIAL_BITBUCKET = 6; + const SOCIAL_MICROSOFT = 7; /** * @param Socialite $user @@ -74,6 +75,8 @@ class OAuth return 'twitter'; case SOCIAL_BITBUCKET: return 'bitbucket'; + case SOCIAL_MICROSOFT: + return 'microsoft'; } } @@ -92,6 +95,8 @@ class OAuth return SOCIAL_TWITTER; case 'bitbucket': return SOCIAL_BITBUCKET; + case 'microsoft': + return SOCIAL_MICROSOFT; } } @@ -103,7 +108,6 @@ class OAuth $this->provider_id = self::SOCIAL_GOOGLE; return $this; - default: return null; break; From 469461f4905c7f0c75349a27659ff1a042c2ef28 Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Sat, 11 Jun 2022 04:07:56 +0200 Subject: [PATCH 04/11] INA-12 | Handle microsoft login --- app/Http/Controllers/Auth/LoginController.php | 161 ++++++++++++++---- 1 file changed, 128 insertions(+), 33 deletions(-) diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index 9a5d5bb00554..c5d561c18500 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -92,7 +92,7 @@ class LoginController extends BaseController * @return void * deprecated .1 API ONLY we don't need to set any session variables */ - public function authenticated(Request $request, User $user) : void + public function authenticated(Request $request, User $user): void { //$this->setCurrentCompanyId($user->companies()->first()->account->default_company_id); } @@ -196,7 +196,7 @@ class LoginController extends BaseController } elseif($user->google_2fa_secret && !$request->has('one_time_password')) { - + return response() ->json(['message' => ctrans('texts.invalid_one_time_password')], 401) ->header('X-App-Version', config('ninja.app_version')) @@ -234,17 +234,17 @@ class LoginController extends BaseController // /* Ensure the user has a valid token */ // if($user->company_users()->count() != $user->tokens()->count()) // { - + // $user->companies->each(function($company) use($user, $request){ - + // if(!CompanyToken::where('user_id', $user->id)->where('company_id', $company->id)->exists()){ - + // CreateCompanyToken::dispatchNow($company, $user, $request->server('HTTP_USER_AGENT')); - + // } - + // }); - + // } // $truth->setCompanyToken(CompanyToken::where('user_id', auth()->user()->id)->where('company_id', $user->account->default_company->id)->first()); @@ -317,29 +317,28 @@ class LoginController extends BaseController { $truth = app()->make(TruthSource::class); - if($truth->getCompanyToken()) + if ($truth->getCompanyToken()) $company_token = $truth->getCompanyToken(); else $company_token = CompanyToken::where('token', $request->header('X-API-TOKEN'))->first(); $cu = CompanyUser::query() - ->where('user_id', $company_token->user_id); + ->where('user_id', $company_token->user_id); - if($cu->count() == 0) + if ($cu->count() == 0) return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400); - $cu->first()->account->companies->each(function ($company) use($cu, $request){ + $cu->first()->account->companies->each(function ($company) use ($cu, $request) { - if($company->tokens()->where('is_system', true)->count() == 0) - { + if ($company->tokens()->where('is_system', true)->count() == 0) { CreateCompanyToken::dispatchNow($company, $cu->first()->user, $request->server('HTTP_USER_AGENT')); } }); - if($request->has('current_company') && $request->input('current_company') == 'true') - $cu->where("company_id", $company_token->company_id); + if ($request->has('current_company') && $request->input('current_company') == 'true') + $cu->where("company_id", $company_token->company_id); - if(Ninja::isHosted() && !$cu->first()->is_owner && !$cu->first()->user->account->isEnterpriseClient()) + if (Ninja::isHosted() && !$cu->first()->is_owner && !$cu->first()->user->account->isEnterpriseClient()) return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403); return $this->refreshResponse($cu); @@ -359,14 +358,110 @@ class LoginController extends BaseController */ public function oauthApiLogin() { + $message = 'Provider not supported'; if (request()->input('provider') == 'google') { return $this->handleGoogleOauth(); + } elseif (request()->input('provider') == 'microsoft') { + if (request()->has('token')) { + return $this->handleMicrosoftOauth(request()->get('token')); + } else { + $message = 'Bearer token missing for the microsoft login'; + } } return response() - ->json(['message' => 'Provider not supported'], 400) - ->header('X-App-Version', config('ninja.app_version')) - ->header('X-Api-Version', config('ninja.minimum_client_version')); + ->json(['message' => $message], 400) + ->header('X-App-Version', config('ninja.app_version')) + ->header('X-Api-Version', config('ninja.minimum_client_version')); + } + + private function handleMicrosoftOauth($token) + { + $user = Socialite::driver('microsoft')->userFromToken($token); + + if ($user) { + $query = [ + 'oauth_user_id' => $user->id, + 'oauth_provider_id' => 'microsoft', + ]; + if ($existing_user = MultiDB::hasUser($query)) { + + if (!$existing_user->account) + return response()->json(['message' => 'User exists, but not attached to any companies! Orphaned user!'], 400); + + Auth::login($existing_user, true); + + $cu = $this->hydrateCompanyUser(); + + if ($cu->count() == 0) + return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400); + + if (Ninja::isHosted() && !$cu->first()->is_owner && !$existing_user->account->isEnterpriseClient()) + return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403); + + return $this->timeConstrainedResponse($cu); + + } + //If this is a result user/email combo - lets add their OAuth details details + if ($existing_login_user = MultiDB::hasUser(['email' => $user->email])) { + if (!$existing_login_user->account) + return response()->json(['message' => 'User exists, but not attached to any companies! Orphaned user!'], 400); + + Auth::login($existing_login_user, true); + + auth()->user()->update([ + 'oauth_user_id' => $user->id, + 'oauth_provider_id' => 'microsoft', + ]); + + $cu = $this->hydrateCompanyUser(); + + if ($cu->count() == 0) + return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400); + + if (Ninja::isHosted() && !$cu->first()->is_owner && !$existing_login_user->account->isEnterpriseClient()) + return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403); + + return $this->timeConstrainedResponse($cu); + } + + $name = OAuth::splitName($user->name); + + $new_account = [ + 'first_name' => $name[0], + 'last_name' => $name[1], + 'password' => '', + 'email' => $user->email, + 'oauth_user_id' => $user->id, + 'oauth_provider_id' => 'microsoft', + ]; + + MultiDB::setDefaultDatabase(); + + $account = CreateAccount::dispatchNow($new_account, request()->getClientIp()); + + Auth::login($account->default_company->owner(), true); + auth()->user()->email_verified_at = now(); + auth()->user()->save(); + + $cu = $this->hydrateCompanyUser(); + + if ($cu->count() == 0) + return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400); + + if (Ninja::isHosted() && !$cu->first()->is_owner && !auth()->user()->account->isEnterpriseClient()) + return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403); + + return $this->timeConstrainedResponse($cu); + } + + + return response() + ->json(['message' => ctrans('texts.invalid_credentials')], 401) + ->header('X-App-Version', config('ninja.app_version')) + ->header('X-Api-Version', config('ninja.minimum_client_version')); + + } private function hydrateCompanyUser() :Builder @@ -394,17 +489,17 @@ class LoginController extends BaseController if(auth()->user()->company_users()->count() != auth()->user()->tokens()->distinct('company_id')->count()) { - + auth()->user()->companies->each(function($company){ - + if(!CompanyToken::where('user_id', auth()->user()->id)->where('company_id', $company->id)->exists()){ - + CreateCompanyToken::dispatchNow($company, auth()->user(), "Google_O_Auth"); - + } - + }); - + } $truth->setCompanyToken(CompanyToken::where('user_id', auth()->user()->id)->where('company_id', $set_company->id)->first()); @@ -444,7 +539,7 @@ class LoginController extends BaseController return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403); return $this->timeConstrainedResponse($cu); - + } //If this is a result user/email combo - lets add their OAuth details details @@ -474,14 +569,14 @@ class LoginController extends BaseController } if ($user) { - + //check the user doesn't already exist in some form if($existing_login_user = MultiDB::hasUser(['email' => $google->harvestEmail($user)])) { if(!$existing_login_user->account) return response()->json(['message' => 'User exists, but not attached to any companies! Orphaned user!'], 400); - + Auth::login($existing_login_user, true); auth()->user()->update([ @@ -490,11 +585,11 @@ class LoginController extends BaseController ]); $cu = $this->hydrateCompanyUser(); - + // $cu = CompanyUser::query() // ->where('user_id', auth()->user()->id); - if($cu->count() == 0) + if ($cu->count() == 0) return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400); if(Ninja::isHosted() && !$cu->first()->is_owner && !$existing_login_user->account->isEnterpriseClient()) @@ -557,7 +652,7 @@ class LoginController extends BaseController if (request()->has('code')) { return $this->handleProviderCallback($provider); } else { - + if(!in_array($provider, ['google'])) return abort(400, 'Invalid provider'); @@ -594,7 +689,7 @@ class LoginController extends BaseController 'oauth_user_id' => $socialite_user->getId(), 'oauth_provider_id' => $provider, 'oauth_user_token' => $oauth_user_token, - 'oauth_user_refresh_token' => $socialite_user->refreshToken + 'oauth_user_refresh_token' => $socialite_user->refreshToken ]; $user->update($update_user); From ce41046fab746561aeaca7d794006f301cd25c7b Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Sat, 11 Jun 2022 04:08:57 +0200 Subject: [PATCH 05/11] INA-12 | Add microsoft oauth keys to .env.example --- .env.example | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.env.example b/.env.example index b41f9a522156..1ebf51d92f75 100644 --- a/.env.example +++ b/.env.example @@ -61,3 +61,7 @@ SENTRY_LARAVEL_DSN=https://39389664f3f14969b4c43dadda00a40b@sentry2.invoicing.co GOOGLE_PLAY_PACKAGE_NAME= APPSTORE_PASSWORD= + +MICROSOFT_CLIENT_ID= +MICROSOFT_CLIENT_SECRET= +MICROSOFT_REDIRECT_URI= From b2420a817dd7b510c0dae001b01830bf7e4ccbe3 Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Sat, 11 Jun 2022 05:26:24 +0200 Subject: [PATCH 06/11] INA-12 | Add microsoft oauth keys to .env.example --- config/services.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/config/services.php b/config/services.php index 8fc958cc9773..1656cdf10168 100644 --- a/config/services.php +++ b/config/services.php @@ -85,4 +85,9 @@ return [ 'client_secret' => env('MICROSOFT_CLIENT_SECRET'), 'redirect' => env('MICROSOFT_REDIRECT_URI') ], + 'apple' => [ + 'client_id' => env('APPLE_CLIENT_ID'), + 'client_secret' => env('APPLE_CLIENT_SECRET'), + 'redirect' => env('APPLE_REDIRECT_URI') + ], ]; From 79efa59f36865c89622c97d853e6ea49502e8433 Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Sat, 11 Jun 2022 05:26:47 +0200 Subject: [PATCH 07/11] INA-12 | Add social apple to the OAuth.php --- app/Libraries/OAuth/OAuth.php | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/app/Libraries/OAuth/OAuth.php b/app/Libraries/OAuth/OAuth.php index 33bec4108c0d..3abe1e88ab2a 100644 --- a/app/Libraries/OAuth/OAuth.php +++ b/app/Libraries/OAuth/OAuth.php @@ -30,6 +30,7 @@ class OAuth const SOCIAL_TWITTER = 5; const SOCIAL_BITBUCKET = 6; const SOCIAL_MICROSOFT = 7; + const SOCIAL_APPLE = 8; /** * @param Socialite $user @@ -39,8 +40,8 @@ class OAuth { /** 1. Ensure user arrives on the correct provider **/ $query = [ - 'oauth_user_id' =>$socialite_user->getId(), - 'oauth_provider_id'=>$provider, + 'oauth_user_id' => $socialite_user->getId(), + 'oauth_provider_id' => $provider, ]; if ($user = MultiDB::hasUser($query)) { @@ -55,12 +56,12 @@ class OAuth { $name = trim($name); $last_name = (strpos($name, ' ') === false) ? '' : preg_replace('#.*\s([\w-]*)$#', '$1', $name); - $first_name = trim(preg_replace('#'.preg_quote($last_name, '/').'#', '', $name)); + $first_name = trim(preg_replace('#' . preg_quote($last_name, '/') . '#', '', $name)); return [$first_name, $last_name]; } - public static function providerToString(int $social_provider) : string + public static function providerToString(int $social_provider): string { switch ($social_provider) { case SOCIAL_GOOGLE: @@ -77,10 +78,12 @@ class OAuth return 'bitbucket'; case SOCIAL_MICROSOFT: return 'microsoft'; + case SOCIAL_APPLE: + return 'apple'; } } - public static function providerToInt(string $social_provider) : int + public static function providerToInt(string $social_provider): int { switch ($social_provider) { case 'google': @@ -97,6 +100,8 @@ class OAuth return SOCIAL_BITBUCKET; case 'microsoft': return SOCIAL_MICROSOFT; + case 'apple': + return SOCIAL_APPLE; } } From 94a07df7cc111e27ccd34fa8f880f099f13fc46b Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Sat, 11 Jun 2022 05:27:26 +0200 Subject: [PATCH 08/11] INA-12 | Register drivers in EventServiceProvider.php (required for Manager to actually register drivers) --- app/Providers/EventServiceProvider.php | 39 +++++++++++++++----------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index b84ab256dfb9..3fe1741fd755 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -264,9 +264,9 @@ class EventServiceProvider extends ServiceProvider * @var array */ protected $listen = [ - AccountCreated::class =>[ + AccountCreated::class => [ ], - MessageSending::class =>[ + MessageSending::class => [ ], MessageSent::class => [ MailSentListener::class, @@ -312,35 +312,35 @@ class EventServiceProvider extends ServiceProvider PaymentWasVoided::class => [ PaymentVoidedActivity::class, ], - PaymentWasRestored::class =>[ + PaymentWasRestored::class => [ PaymentRestoredActivity::class, ], // Clients - ClientWasCreated::class =>[ + ClientWasCreated::class => [ CreatedClientActivity::class, ], - ClientWasArchived::class =>[ + ClientWasArchived::class => [ ArchivedClientActivity::class, ], - ClientWasUpdated::class =>[ + ClientWasUpdated::class => [ ClientUpdatedActivity::class, ], - ClientWasDeleted::class =>[ + ClientWasDeleted::class => [ DeleteClientActivity::class, ], - ClientWasRestored::class =>[ + ClientWasRestored::class => [ RestoreClientActivity::class, ], // Documents - DocumentWasCreated::class =>[ + DocumentWasCreated::class => [ ], - DocumentWasArchived::class =>[ + DocumentWasArchived::class => [ ], - DocumentWasUpdated::class =>[ + DocumentWasUpdated::class => [ ], - DocumentWasDeleted::class =>[ + DocumentWasDeleted::class => [ ], - DocumentWasRestored::class =>[ + DocumentWasRestored::class => [ ], CreditWasCreated::class => [ CreatedCreditActivity::class, @@ -404,11 +404,11 @@ class EventServiceProvider extends ServiceProvider InvoiceWasCreated::class => [ CreateInvoiceActivity::class, InvoiceCreatedNotification::class, - // CreateInvoicePdf::class, + // CreateInvoicePdf::class, ], InvoiceWasPaid::class => [ - InvoicePaidActivity::class, - CreateInvoicePdf::class, + InvoicePaidActivity::class, + CreateInvoicePdf::class, ], InvoiceWasViewed::class => [ InvoiceViewedActivity::class, @@ -593,7 +593,12 @@ class EventServiceProvider extends ServiceProvider ], VendorWasUpdated::class => [ VendorUpdatedActivity::class, - ] + ], + \SocialiteProviders\Manager\SocialiteWasCalled::class => [ + // ... Manager won't register drivers that are not added to this listener. + \SocialiteProviders\Apple\AppleExtendSocialite::class . '@handle', + \SocialiteProviders\Microsoft\MicrosoftExtendSocialite::class . '@handle', + ], ]; From 85557e8a2a73f9d3f63f4f93acdf0d3e23b4d04d Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Sat, 11 Jun 2022 05:27:38 +0200 Subject: [PATCH 09/11] INA-12 | Install apple driver for socialite --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 31da6d3c0db9..452f41241b6c 100644 --- a/composer.json +++ b/composer.json @@ -77,6 +77,7 @@ "sentry/sentry-laravel": "^2", "setasign/fpdf": "^1.8", "setasign/fpdi": "^2.3", + "socialiteproviders/apple": "^5.2", "socialiteproviders/microsoft": "^4.1", "square/square": "13.0.0.20210721", "stripe/stripe-php": "^7.50", From 4884de4ec8c6088c891e93ffa00121338264ff1c Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Sat, 11 Jun 2022 05:27:45 +0200 Subject: [PATCH 10/11] INA-12 | Add apple keys in .env.example --- .env.example | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.env.example b/.env.example index 1ebf51d92f75..cb0e65ee77b1 100644 --- a/.env.example +++ b/.env.example @@ -65,3 +65,7 @@ APPSTORE_PASSWORD= MICROSOFT_CLIENT_ID= MICROSOFT_CLIENT_SECRET= MICROSOFT_REDIRECT_URI= + +APPLE_CLIENT_ID= +APPLE_CLIENT_SECRET= +APPLE_REDIRECT_URI= From 117a4e4028837c74499a2eadb0b3b0ed894176a5 Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Sat, 11 Jun 2022 05:31:32 +0200 Subject: [PATCH 11/11] INA-12 | add apple to the login, refactor of microsoft login. --- app/Http/Controllers/Auth/LoginController.php | 175 ++++++++++-------- 1 file changed, 94 insertions(+), 81 deletions(-) diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index c5d561c18500..ec52fd49644a 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -168,9 +168,9 @@ class LoginController extends BaseController $this->fireLockoutEvent($request); return response() - ->json(['message' => 'Too many login attempts, you are being throttled'], 401) - ->header('X-App-Version', config('ninja.app_version')) - ->header('X-Api-Version', config('ninja.minimum_client_version')); + ->json(['message' => 'Too many login attempts, you are being throttled'], 401) + ->header('X-App-Version', config('ninja.app_version')) + ->header('X-Api-Version', config('ninja.minimum_client_version')); } if ($this->attemptLogin($request)) { @@ -250,7 +250,7 @@ class LoginController extends BaseController // $truth->setCompanyToken(CompanyToken::where('user_id', auth()->user()->id)->where('company_id', $user->account->default_company->id)->first()); /*On the hosted platform, only owners can login for free/pro accounts*/ - if(Ninja::isHosted() && !$cu->first()->is_owner && !$user->account->isEnterpriseClient()) + if (Ninja::isHosted() && !$cu->first()->is_owner && !$user->account->isEnterpriseClient()) return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403); event(new UserLoggedIn($user, $user->account->default_company, Ninja::eventVars($user->id))); @@ -267,9 +267,9 @@ class LoginController extends BaseController $this->incrementLoginAttempts($request); return response() - ->json(['message' => ctrans('texts.invalid_credentials')], 401) - ->header('X-App-Version', config('ninja.app_version')) - ->header('X-Api-Version', config('ninja.minimum_client_version')); + ->json(['message' => ctrans('texts.invalid_credentials')], 401) + ->header('X-App-Version', config('ninja.app_version')) + ->header('X-Api-Version', config('ninja.minimum_client_version')); } } @@ -358,15 +358,22 @@ class LoginController extends BaseController */ public function oauthApiLogin() { + $message = 'Provider not supported'; if (request()->input('provider') == 'google') { return $this->handleGoogleOauth(); } elseif (request()->input('provider') == 'microsoft') { if (request()->has('token')) { - return $this->handleMicrosoftOauth(request()->get('token')); + return $this->handleSocialiteLogin('microsoft', request()->get('token')); } else { $message = 'Bearer token missing for the microsoft login'; } + } elseif (request()->input('provider') == 'apple') { + if (request()->has('token')) { + return $this->handleSocialiteLogin('apple', request()->get('token')); + } else { + $message = 'Token is missing for the apple login'; + } } return response() @@ -375,103 +382,110 @@ class LoginController extends BaseController ->header('X-Api-Version', config('ninja.minimum_client_version')); } - private function handleMicrosoftOauth($token) + private function getSocialiteUser(string $provider, string $token) { - $user = Socialite::driver('microsoft')->userFromToken($token); + return Socialite::driver($provider)->userFromToken($token); + } + private function handleSocialiteLogin($provider, $token) + { + $user = $this->getSocialiteUser($provider, $token); if ($user) { - $query = [ - 'oauth_user_id' => $user->id, - 'oauth_provider_id' => 'microsoft', - ]; - if ($existing_user = MultiDB::hasUser($query)) { + return $this->loginOrCreateFromSocialite($user, $provider); + } + return response() + ->json(['message' => ctrans('texts.invalid_credentials')], 401) + ->header('X-App-Version', config('ninja.app_version')) + ->header('X-Api-Version', config('ninja.minimum_client_version')); - if (!$existing_user->account) - return response()->json(['message' => 'User exists, but not attached to any companies! Orphaned user!'], 400); + } - Auth::login($existing_user, true); - $cu = $this->hydrateCompanyUser(); + private function loginOrCreateFromSocialite($user, $provider) + { + $query = [ + 'oauth_user_id' => $user->id, + 'oauth_provider_id' => $provider, + ]; + if ($existing_user = MultiDB::hasUser($query)) { - if ($cu->count() == 0) - return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400); + if (!$existing_user->account) + return response()->json(['message' => 'User exists, but not attached to any companies! Orphaned user!'], 400); - if (Ninja::isHosted() && !$cu->first()->is_owner && !$existing_user->account->isEnterpriseClient()) - return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403); - - return $this->timeConstrainedResponse($cu); - - } - //If this is a result user/email combo - lets add their OAuth details details - if ($existing_login_user = MultiDB::hasUser(['email' => $user->email])) { - if (!$existing_login_user->account) - return response()->json(['message' => 'User exists, but not attached to any companies! Orphaned user!'], 400); - - Auth::login($existing_login_user, true); - - auth()->user()->update([ - 'oauth_user_id' => $user->id, - 'oauth_provider_id' => 'microsoft', - ]); - - $cu = $this->hydrateCompanyUser(); - - if ($cu->count() == 0) - return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400); - - if (Ninja::isHosted() && !$cu->first()->is_owner && !$existing_login_user->account->isEnterpriseClient()) - return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403); - - return $this->timeConstrainedResponse($cu); - } - - $name = OAuth::splitName($user->name); - - $new_account = [ - 'first_name' => $name[0], - 'last_name' => $name[1], - 'password' => '', - 'email' => $user->email, - 'oauth_user_id' => $user->id, - 'oauth_provider_id' => 'microsoft', - ]; - - MultiDB::setDefaultDatabase(); - - $account = CreateAccount::dispatchNow($new_account, request()->getClientIp()); - - Auth::login($account->default_company->owner(), true); - auth()->user()->email_verified_at = now(); - auth()->user()->save(); + Auth::login($existing_user, true); $cu = $this->hydrateCompanyUser(); if ($cu->count() == 0) return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400); - if (Ninja::isHosted() && !$cu->first()->is_owner && !auth()->user()->account->isEnterpriseClient()) + if (Ninja::isHosted() && !$cu->first()->is_owner && !$existing_user->account->isEnterpriseClient()) + return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403); + + return $this->timeConstrainedResponse($cu); + + } + //If this is a result user/email combo - lets add their OAuth details details + if ($existing_login_user = MultiDB::hasUser(['email' => $user->email])) { + if (!$existing_login_user->account) + return response()->json(['message' => 'User exists, but not attached to any companies! Orphaned user!'], 400); + + Auth::login($existing_login_user, true); + + auth()->user()->update([ + 'oauth_user_id' => $user->id, + 'oauth_provider_id' => $provider, + ]); + + $cu = $this->hydrateCompanyUser(); + + if ($cu->count() == 0) + return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400); + + if (Ninja::isHosted() && !$cu->first()->is_owner && !$existing_login_user->account->isEnterpriseClient()) return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403); return $this->timeConstrainedResponse($cu); } + $name = OAuth::splitName($user->name); + $new_account = [ + 'first_name' => $name[0], + 'last_name' => $name[1], + 'password' => '', + 'email' => $user->email, + 'oauth_user_id' => $user->id, + 'oauth_provider_id' => $provider, + ]; - return response() - ->json(['message' => ctrans('texts.invalid_credentials')], 401) - ->header('X-App-Version', config('ninja.app_version')) - ->header('X-Api-Version', config('ninja.minimum_client_version')); + MultiDB::setDefaultDatabase(); + $account = CreateAccount::dispatchNow($new_account, request()->getClientIp()); + Auth::login($account->default_company->owner(), true); + auth()->user()->email_verified_at = now(); + auth()->user()->save(); + + $cu = $this->hydrateCompanyUser(); + + if ($cu->count() == 0) + return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400); + + if (Ninja::isHosted() && !$cu->first()->is_owner && !auth()->user()->account->isEnterpriseClient()) + return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403); + + return $this->timeConstrainedResponse($cu); } - private function hydrateCompanyUser() :Builder + + private function hydrateCompanyUser(): Builder { $cu = CompanyUser::query()->where('user_id', auth()->user()->id); - if(CompanyUser::query()->where('user_id', auth()->user()->id)->where('company_id', auth()->user()->account->default_company_id)->exists()) + if (CompanyUser::query()->where('user_id', auth()->user()->id)->where('company_id', auth()->user()->account->default_company_id)->exists()) $set_company = auth()->user()->account->default_company; - else{ + else { $set_company = $cu->first()->company; } @@ -487,14 +501,13 @@ class LoginController extends BaseController if($cu->count() == 0) return $cu; - if(auth()->user()->company_users()->count() != auth()->user()->tokens()->distinct('company_id')->count()) - { + if (auth()->user()->company_users()->count() != auth()->user()->tokens()->distinct('company_id')->count()) { - auth()->user()->companies->each(function($company){ + auth()->user()->companies->each(function ($company) { - if(!CompanyToken::where('user_id', auth()->user()->id)->where('company_id', $company->id)->exists()){ + if (!CompanyToken::where('user_id', auth()->user()->id)->where('company_id', $company->id)->exists()) { - CreateCompanyToken::dispatchNow($company, auth()->user(), "Google_O_Auth"); + CreateCompanyToken::dispatchNow($company, auth()->user(), "Google_O_Auth"); }