# Laravel Socialite Slim Laravel Socialite Slim is a lightweight OAuth authentication package that provides a fluent, expressive interface for authenticating with Google, GitHub, and Telegram. Built as a slim alternative to Laravel Socialite, it eliminates the boilerplate code traditionally required for OAuth integrations while maintaining flexibility and ease of use. The package handles the complete OAuth flow from redirecting users to provider authentication pages through retrieving and managing user data. The package includes two main features: basic OAuth authentication for retrieving user information from providers, and an advanced OAuth connected users system for managing persistent OAuth connections. The connected users feature allows you to link OAuth accounts to existing user records, track access tokens and refresh tokens, monitor token expiration, and manage multiple OAuth connections per user. This makes it ideal for applications that need to support social login, allow users to connect multiple OAuth providers, or integrate with third-party APIs using OAuth credentials. ## Installation and Configuration Install via Composer and configure service providers ```bash composer require saeedvir/socialite-slim ``` ```php // config/app.php 'providers' => [ Saeedvir\SocialiteSlim\SocialiteServiceProvider::class, ], 'aliases' => [ 'Socialite' => Saeedvir\SocialiteSlim\Facades\Socialite::class, 'OAuth' => Saeedvir\SocialiteSlim\Facades\OAuth::class, ], ``` ```php // config/services.php 'github' => [ 'client_id' => env('GITHUB_CLIENT_ID'), 'client_secret' => env('GITHUB_CLIENT_SECRET'), 'redirect' => 'http://your-app.com/auth/github/callback', ], 'google' => [ 'client_id' => env('GOOGLE_CLIENT_ID'), 'client_secret' => env('GOOGLE_CLIENT_SECRET'), 'redirect' => 'http://your-app.com/auth/google/callback', ], 'telegram' => [ 'client_id' => env('TELEGRAM_CLIENT_ID'), 'client_secret' => env('TELEGRAM_CLIENT_SECRET'), 'redirect' => 'http://your-app.com/auth/telegram/callback', ], ``` ```bash # Publish migrations for OAuth connected users feature php artisan vendor:publish --provider="Saeedvir\SocialiteSlim\SocialiteServiceProvider" --tag="socialite-migrations" php artisan migrate ``` ## Basic OAuth Authentication Redirect users to provider and retrieve authenticated user data ```php redirect(); } public function handleProviderCallback($provider) { try { // Retrieve user from provider $user = Socialite::driver($provider)->user(); // Access user properties $providerId = $user->getId(); // Provider's user ID $name = $user->getName(); // User's full name $email = $user->getEmail(); // User's email $avatar = $user->getAvatar(); // Avatar URL $nickname = $user->getNickname(); // Username/nickname $accessToken = $user->token; // OAuth access token $refreshToken = $user->refreshToken; // OAuth refresh token $expiresIn = $user->expiresIn; // Token expiration seconds $rawData = $user->getRaw(); // Raw provider response // Create or update user in your database $localUser = User::updateOrCreate( ['email' => $email], [ 'name' => $name, 'avatar' => $avatar, 'github_id' => $provider === 'github' ? $providerId : null, 'google_id' => $provider === 'google' ? $providerId : null, ] ); // Log the user in auth()->login($localUser); return redirect()->route('dashboard')->with('success', 'Successfully authenticated!'); } catch (\Exception $e) { return redirect()->route('login')->with('error', 'Authentication failed: ' . $e->getMessage()); } } } ``` ```php // routes/web.php Route::prefix('auth')->group(function () { Route::get('/{provider}/redirect', [AuthController::class, 'redirectToProvider']) ->name('auth.redirect'); Route::get('/{provider}/callback', [AuthController::class, 'handleProviderCallback']) ->name('auth.callback'); }); ``` ## GitHub Provider Authentication Authenticate with GitHub and access GitHub-specific user data ```php scopes(['user:email', 'read:user', 'repo']) ->redirect(); } // Handle GitHub callback public function handleGithubCallback() { try { $user = Socialite::driver('github')->user(); // GitHub-specific fields $githubId = $user->getId(); // GitHub user ID $username = $user->getNickname(); // GitHub username (login) $nodeId = $user->user['node_id']; // GitHub node ID $avatarUrl = $user->getAvatar(); // Avatar URL $email = $user->getEmail(); // Primary verified email $name = $user->getName(); // Full name // Store or update user $localUser = User::updateOrCreate( ['github_id' => $githubId], [ 'name' => $name ?? $username, 'email' => $email, 'github_username' => $username, 'avatar' => $avatarUrl, 'github_token' => $user->token, ] ); auth()->login($localUser); return redirect()->route('dashboard'); } catch (\Exception $e) { return redirect()->route('login')->withErrors(['error' => 'GitHub authentication failed']); } } ``` ## Google Provider Authentication Authenticate with Google using OAuth 2.0 and OpenID Connect ```php scopes(['openid', 'profile', 'email']) ->redirect(); } // Handle Google callback public function handleGoogleCallback() { try { $user = Socialite::driver('google')->user(); // Google-specific fields $googleId = $user->getId(); // Google user ID (sub) $email = $user->getEmail(); // Email address $name = $user->getName(); // Full name $avatar = $user->getAvatar(); // Profile picture URL $emailVerified = $user->user['email_verified']; // Email verification status // Access raw Google data $rawUser = $user->getRaw(); $givenName = $rawUser['given_name'] ?? null; $familyName = $rawUser['family_name'] ?? null; $locale = $rawUser['locale'] ?? null; // Create or update user $localUser = User::updateOrCreate( ['google_id' => $googleId], [ 'name' => $name, 'email' => $email, 'avatar' => $avatar, 'email_verified_at' => $emailVerified ? now() : null, 'google_token' => $user->token, 'google_refresh_token' => $user->refreshToken, ] ); auth()->login($localUser); return redirect()->route('dashboard'); } catch (\Exception $e) { return redirect()->route('login')->withErrors(['error' => 'Google authentication failed']); } } // Refresh Google access token public function refreshGoogleToken($refreshToken) { try { $token = Socialite::driver('google')->refreshToken($refreshToken); // Update stored token auth()->user()->update([ 'google_token' => $token->accessToken, 'google_refresh_token' => $token->refreshToken, ]); return response()->json(['message' => 'Token refreshed successfully']); } catch (\Exception $e) { return response()->json(['error' => 'Token refresh failed'], 400); } } ``` ## Telegram Provider Authentication Authenticate with Telegram OAuth ```php redirect(); } // Handle Telegram callback public function handleTelegramCallback() { try { $user = Socialite::driver('telegram')->user(); // Telegram-specific fields $telegramId = $user->getId(); // Telegram user ID $username = $user->getNickname(); // Telegram username $name = $user->getName(); // Full name $accessToken = $user->token; // Access token // Create or update user $localUser = User::updateOrCreate( ['telegram_id' => $telegramId], [ 'name' => $name ?? $username, 'telegram_username' => $username, 'telegram_token' => $accessToken, ] ); auth()->login($localUser); return redirect()->route('dashboard'); } catch (\Exception $e) { return redirect()->route('login')->withErrors(['error' => 'Telegram authentication failed']); } } ``` ## OAuth Connected Users - User Model Setup Add OAuth connections trait to User model for managing multiple OAuth accounts ```php 'datetime', ]; } ``` ```php // Usage: Access OAuth connections through the trait $user = User::find(1); // Get all OAuth connections for the user $connections = $user->oauthConnections; // Returns Collection of OauthConnectedUser models // Check if user has specific OAuth connection $hasGithub = $user->hasOauthConnection('github'); // true/false $hasGoogle = $user->hasOauthConnection('google'); // true/false // Get specific OAuth connection $githubConnection = $user->getOauthConnection('github'); // Returns OauthConnectedUser model or null // Access connection properties if ($githubConnection) { echo $githubConnection->provider; // 'github' echo $githubConnection->provider_id; // GitHub user ID echo $githubConnection->email; // Email from GitHub echo $githubConnection->name; // Name from GitHub echo $githubConnection->avatar; // Avatar URL echo $githubConnection->access_token; // OAuth access token echo $githubConnection->refresh_token; // OAuth refresh token echo $githubConnection->expires_at; // Token expiration datetime } // Connect a new OAuth account to user $oauthUser = OauthConnectedUser::find(1); $user->connectOauthAccount($oauthUser); ``` ## OAuth Connected Users - Complete Authentication Flow Full implementation of OAuth authentication with connected users management ```php redirect(); } public function handleProviderCallback($provider) { try { // Get user from OAuth provider $oauthUser = Socialite::driver($provider)->user(); // Prepare user data for storage $userData = [ 'email' => $oauthUser->getEmail(), 'name' => $oauthUser->getName(), 'nickname' => $oauthUser->getNickname(), 'avatar' => $oauthUser->getAvatar(), 'access_token' => $oauthUser->token, 'refresh_token' => $oauthUser->refreshToken ?? null, 'expires_at' => isset($oauthUser->expiresIn) ? now()->addSeconds($oauthUser->expiresIn) : null, ]; // Find or create OAuth connected user $connectedUser = OAuth::findOrCreateOauthUser( $provider, $oauthUser->getId(), $userData ); // Case 1: User is already logged in - connect OAuth account to current user if (auth()->check()) { OAuth::connectToUser($connectedUser, auth()->user()); return redirect()->route('settings.connections') ->with('success', ucfirst($provider) . ' account connected successfully!'); } // Case 2: OAuth account already connected to a local user - log them in if ($connectedUser->user) { auth()->login($connectedUser->user); return redirect()->route('dashboard'); } // Case 3: User exists with same email - connect and log them in if ($userData['email']) { $user = User::where('email', $userData['email'])->first(); if ($user) { OAuth::connectToUser($connectedUser, $user); auth()->login($user); return redirect()->route('dashboard'); } } // Case 4: New user - create account and connect OAuth $user = User::create([ 'name' => $userData['name'] ?? $userData['nickname'], 'email' => $userData['email'], 'avatar' => $userData['avatar'], 'email_verified_at' => now(), // Trust OAuth provider email ]); OAuth::connectToUser($connectedUser, $user); auth()->login($user); return redirect()->route('dashboard') ->with('success', 'Account created successfully!'); } catch (\Exception $e) { return redirect()->route('login') ->withErrors(['error' => 'Failed to authenticate with ' . ucfirst($provider)]); } } // Display user's OAuth connections public function index() { $connections = auth()->user()->oauthConnections; return view('oauth.connections', [ 'connections' => $connections, 'hasGithub' => auth()->user()->hasOauthConnection('github'), 'hasGoogle' => auth()->user()->hasOauthConnection('google'), 'hasTelegram' => auth()->user()->hasOauthConnection('telegram'), ]); } // Remove OAuth connection public function destroy($id) { $connection = \Saeedvir\SocialiteSlim\Models\OauthConnectedUser::findOrFail($id); // Verify ownership if ($connection->user_id !== auth()->id()) { abort(403, 'Unauthorized action.'); } $provider = $connection->provider; $connection->delete(); return redirect()->back() ->with('success', ucfirst($provider) . ' connection removed successfully!'); } } ``` ## OAuth Facade Methods Use the OAuth facade for managing OAuth connections programmatically ```php 'user@example.com', 'name' => 'John Doe', 'nickname' => 'johndoe', 'avatar' => 'https://avatar.url/image.jpg', 'access_token' => 'ghp_xxxxxxxxxxxxxxxxxxxx', 'refresh_token' => 'refresh_token_here', 'expires_at' => now()->addHours(2), ] ); // Returns OauthConnectedUser model (creates new or updates existing) // Connect OAuth user to existing user account $user = User::find(1); $connectedUser = OauthConnectedUser::find(5); OAuth::connectToUser($connectedUser, $user); // Updates $connectedUser->user_id to link accounts // Get OAuth user by provider and provider ID $oauthUser = OAuth::getOauthUser('google', '109876543210'); // Returns OauthConnectedUser model or null // Check if user has OAuth connection for specific provider $user = User::find(1); $hasConnection = OAuth::userHasOauthConnection($user, 'github'); // Returns boolean // Complete example: Handle new OAuth login $provider = 'github'; $providerId = '87654321'; $userData = [ 'email' => 'jane@example.com', 'name' => 'Jane Smith', 'nickname' => 'janesmith', 'avatar' => 'https://github.com/avatar.jpg', 'access_token' => 'ghp_newtoken123456789', 'refresh_token' => null, 'expires_at' => now()->addYear(), ]; // Step 1: Find or create OAuth connection $oauthConnection = OAuth::findOrCreateOauthUser($provider, $providerId, $userData); // Step 2: Check if already connected to user if ($oauthConnection->user_id) { // User exists, log them in auth()->login($oauthConnection->user); } else { // Step 3: Try to find user by email $existingUser = User::where('email', $userData['email'])->first(); if ($existingUser) { // Connect to existing user OAuth::connectToUser($oauthConnection, $existingUser); auth()->login($existingUser); } else { // Create new user and connect $newUser = User::create([ 'name' => $userData['name'], 'email' => $userData['email'], 'avatar' => $userData['avatar'], ]); OAuth::connectToUser($oauthConnection, $newUser); auth()->login($newUser); } } ``` ## OAuth Connected User Model Methods Query and manage OAuth connections using the OauthConnectedUser model ```php get(); // Query by provider and provider ID $connection = OauthConnectedUser::provider('google') ->providerId('109876543210') ->first(); // Get all connections for a specific user $userConnections = OauthConnectedUser::where('user_id', 1)->get(); // Check if token is expired $connection = OauthConnectedUser::find(1); $isExpired = $connection->isTokenExpired(); // Returns true if expires_at is in the past, false otherwise // Access connection properties foreach ($userConnections as $connection) { echo $connection->provider; // 'github', 'google', 'telegram' echo $connection->provider_id; // Provider's user ID echo $connection->email; // Email from provider echo $connection->name; // Name from provider echo $connection->nickname; // Username/nickname echo $connection->avatar; // Avatar URL echo $connection->access_token; // OAuth access token echo $connection->refresh_token; // OAuth refresh token (nullable) echo $connection->expires_at; // Carbon datetime or null echo $connection->created_at; // When connection was created echo $connection->updated_at; // When connection was last updated } // Get the associated user $connection = OauthConnectedUser::find(1); $user = $connection->user; // Returns User model or null // Update token after refresh $connection = OauthConnectedUser::provider('google') ->providerId('109876543210') ->first(); $connection->update([ 'access_token' => 'new_access_token_here', 'refresh_token' => 'new_refresh_token_here', 'expires_at' => now()->addHour(), ]); // Find expired tokens that need refresh $expiredConnections = OauthConnectedUser::whereNotNull('expires_at') ->where('expires_at', '<', now()) ->whereNotNull('refresh_token') ->get(); foreach ($expiredConnections as $connection) { // Refresh token logic if ($connection->isTokenExpired() && $connection->refresh_token) { $newToken = Socialite::driver($connection->provider) ->refreshToken($connection->refresh_token); $connection->update([ 'access_token' => $newToken->accessToken, 'refresh_token' => $newToken->refreshToken, 'expires_at' => now()->addSeconds($newToken->expiresIn), ]); } } ``` ## Stateless Authentication Use stateless mode for API authentication without sessions ```php stateless() ->redirect(); } // Handle callback without session state verification public function handleCallbackStateless($provider) { try { $user = Socialite::driver($provider) ->stateless() ->user(); // Process user data return response()->json([ 'id' => $user->getId(), 'name' => $user->getName(), 'email' => $user->getEmail(), 'avatar' => $user->getAvatar(), 'token' => $user->token, ]); } catch (\Exception $e) { return response()->json(['error' => 'Authentication failed'], 401); } } // Get user from existing token public function getUserFromToken($token, $provider) { try { $user = Socialite::driver($provider)->userFromToken($token); return response()->json([ 'id' => $user->getId(), 'name' => $user->getName(), 'email' => $user->getEmail(), 'avatar' => $user->getAvatar(), ]); } catch (\Exception $e) { return response()->json(['error' => 'Invalid token'], 401); } } ``` ## Advanced Configuration Customize OAuth providers with additional parameters and scopes ```php scopes(['user:email', 'read:org', 'repo', 'gist']) ->redirect(); } // Set custom redirect URL dynamically public function redirectWithCustomUrl() { $redirectUrl = route('auth.callback.custom', ['provider' => 'google']); return Socialite::driver('google') ->redirectUrl($redirectUrl) ->redirect(); } // Add custom parameters to OAuth request public function redirectWithParameters() { return Socialite::driver('google') ->with(['access_type' => 'offline', 'prompt' => 'consent']) ->redirect(); } // Combine multiple options public function redirectWithAllOptions() { return Socialite::driver('github') ->scopes(['user:email', 'read:user']) ->with(['allow_signup' => 'true']) ->redirectUrl(route('auth.github.callback')) ->redirect(); } // Enable PKCE (Proof Key for Code Exchange) public function redirectWithPKCE() { return Socialite::driver('google') ->enablePKCE() ->redirect(); } ``` ## Error Handling and Validation Handle OAuth errors and validate responses ```php user(); // Validate required fields if (!$user->getEmail()) { return redirect()->route('login') ->withErrors(['error' => 'Email not provided by ' . ucfirst($provider)]); } // Process user... return redirect()->route('dashboard'); } catch (InvalidStateException $e) { // State mismatch - possible CSRF attack return redirect()->route('login') ->withErrors(['error' => 'Invalid state parameter. Please try again.']); } catch (DriverMissingConfigurationException $e) { // Missing configuration \Log::error('OAuth configuration error: ' . $e->getMessage()); return redirect()->route('login') ->withErrors(['error' => 'OAuth provider not properly configured.']); } catch (\GuzzleHttp\Exception\ClientException $e) { // HTTP client error (4xx) $statusCode = $e->getResponse()->getStatusCode(); \Log::error('OAuth HTTP error: ' . $statusCode); return redirect()->route('login') ->withErrors(['error' => 'Failed to authenticate with provider.']); } catch (\Exception $e) { // General exception \Log::error('OAuth error: ' . $e->getMessage()); return redirect()->route('login') ->withErrors(['error' => 'An unexpected error occurred.']); } } // Validate OAuth connection before usage public function validateConnection($connectionId) { $connection = OauthConnectedUser::find($connectionId); if (!$connection) { throw new \Exception('OAuth connection not found'); } if ($connection->user_id !== auth()->id()) { throw new \Exception('Unauthorized access to OAuth connection'); } if ($connection->isTokenExpired()) { if (!$connection->refresh_token) { throw new \Exception('OAuth token expired and no refresh token available'); } // Attempt to refresh token try { $newToken = Socialite::driver($connection->provider) ->refreshToken($connection->refresh_token); $connection->update([ 'access_token' => $newToken->accessToken, 'refresh_token' => $newToken->refreshToken ?? $connection->refresh_token, 'expires_at' => now()->addSeconds($newToken->expiresIn), ]); } catch (\Exception $e) { throw new \Exception('Failed to refresh OAuth token: ' . $e->getMessage()); } } return $connection; } ``` ## Summary Laravel Socialite Slim provides a comprehensive OAuth authentication solution for Laravel applications with support for GitHub, Google, and Telegram providers. The basic authentication flow allows developers to quickly implement social login by redirecting users to provider authentication pages and retrieving user information with just a few lines of code. The package returns standardized user objects with consistent methods for accessing profile data regardless of the provider, making it easy to integrate multiple OAuth providers without learning different APIs. The advanced OAuth connected users feature transforms Socialite Slim into a complete identity management system. By using the HasOAuthConnections trait and OAuth facade, applications can track multiple OAuth connections per user, handle token refresh automatically, and manage complex authentication scenarios such as linking multiple social accounts to a single user or converting OAuth logins into full user accounts. The system stores all OAuth data in a dedicated database table with proper relationships, making it ideal for applications that need to maintain long-term OAuth integrations, support multiple login methods, or interact with provider APIs using stored access tokens. Whether building a simple social login or a complex multi-provider authentication system, Socialite Slim provides the flexibility and tools needed for production-ready OAuth implementations.