7a137 google auth (#520)

* google oauth

* fix lint

* cleanup debug

* Oauth changes, alert message, email validation

* fix exception and inline return condition

* fix google oauth

* UI fixes

* fix provider user

---------

Co-authored-by: Julien Nahum <julien@nahum.net>
This commit is contained in:
Favour Olayinka
2024-08-19 14:22:57 +01:00
committed by GitHub
parent 5049ba7fb1
commit 7ac8503201
14 changed files with 273 additions and 52 deletions

View File

@@ -2,12 +2,12 @@
namespace App\Http\Controllers\Auth;
use App\Exceptions\EmailTakenException;
use App\Http\Controllers\Controller;
use App\Integrations\OAuth\OAuthProviderService;
use App\Models\OAuthProvider;
use App\Models\User;
use App\Models\Workspace;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Laravel\Socialite\Facades\Socialite;
class OAuthController extends Controller
{
@@ -31,11 +31,11 @@ class OAuthController extends Controller
* @param string $provider
* @return \Illuminate\Http\RedirectResponse
*/
public function redirect($provider)
public function redirect(OAuthProviderService $provider)
{
return [
'url' => Socialite::driver($provider)->stateless()->redirect()->getTargetUrl(),
];
return response()->json([
'url' => $provider->getDriver()->setRedirectUrl(config('services.google.auth_redirect'))->getRedirectUrl()
]);
}
/**
@@ -44,69 +44,103 @@ class OAuthController extends Controller
* @param string $driver
* @return \Illuminate\Http\Response
*/
public function handleCallback($provider)
public function handleCallback(OAuthProviderService $provider)
{
$user = Socialite::driver($provider)->stateless()->user();
$user = $this->findOrCreateUser($provider, $user);
try {
$driverUser = $provider->getDriver()->setRedirectUrl(config('services.google.auth_redirect'))->getUser();
} catch (\Exception $e) {
return $this->error([
"message" => "OAuth service failed to authenticate: " . $e->getMessage()
]);
}
$user = $this->findOrCreateUser($provider, $driverUser);
if (!$user) {
return $this->error([
"message" => "User not found."
]);
}
if ($user->has_registered) {
return $this->error([
"message" => "This email is already registered. Please sign in with your password."
]);
}
$this->guard()->setToken(
$token = $this->guard()->login($user)
);
return view('oauth/callback', [
return response()->json([
'token' => $token,
'token_type' => 'bearer',
'expires_in' => $this->guard()->getPayload()->get('exp') - time(),
'new_user' => $user->new_user
]);
}
/**
* @param string $provider
* @param \Laravel\Socialite\Contracts\User $sUser
* @return \App\Models\User
* @p aram \Laravel\Socialite\Contracts\User $socialiteUser
* @return \App\Models\User | null
*/
protected function findOrCreateUser($provider, $user)
protected function findOrCreateUser($provider, $socialiteUser)
{
$oauthProvider = OAuthProvider::where('provider', $provider)
->where('provider_user_id', $user->getId())
->where('provider_user_id', $socialiteUser->getId())
->first();
if ($oauthProvider) {
$oauthProvider->update([
'access_token' => $user->token,
'refresh_token' => $user->refreshToken,
'access_token' => $socialiteUser->token,
'refresh_token' => $socialiteUser->refreshToken,
]);
return $oauthProvider->user;
}
if (User::where('email', $user->getEmail())->exists()) {
throw new EmailTakenException();
if (!$provider->getDriver()->canCreateUser()) {
return null;
}
return $this->createUser($provider, $user);
}
$email = strtolower($socialiteUser->getEmail());
$user = User::whereEmail($email)->first();
if ($user) {
$user->has_registered = true;
return $user;
}
/**
* @param string $provider
* @param \Laravel\Socialite\Contracts\User $sUser
* @return \App\Models\User
*/
protected function createUser($provider, $sUser)
{
$user = User::create([
'name' => $sUser->getName(),
'email' => $sUser->getEmail(),
'name' => $socialiteUser->getName(),
'email' => $email,
'email_verified_at' => now(),
]);
$user->oauthProviders()->create([
'provider' => $provider,
'provider_user_id' => $sUser->getId(),
'access_token' => $sUser->token,
'refresh_token' => $sUser->refreshToken,
// Create and sync workspace
$workspace = Workspace::create([
'name' => 'My Workspace',
'icon' => '🧪',
]);
$user->workspaces()->sync([
$workspace->id => [
'role' => User::ROLE_ADMIN,
],
], false);
$user->new_user = true;
OAuthProvider::create(
[
'user_id' => $user->id,
'provider' => $provider,
'provider_user_id' => $socialiteUser->getId(),
'access_token' => $socialiteUser->token,
'refresh_token' => $socialiteUser->refreshToken,
'name' => $socialiteUser->getName(),
'email' => $socialiteUser->getEmail(),
]
);
return $user;
}
}

View File

@@ -10,7 +10,7 @@ class FontsController extends Controller
public function index(Request $request)
{
return \Cache::remember('google_fonts', 60 * 60, function () {
$url = "https://www.googleapis.com/webfonts/v1/webfonts?sort=popularity&key=" . config('services.google_fonts_api_key');
$url = "https://www.googleapis.com/webfonts/v1/webfonts?sort=popularity&key=" . config('services.google.fonts_api_key');
$response = Http::get($url);
if ($response->successful()) {
$fonts = collect($response->json()['items'])->filter(function ($font) {

View File

@@ -7,5 +7,7 @@ use Laravel\Socialite\Contracts\User;
interface OAuthDriver
{
public function getRedirectUrl(): string;
public function setRedirectUrl($url): self;
public function getUser(): User;
public function canCreateUser(): bool;
}

View File

@@ -10,6 +10,8 @@ use Laravel\Socialite\Two\GoogleProvider;
class OAuthGoogleDriver implements OAuthDriver
{
private ?string $redirectUrl = null;
protected GoogleProvider $provider;
public function __construct()
@@ -22,6 +24,7 @@ class OAuthGoogleDriver implements OAuthDriver
return $this->provider
->scopes([Sheets::DRIVE_FILE])
->stateless()
->redirectUrl($this->redirectUrl ?? config('services.google.redirect'))
->with([
'access_type' => 'offline',
'prompt' => 'consent select_account'
@@ -34,6 +37,19 @@ class OAuthGoogleDriver implements OAuthDriver
{
return $this->provider
->stateless()
->redirectUrl($this->redirectUrl ?? config('services.google.redirect'))
->user();
}
public function canCreateUser(): bool
{
return true;
}
public function setRedirectUrl($url): OAuthDriver
{
$this->redirectUrl = $url;
return $this;
}
}