texhub / social-auth
Simple, elegant social OAuth 2.0 login for any PHP framework with first-class Laravel support β Google & GitHub out of the box, easily extensible.
Requires
- php: ^8.2
- ext-curl: *
- ext-json: *
Requires (Dev)
- illuminate/support: ^11.0 || ^12.0 || ^13.0
- phpunit/phpunit: ^11.0 || ^12.0
Suggests
- illuminate/support: Required to use the package inside a Laravel application (service provider, facade, config publishing).
README
π English Β· Π ΡΡΡΠΊΠΈΠΉ
Simple, elegant social OAuth 2.0 login for any PHP framework β Google & GitHub out of the box, easily extensible β with first-class Laravel support.
β¨ Features
- π OAuth 2.0 Authorization Code flow done for you
- π’ Google & π GitHub providers built in (GitHub falls back to the verified primary email)
- π€ Normalized
Userβid,nickname,name,email,avatar,token,raw - π‘ CSRF state helper, custom scopes & extra params
- π§© Extensible β add your own provider in a few lines
- π§ͺ Fully unit-tested (no network), pluggable HTTP transport
π¦ Installation
composer require texhub/social-auth
Requirements: PHP β₯ 8.2 with curl, json.
π Quick start
use TexHub\SocialAuth\SocialAuth; $social = SocialAuth::fromArray([ 'google' => [ 'client_id' => '...', 'client_secret' => '...', 'redirect' => 'https://app.tj/auth/google/callback', ], 'github' => [ 'client_id' => '...', 'client_secret' => '...', 'redirect' => 'https://app.tj/auth/github/callback', ], ]); // 1) Send the user to the provider (store the state in the session for CSRF): $state = SocialAuth::generateState(); $_SESSION['oauth_state'] = $state; header('Location: ' . $social->driver('google')->redirectUrl($state)); // 2) On your callback, verify state then get the user: if (($_GET['state'] ?? null) !== ($_SESSION['oauth_state'] ?? null)) { exit('Invalid state'); } $user = $social->driver('google')->userFromCode($_GET['code']); $user->id; // provider user id $user->name; // full name $user->email; // email $user->avatar; // avatar URL $user->nickname; // login/handle (GitHub) $user->token->accessToken; // OAuth access token $user->token->refreshToken; // when available (Google offline access)
π’ Google / π GitHub
Both work identically β just switch the driver name:
$social->driver('github')->redirectUrl($state); $user = $social->driver('github')->userFromCode($code);
Default scopes: Google β openid profile email, GitHub β read:user user:email.
Override per provider via scopes, and add provider params via extra
(e.g. Google ['access_type' => 'offline', 'prompt' => 'consent'] for a refresh token).
π§© Add your own provider
use TexHub\SocialAuth\Providers\AbstractProvider; use TexHub\SocialAuth\User; final class FacebookProvider extends AbstractProvider { protected function authorizeUrl(): string { return 'https://www.facebook.com/v19.0/dialog/oauth'; } protected function tokenUrl(): string { return 'https://graph.facebook.com/v19.0/oauth/access_token'; } protected function defaultScopes(): array { return ['email', 'public_profile']; } protected function fetchUser(string $token): array { return $this->get('https://graph.facebook.com/me?fields=id,name,email,picture', $token); } protected function mapUser(array $raw): User { return new User((string) $raw['id'], null, $raw['name'] ?? null, $raw['email'] ?? null, $raw['picture']['data']['url'] ?? null, $raw); } } $social->extend('facebook', FacebookProvider::class) ->configure('facebook', \TexHub\SocialAuth\ProviderConfig::fromArray($cfg));
βοΈ Error handling
use TexHub\SocialAuth\Exceptions\ApiException; use TexHub\SocialAuth\Exceptions\ConfigurationException; try { $user = $social->driver('google')->userFromCode($code); } catch (ApiException $e) { // token exchange / profile error β $e->getMessage(), $e->httpStatus, $e->payload } catch (ConfigurationException $e) { // unknown / unconfigured provider }
π§© Laravel
Auto-discovered. Publish config:
php artisan vendor:publish --tag=social-auth-config
.env:
GOOGLE_CLIENT_ID=... GOOGLE_CLIENT_SECRET=... GOOGLE_REDIRECT_URI=https://app.tj/auth/google/callback GITHUB_CLIENT_ID=... GITHUB_CLIENT_SECRET=... GITHUB_REDIRECT_URI=https://app.tj/auth/github/callback
Controller:
use Illuminate\Http\Request; use TexHub\SocialAuth\Laravel\SocialAuth; public function redirect(string $provider, Request $request) { $state = \TexHub\SocialAuth\SocialAuth::generateState(); $request->session()->put('oauth_state', $state); return redirect()->away(SocialAuth::driver($provider)->redirectUrl($state)); } public function callback(string $provider, Request $request) { abort_unless($request->query('state') === $request->session()->pull('oauth_state'), 419); $user = SocialAuth::driver($provider)->userFromCode($request->query('code')); $account = User::updateOrCreate( ['provider' => $provider, 'provider_id' => $user->id], ['name' => $user->name, 'email' => $user->email, 'avatar' => $user->avatar], ); auth()->login($account); return redirect('/dashboard'); }
Routes:
Route::get('/auth/{provider}', [AuthController::class, 'redirect']); Route::get('/auth/{provider}/callback', [AuthController::class, 'callback']);
π§ͺ Testing
Inject a fake transport β no network needed:
use TexHub\SocialAuth\SocialAuth; use TexHub\SocialAuth\Tests\Support\FakeTransport; $t = (new FakeTransport()) ->on('oauth2.googleapis.com/token', ['access_token' => 't', 'token_type' => 'Bearer']) ->on('userinfo', ['sub' => '1', 'email' => 'a@b.c', 'name' => 'A']); $social = SocialAuth::fromArray($configs, $t); $user = $social->driver('google')->userFromCode('code');
composer install && composer test
π Architecture
src/
βββ SocialAuth.php # manager / driver factory
βββ ProviderConfig.php # per-provider client id/secret/redirect/scopes
βββ Token.php Β· User.php # normalized value objects
βββ Contracts/Provider.php # provider interface
βββ Providers/ # AbstractProvider, GoogleProvider, GitHubProvider
βββ Http/ # Transport, CurlTransport, RawResponse
βββ Exceptions/ # ApiException, ConfigurationException, β¦
βββ Laravel/ # ServiceProvider + Facade
License
MIT Β© TexHub Pro β built by Mahmudi Shodmehr.