From c793596a3a4e6f8c017e6569580ae66fec97d077 Mon Sep 17 00:00:00 2001 From: miguel456 Date: Sun, 28 Aug 2022 05:46:32 +0100 Subject: [PATCH] feat: add Discord facade --- app/Facades/Discord.php | 32 +++++++++++++ app/Helpers/Discord.php | 46 +++++++++++++++++- .../Controllers/Auth/DiscordController.php | 44 +++++++++++------ app/Observers/UserObserver.php | 4 +- app/Providers/DiscordOuthProvider.php | 31 ++++++++++++ app/User.php | 47 +++++++------------ config/app.php | 4 +- ...27_052451_add_int_profile_pic_to_users.php | 22 +++++++++ 8 files changed, 182 insertions(+), 48 deletions(-) create mode 100644 app/Facades/Discord.php create mode 100644 app/Providers/DiscordOuthProvider.php create mode 100644 database/migrations/2022_08_27_052451_add_int_profile_pic_to_users.php diff --git a/app/Facades/Discord.php b/app/Facades/Discord.php new file mode 100644 index 0000000..8a364a9 --- /dev/null +++ b/app/Facades/Discord.php @@ -0,0 +1,32 @@ +. + */ + +namespace App\Facades; + +use Illuminate\Support\Facades\Facade; + +class Discord extends Facade +{ + protected static function getFacadeAccessor() + { + return 'discordServiceFacade'; + } +} diff --git a/app/Helpers/Discord.php b/app/Helpers/Discord.php index 8ebfe88..e4b0a1f 100644 --- a/app/Helpers/Discord.php +++ b/app/Helpers/Discord.php @@ -24,8 +24,11 @@ namespace App\Helpers; use App\Exceptions\AccountNotLinkedException; use App\User; +use Carbon\Carbon; use Illuminate\Http\Client\RequestException; +use Illuminate\Support\Collection; use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Crypt; use Illuminate\Support\Facades\Http; @@ -50,7 +53,7 @@ class Discord public function __construct() { - if (!is_null($this->workingGuild)) { + if (isset($this->workingGuild)) { $this->setWorkingGuild(config('services.discord.home_guild')); } } @@ -85,6 +88,47 @@ class Discord throw new AccountNotLinkedException('Specified website user has not linked their Discord account yet.'); } + /** + * Obtains the current user's authentication info. Caches the data for the access_token's TTL, thus + * preventing unnecessary API requests. + * + * @return object Current user's authentication info (has no sensitive fields) + * @throws RequestException + */ + public function getAuthorizationInfo(): object { + + if (Cache::has($this->user->discord_user_id)) { + return unserialize(Cache::get($this->user->discord_user_id)); + } + else { + $authInfo = (object) Http::withToken($this->user->discord_token) + ->get(config('services.discord.base_url') . '/oauth2/@me') + ->throw() + ->json(); + + Cache::put($this->user->discord_user_id, serialize($authInfo), Carbon::parse($authInfo->expires)); + + return $authInfo; + } + } + + + /** + * Checks if the user's token is close to expiring. + * Tokens should be refreshed the day before they expire. + * + * @return bool Whether the user's token needs to be refreshed + * @throws RequestException + */ + public function needsRefresh(): bool { + return Carbon::parse($this->getAuthorizationInfo()->expires)->diffInDays() == 1; + } + + + + public function exchangeRefreshToken() { + + } diff --git a/app/Http/Controllers/Auth/DiscordController.php b/app/Http/Controllers/Auth/DiscordController.php index 2cedaaf..8c60ed7 100644 --- a/app/Http/Controllers/Auth/DiscordController.php +++ b/app/Http/Controllers/Auth/DiscordController.php @@ -21,6 +21,7 @@ namespace App\Http\Controllers\Auth; +use App\Facades\Discord; use App\Http\Controllers\Controller; use App\User; use Illuminate\Support\Facades\Auth; @@ -41,21 +42,36 @@ class DiscordController extends Controller public function discordCallback() { $discordUser = Socialite::driver('discord')->user(); - $appUser = User::upsert([ - 'uuid' => Str::uuid(), - 'name' => $discordUser->getName(), - 'email' => $discordUser->getEmail(), - 'email_verified_at' => now(), // verify the account since it came from a trusted provider - 'username' => $discordUser->getNickname(), - 'currentIp' => \request()->ip(), - 'discord_user_id' => $discordUser->getId(), - 'discord_token' => $discordUser->token, - 'discord_refresh_token' => $discordUser->refreshToken - ], [ - 'email' => $discordUser->getEmail() - ]); + $appUser = User::where('email', $discordUser->getEmail())->first(); - Auth::login(User::where('email', $discordUser->getEmail())->first()); + if ($appUser) { + + $appUser->discord_token = $discordUser->token; + $appUser->discord_refresh_token = $discordUser->refreshToken; + $appUser->discord_user_id = $discordUser->getId(); + $appUser->discord_pfp = $discordUser->getAvatar(); + $appUser->save(); + + Auth::login($appUser, true); + + } else { + + $oAuthUser = User::create([ + 'uuid' => Str::uuid(), + 'name' => $discordUser->getName(), + 'email' => $discordUser->getEmail(), + 'email_verified_at' => now(), // verify the account since it came from a trusted provider + 'username' => $discordUser->getNickname(), + 'currentIp' => \request()->ip(), + 'registrationIp' => request()->ip(), + 'discord_user_id' => $discordUser->getId(), + 'discord_pfp' => $discordUser->getAvatar(), + 'discord_token' => $discordUser->token, + 'discord_refresh_token' => $discordUser->refreshToken + ]); + + Auth::login($oAuthUser, true); + } return redirect() ->route('dashboard'); diff --git a/app/Observers/UserObserver.php b/app/Observers/UserObserver.php index 99c8676..298ac07 100755 --- a/app/Observers/UserObserver.php +++ b/app/Observers/UserObserver.php @@ -41,8 +41,10 @@ class UserObserver * @param \App\User $user * @return void */ - public function created(ProfileService $profileService, User $user) + public function created(User $user) { + $profileService = new ProfileService(); + try { $profileService->createProfile($user); diff --git a/app/Providers/DiscordOuthProvider.php b/app/Providers/DiscordOuthProvider.php new file mode 100644 index 0000000..0704604 --- /dev/null +++ b/app/Providers/DiscordOuthProvider.php @@ -0,0 +1,31 @@ + 'datetime', + 'discord_token' => 'encrypted', + 'discord_refresh_token' => 'encrypted' ]; // RELATIONSHIPS @@ -101,35 +115,6 @@ class User extends Authenticatable implements MustVerifyEmail return $this->hasMany('App\Absence', 'requesterID'); } - - // ACCESSORS AND MUTATORS - - /** - * Encrypt/decrypt the Discord access token. Data is encrypted at rest. - * - * @return Attribute - */ - public function discordToken(): Attribute { - return Attribute::make( - get: fn ($value) => Crypt::decryptString($value), - set: fn ($value) => Crypt::encryptString($value) - ); - } - - /** - * Encrypt/decrypt the Discord refresh token. Data is encrypted at rest. - * - * @return Attribute - */ - public function discordRefreshToken(): Attribute { - return Attribute::make( - get: fn ($value) => Crypt::decryptString($value), - set: fn ($value) => Crypt::encryptString($value) - ); - } - - - // UTILITY LOGIC public function isVerified(): bool { diff --git a/config/app.php b/config/app.php index 520850a..4fa2d1d 100755 --- a/config/app.php +++ b/config/app.php @@ -291,6 +291,7 @@ return [ \App\Providers\OptionsProvider::class, App\Providers\DigitalStorageProvider::class, App\Providers\JSONProvider::class, + App\Providers\DiscordOuthProvider::class, NotificationChannels\Discord\DiscordServiceProvider::class, ], @@ -349,7 +350,8 @@ return [ 'Markdown' => GrahamCampbell\Markdown\Facades\Markdown::class, 'ContextAwareValidator' => App\Facades\ContextAwareValidation::class, 'Settings' => App\Facades\Options::class, - 'JSON' => App\Facades\JSON::class + 'JSON' => App\Facades\JSON::class, + 'DiscordOauth' => App\Facades\Discord::class ], diff --git a/database/migrations/2022_08_27_052451_add_int_profile_pic_to_users.php b/database/migrations/2022_08_27_052451_add_int_profile_pic_to_users.php new file mode 100644 index 0000000..68d46bd --- /dev/null +++ b/database/migrations/2022_08_27_052451_add_int_profile_pic_to_users.php @@ -0,0 +1,22 @@ +longText('discord_pfp')->after('discord_user_id')->nullable(); + }); + } + + public function down() + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('discord_pfp'); + }); + } +}