From b03c64ea981cb2960007d16c9dbd3e6588c41986 Mon Sep 17 00:00:00 2001 From: Deon George Date: Fri, 23 Aug 2024 17:28:00 +1000 Subject: [PATCH] Put back google social login --- app/Casts/CollectionOrNull.php | 2 +- .../Auth/SocialLoginController.php | 85 +++++-- app/Mail/SocialLink.php | 48 ++++ config/auth.php | 216 +++++++++--------- config/services.php | 6 + .../views/mail/system/social_link.blade.php | 21 ++ .../adminlte/auth/social_link.blade.php | 76 ++++++ routes/web.php | 2 + 8 files changed, 337 insertions(+), 119 deletions(-) create mode 100644 app/Mail/SocialLink.php create mode 100644 resources/views/mail/system/social_link.blade.php create mode 100644 resources/views/theme/backend/adminlte/auth/social_link.blade.php diff --git a/app/Casts/CollectionOrNull.php b/app/Casts/CollectionOrNull.php index dc1079d..f9e8b49 100644 --- a/app/Casts/CollectionOrNull.php +++ b/app/Casts/CollectionOrNull.php @@ -24,6 +24,6 @@ class CollectionOrNull implements CastsAttributes */ public function set(Model $model, string $key, mixed $value, array $attributes): mixed { - return $value->count() ? json_encode($value) : NULL; + return count($value) ? json_encode($value) : NULL; } } \ No newline at end of file diff --git a/app/Http/Controllers/Auth/SocialLoginController.php b/app/Http/Controllers/Auth/SocialLoginController.php index ce36112..628aa87 100644 --- a/app/Http/Controllers/Auth/SocialLoginController.php +++ b/app/Http/Controllers/Auth/SocialLoginController.php @@ -3,10 +3,15 @@ namespace App\Http\Controllers\Auth; use Carbon\Carbon; +use Illuminate\Contracts\View\View; +use Illuminate\Http\Request; +use Illuminate\Support\Arr; use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\Mail; use Laravel\Socialite\Facades\Socialite; use App\Http\Controllers\Controller; +use App\Mail\SocialLink; use App\Models\{ProviderOauth,ProviderToken,User,UserOauth}; class SocialLoginController extends Controller @@ -22,54 +27,58 @@ class SocialLoginController extends Controller $openiduser = Socialite::with($provider)->user(); if (! $openiduser) - return redirect('/home')->with('error','No user details obtained.'); + return redirect('/home') + ->with('error','No user details obtained.'); $oo = ProviderOauth::firstOrCreate(['name'=>$provider,'active'=>TRUE]); // See if this user has connected and linked previously $aoo = $oo->users->where('userid',$openiduser->id); - if ($aoo->count() == 1) { + if ($aoo->count() === 1) { $aoo = $aoo->first(); - if ((is_null($user=$aoo->user) AND (is_null($aoo->account) OR is_null($user=$aoo->account->user))) OR ! $user->active) { - if (! $user) { + if ((is_null($user=$aoo->user) && (is_null($aoo->account) || is_null($user=$aoo->account->user))) || ! $user->active) { + if (! $user) $user = User::where('email',$openiduser->email)->first(); - } - if (! $user OR ! $user->active) { - return redirect('/login')->with('error','Invalid account, or account inactive, please contact an admin.'); - } + if ((! $user) || (! $user->active)) + return redirect('/login') + ->with('error','Invalid account, or account inactive, please contact an admin.'); return $this->link($provider,$aoo,$user); } // All Set to login - Auth::login($user,FALSE); + Auth::login($user); // If there are too many users, then we have a problem } elseif ($aoo->count() > 1) { - return redirect('/login')->with('error','Seems you have multiple oauth IDs, please contact an admin.'); + return redirect('/login') + ->with('error','Seems you have multiple oauth IDs, please contact an admin.'); // User is using OAUTH for the first time. } else { $uo = User::active()->where('email',$openiduser->email); // See if their is an account with this email address - if ($uo->count() == 1) { + if ($uo->count() === 1) { $aoo = new UserOauth; $aoo->userid = $openiduser->id; $aoo->oauth_data = $openiduser->user; + $oo->users()->save($aoo); return $this->link($provider,$aoo,$uo->first()); // If there are too many users, then we have a problem } elseif ($uo->count() > 1) { - return redirect('/login')->with('error','Seems you have multiple accounts, please contact an admin.'); + return redirect('/login') + ->with('error','Seems you have multiple accounts, please contact an admin.'); } else { - return redirect('/login')->with('error','Seems you dont have an account with that email, please contact an admin.'); + return redirect('/login') + ->with('error','Seems you dont have an account with that email, please contact an admin.'); } } @@ -82,7 +91,8 @@ class SocialLoginController extends Controller $openiduser = Socialite::with($provider)->user(); if (! $openiduser) - return redirect('/home')->with('error','No user details obtained.'); + return redirect('/home') + ->with('error','No user details obtained.'); $po = ProviderOauth::where('name',$provider)->singleOrFail(); @@ -101,4 +111,51 @@ class SocialLoginController extends Controller ->intended('/home') ->with('success','Token refreshed.'); } + + /** + * We have identified the user and oauth, just need them to confirm the link + * + * @param $provider + * @param UserOauth $ao + * @param User $uo + * @return View + */ + public function link($provider,UserOauth $ao,User $uo): View + { + // @note If this is sent now (send()), it results in the caller to be executed a second time (handleProviderCallback()). + Mail::to($uo->email)->queue(new SocialLink($ao)); + + return view('theme.backend.adminlte.auth.social_link') + ->with('oauthid',$ao->id) + ->with('provider',$provider); + } + + public function linkcomplete(Request $request,$provider) + { + // Load our oauth id + $aoo = UserOauth::findOrFail($request->post('oauthid')); + + // Check our email matches + if (Arr::get($aoo->oauth_data,'email','invalid') !== $request->post('email')) + return redirect('/login') + ->with('error','Account details didnt match to make link.'); + + // Check our token matches + if ($aoo->link_token !== $request->post('token')) + return redirect('/login') + ->with('error','Token details didnt match to make link.'); + + // Load our email. + $uo = User::where('email',$request->post('email'))->firstOrFail(); + + // Incase we have an existing record with a different oauthid + UserOauth::where('user_id',$uo->id)->delete(); + + $aoo->user_id = $uo->id; + $aoo->save(); + Auth::login($uo); + + return redirect() + ->intended('/home'); + } } \ No newline at end of file diff --git a/app/Mail/SocialLink.php b/app/Mail/SocialLink.php new file mode 100644 index 0000000..8149a32 --- /dev/null +++ b/app/Mail/SocialLink.php @@ -0,0 +1,48 @@ +site = $o->site; + $this->token = $o->link_token; + $this->user = $o->user; + } + + /** + * Build the message. + * + * @return $this + */ + public function build() + { + Config::set('site',$this->site); + + return $this + ->markdown('mail.system.social_link') + ->subject('Link your Account') + ->with([ + 'site'=>$this->site, + ]); + } +} \ No newline at end of file diff --git a/config/auth.php b/config/auth.php index e6636c4..bf9dbf6 100644 --- a/config/auth.php +++ b/config/auth.php @@ -2,120 +2,128 @@ return [ - /* - |-------------------------------------------------------------------------- - | Authentication Defaults - |-------------------------------------------------------------------------- - | - | This option defines the default authentication "guard" and password - | reset "broker" for your application. You may change these values - | as required, but they're a perfect start for most applications. - | - */ + /* + |-------------------------------------------------------------------------- + | Authentication Defaults + |-------------------------------------------------------------------------- + | + | This option defines the default authentication "guard" and password + | reset "broker" for your application. You may change these values + | as required, but they're a perfect start for most applications. + | + */ - 'defaults' => [ - 'guard' => env('AUTH_GUARD', 'web'), - 'passwords' => env('AUTH_PASSWORD_BROKER', 'users'), - ], + 'defaults' => [ + 'guard' => env('AUTH_GUARD', 'web'), + 'passwords' => env('AUTH_PASSWORD_BROKER', 'users'), + ], - /* - |-------------------------------------------------------------------------- - | Authentication Guards - |-------------------------------------------------------------------------- - | - | Next, you may define every authentication guard for your application. - | Of course, a great default configuration has been defined for you - | which utilizes session storage plus the Eloquent user provider. - | - | All authentication guards have a user provider, which defines how the - | users are actually retrieved out of your database or other storage - | system used by the application. Typically, Eloquent is utilized. - | - | Supported: "session" - | - */ + /* + |-------------------------------------------------------------------------- + | Authentication Guards + |-------------------------------------------------------------------------- + | + | Next, you may define every authentication guard for your application. + | Of course, a great default configuration has been defined for you + | which utilizes session storage plus the Eloquent user provider. + | + | All authentication guards have a user provider, which defines how the + | users are actually retrieved out of your database or other storage + | system used by the application. Typically, Eloquent is utilized. + | + | Supported: "session" + | + */ - 'guards' => [ - 'web' => [ - 'driver' => 'session', - 'provider' => 'users', - ], + 'guards' => [ + 'web' => [ + 'driver' => 'session', + 'provider' => 'users', + ], - 'api' => [ - 'driver' => 'passport', - 'provider' => 'users', - ], + 'api' => [ + 'driver' => 'passport', + 'provider' => 'users', + ], - ], + ], - /* - |-------------------------------------------------------------------------- - | User Providers - |-------------------------------------------------------------------------- - | - | All authentication guards have a user provider, which defines how the - | users are actually retrieved out of your database or other storage - | system used by the application. Typically, Eloquent is utilized. - | - | If you have multiple user tables or models you may configure multiple - | providers to represent the model / table. These providers may then - | be assigned to any extra authentication guards you have defined. - | - | Supported: "database", "eloquent" - | - */ + /* + |-------------------------------------------------------------------------- + | User Providers + |-------------------------------------------------------------------------- + | + | All authentication guards have a user provider, which defines how the + | users are actually retrieved out of your database or other storage + | system used by the application. Typically, Eloquent is utilized. + | + | If you have multiple user tables or models you may configure multiple + | providers to represent the model / table. These providers may then + | be assigned to any extra authentication guards you have defined. + | + | Supported: "database", "eloquent" + | + */ - 'providers' => [ - 'users' => [ - 'driver' => 'eloquent', - 'model' => env('AUTH_MODEL', App\Models\User::class), - ], + 'providers' => [ + 'users' => [ + 'driver' => 'eloquent', + 'model' => env('AUTH_MODEL', App\Models\User::class), + ], - // 'users' => [ - // 'driver' => 'database', - // 'table' => 'users', - // ], - ], + // 'users' => [ + // 'driver' => 'database', + // 'table' => 'users', + // ], + ], - /* - |-------------------------------------------------------------------------- - | Resetting Passwords - |-------------------------------------------------------------------------- - | - | These configuration options specify the behavior of Laravel's password - | reset functionality, including the table utilized for token storage - | and the user provider that is invoked to actually retrieve users. - | - | The expiry time is the number of minutes that each reset token will be - | considered valid. This security feature keeps tokens short-lived so - | they have less time to be guessed. You may change this as needed. - | - | The throttle setting is the number of seconds a user must wait before - | generating more password reset tokens. This prevents the user from - | quickly generating a very large amount of password reset tokens. - | - */ + /* + |-------------------------------------------------------------------------- + | Resetting Passwords + |-------------------------------------------------------------------------- + | + | These configuration options specify the behavior of Laravel's password + | reset functionality, including the table utilized for token storage + | and the user provider that is invoked to actually retrieve users. + | + | The expiry time is the number of minutes that each reset token will be + | considered valid. This security feature keeps tokens short-lived so + | they have less time to be guessed. You may change this as needed. + | + | The throttle setting is the number of seconds a user must wait before + | generating more password reset tokens. This prevents the user from + | quickly generating a very large amount of password reset tokens. + | + */ - 'passwords' => [ - 'users' => [ - 'provider' => 'users', - 'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'), - 'expire' => 60, - 'throttle' => 60, - ], - ], + 'passwords' => [ + 'users' => [ + 'provider' => 'users', + 'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'), + 'expire' => 60, + 'throttle' => 60, + ], + ], - /* - |-------------------------------------------------------------------------- - | Password Confirmation Timeout - |-------------------------------------------------------------------------- - | - | Here you may define the amount of seconds before a password confirmation - | window expires and users are asked to re-enter their password via the - | confirmation screen. By default, the timeout lasts for three hours. - | - */ + /* + |-------------------------------------------------------------------------- + | Password Confirmation Timeout + |-------------------------------------------------------------------------- + | + | Here you may define the amount of seconds before a password confirmation + | window expires and users are asked to re-enter their password via the + | confirmation screen. By default, the timeout lasts for three hours. + | + */ - 'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800), + 'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800), -]; + 'social' => [ + 'google' => [ + 'name' => 'Google', + 'id' => 'google', + 'class' => 'btn-danger', + 'icon' => 'fab fa-google', + ], + ], +]; \ No newline at end of file diff --git a/config/services.php b/config/services.php index c5208e3..a9d6055 100644 --- a/config/services.php +++ b/config/services.php @@ -42,6 +42,12 @@ return [ 'guid' => env('EZYPAY_GUID'), ], + 'google' => [ + 'client_id' => env('AUTH_GOOGLE_CLIENT_ID'), + 'client_secret' => env('AUTH_GOOGLE_SECRET'), + 'redirect' => '/auth/google/callback', + ], + 'provider' => [ 'intuit' => [ 'api'=> \Intuit\API::class, diff --git a/resources/views/mail/system/social_link.blade.php b/resources/views/mail/system/social_link.blade.php new file mode 100644 index 0000000..219da98 --- /dev/null +++ b/resources/views/mail/system/social_link.blade.php @@ -0,0 +1,21 @@ +@component('mail::message',['site'=>$site,'heading'=>'Link Your Account']) +Hi {{ isset($user) ? $user->full_name.',' : '' }} + +A request was made to link your account to a social login. +If you didnt make this request, you can ignore this, and the request will be ignored. +If you did make the request, then please enter the code displayed below. + +@component('mail::table') +{{ $token }} +@endcomponent + +Once you've keyed in this code, you'll be able to login to your account using your social login instead of a username and a password. + +Thanks, + +{{ config('mail.from.name') }} + +@component('mail::subcopy') +If you didnt make this request, you can safely ignore this email - no change was made to your account, nor was it accessed by an unauthorised person. +@endcomponent +@endcomponent \ No newline at end of file diff --git a/resources/views/theme/backend/adminlte/auth/social_link.blade.php b/resources/views/theme/backend/adminlte/auth/social_link.blade.php new file mode 100644 index 0000000..3cced37 --- /dev/null +++ b/resources/views/theme/backend/adminlte/auth/social_link.blade.php @@ -0,0 +1,76 @@ +@extends('adminlte::layouts.auth') + +@section('htmlheader_title') + Link Account +@endsection + +@section('content') +
+ + +
+ NOTE: Link your account.

+
    +
  • An email has been sent to you with a token, please use those details here:
  • +
+
+ + @if (count($errors) > 0) +
+ Whoops! {{ trans('adminlte_lang::message.someproblems') }}

+
    + @foreach ($errors->all() as $error) +
  • {{ $error }}
  • + @endforeach +
+
+ @endif + +
+ +
+
+@endsection \ No newline at end of file diff --git a/routes/web.php b/routes/web.php index 4f01521..d447243 100644 --- a/routes/web.php +++ b/routes/web.php @@ -62,6 +62,8 @@ Route::get('pay/paypal/capture',[PaypalController::class,'capture']); Route::get('auth/{socialProvider}',[SocialLoginController::class,'redirectToProvider']); Route::get('auth/{socialProvider}/callback',[SocialLoginController::class,'handleProviderCallback']); Route::get('auth/{socialProvider}/token',[SocialLoginController::class,'handleBearerTokenCallback']); +Route::get('auth/{socialProvider}/link',[SocialLoginController::class,'link']); +Route::post('auth/{socialProvider}/linkcomplete',[SocialLoginController::class,'linkcomplete']); // Return from user switch Route::get('admin/switch/stop',[SwitchUserController::class,'switch_stop'])