Skip to content

Commit c848375

Browse files
committed
WIP
1 parent da5d0d2 commit c848375

10 files changed

+301
-25
lines changed

README.md

+278-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
</p>
44

55
<p align="center">
6-
An opinionated social authentication handler using Laravel Socialite.
6+
An opinionated way to authenticate users using Laravel Socialite.
77
</p>
88

99
<p align="center">
@@ -13,8 +13,284 @@ An opinionated social authentication handler using Laravel Socialite.
1313
<a href="https://packagist.org/packages/directorytree/bartender" target="_blank"><img src="https://img.shields.io/packagist/l/directorytree/bartender.svg?style=flat-square"/></a>
1414
</p>
1515

16-
### Index
16+
---
17+
18+
## Index
1719

1820
- [Requirements](#requirements)
1921
- [Installation](#installation)
22+
- [Setup](#setup)
2023
- [Usage](#usage)
24+
25+
## Requirements
26+
27+
- PHP >= 8.0
28+
- Laravel >= 9.0
29+
- Laravel Socialite >= 5.0
30+
31+
## Installation
32+
33+
You can install the package via composer:
34+
35+
```bash
36+
composer require directorytree/bartender
37+
```
38+
39+
Then, publish the migration:
40+
41+
> This will create a `provider_id` and `provider_name` column on the `users` table.
42+
43+
```bash
44+
php artisan vendor:publish --provider="DirectoryTree\Bartender\BartenderServiceProvider"
45+
```
46+
47+
Finally, run the migration:
48+
49+
```bash
50+
php artisan migrate
51+
```
52+
53+
## Setup
54+
55+
Register the authentication routes:
56+
57+
> This will register the `/auth/{driver}/redirect` and `/auth/{driver}/callback` routes.
58+
59+
```php
60+
// routes/web.php
61+
62+
use DirectoryTree\Bartender\Facades\Bartender;
63+
64+
Bartender::routes();
65+
```
66+
67+
Set up your [Socialite Providers](https://socialiteproviders.com/) in your `services.php` configuration file:
68+
69+
```php
70+
// config/services.php
71+
72+
return [
73+
// ...
74+
75+
'google' => ['...'],
76+
77+
'microsoft' => ['...'],
78+
];
79+
```
80+
81+
Register the Socialite Provider in your `AuthServiceProvider`:
82+
83+
```php
84+
// app/Providers/AuthServiceProvider.php
85+
86+
use DirectoryTree\Bartender\Bartender;
87+
88+
class AuthServiceProvider extends ServiceProvider
89+
{
90+
// ...
91+
92+
public function boot()
93+
{
94+
Bartender::registerProvider('google');
95+
Bartender::registerProvider('microsoft');
96+
}
97+
}
98+
```
99+
100+
If your application uses a `User` model outside the `App\Models` namespace, you can set it using the `Bartender` facade:
101+
102+
```php
103+
// app/Providers/AuthServiceProvider.php
104+
105+
use App\User;
106+
use DirectoryTree\Bartender\Facades\Bartender;
107+
108+
class AuthServiceProvider extends ServiceProvider
109+
{
110+
// ...
111+
112+
public function boot()
113+
{
114+
Bartender::useUserModel(User::class);
115+
}
116+
}
117+
```
118+
119+
## Usage
120+
121+
Direct your user to the `/auth/{driver}/redirect` route to authenticate with the given driver:
122+
123+
```blade
124+
<a href="{{ route('auth.driver.redirect', 'google') }}">
125+
Login with Google
126+
</a>
127+
128+
<a href="{{ route('auth.driver.redirect', 'microsoft') }}">
129+
Login with Microsoft
130+
</a>
131+
```
132+
133+
Once the user successfully authenticates, they will be redirected to the `/auth/{driver}/callback`
134+
route, and the users account will automatically be created or updated.
135+
136+
## Extending & Customizing
137+
138+
Almost everything can be swapped out in Bartender.
139+
140+
141+
If you would like to handle everything yourself for redirects and callbacks, you may create your own `ProviderHandler`:
142+
143+
```php
144+
namespace App\Socialite;
145+
146+
use Illuminate\Http\Request;
147+
use DirectoryTree\Bartender\ProviderHandler;
148+
149+
class UserProviderHandler implements ProviderHandler
150+
{
151+
/**
152+
* Constructor.
153+
*/
154+
public function __construct(
155+
protected Request $request
156+
) {
157+
}
158+
159+
/**
160+
* Handle redirecting the user to the OAuth provider.
161+
*/
162+
public function redirect(Provider $provider, string $driver): RedirectResponse
163+
{
164+
// Perform additional logic here...
165+
166+
return $provider->redirect();
167+
}
168+
169+
/**
170+
* Handle an OAuth response from the provider.
171+
*/
172+
public function callback(Provider $provider, string $driver): RedirectResponse
173+
{
174+
// Authenticate the user your own way...
175+
176+
return redirect()->route('dashboard');
177+
}
178+
}
179+
180+
```
181+
182+
183+
### User Creation & Updating
184+
185+
If you would like to customize the creation of the user provider, you can create your own
186+
`UserProvider` implementation:
187+
188+
```php
189+
namespace App\Socialite;
190+
191+
use Illuminate\Contracts\Auth\Authenticatable;
192+
use DirectoryTree\Bartender\ProviderRepository;
193+
use Laravel\Socialite\Two\User as SocialiteUser;
194+
195+
class UserProviderRepository implements ProviderRepository
196+
{
197+
/**
198+
* Determine if the user already exists under a different provider.
199+
*/
200+
public function exists(string $driver, SocialiteUser $user): bool
201+
{
202+
// ...
203+
}
204+
205+
/**
206+
* Update or create the socialite user.
207+
*/
208+
public function updateOrCreate(string $driver, SocialiteUser $user): Authenticatable
209+
{
210+
// ...
211+
}
212+
}
213+
```
214+
215+
Then, bind your implementation in the service container in your `AppServiceProvider`:
216+
217+
```php
218+
namespace App\Providers;
219+
220+
use App\Socialite\UserProviderRepository;
221+
use DirectoryTree\Bartender\ProviderRepository;
222+
223+
class AppServiceProvider extends ServiceProvider
224+
{
225+
// ...
226+
227+
public function register()
228+
{
229+
$this->app->bind(ProviderRepository::class, UserProviderRepository::class);
230+
}
231+
}
232+
```
233+
234+
### User Redirects & Flash Messaging
235+
236+
If you would like to customize the behavior of the redirects and flash messages depending
237+
on the outcome of a OAuth callback, you can create your own `ProviderRedirector` implementation:
238+
239+
```php
240+
namespace App\Socialite;
241+
242+
class UserProviderRedirector implements ProviderRedirector
243+
{
244+
/**
245+
* Redirect when unable to authenticate the user.
246+
*/
247+
public function unableToAuthenticateUser(Exception $e, string $driver): RedirectResponse
248+
{
249+
return redirect()->route('login')->with('error', 'Unable to authenticate user.');
250+
}
251+
252+
/**
253+
* Redirect when the user already exists.
254+
*/
255+
public function userAlreadyExists(SocialiteUser $user, string $driver): RedirectResponse
256+
{
257+
return redirect()->route('login')->with('error', 'User already exists.');
258+
}
259+
260+
/**
261+
* Redirect when unable to create the user.
262+
*/
263+
public function unableToCreateUser(Exception $e, SocialiteUser $user, string $driver): RedirectResponse
264+
{
265+
return redirect()->route('login')->with('error', 'Unable to create user.');
266+
}
267+
268+
/**
269+
* Handle when the user has been successfully authenticated.
270+
*/
271+
public function userAuthenticated(Authenticatable $user, SocialiteUser $socialite, string $driver): RedirectResponse
272+
{
273+
Auth::login($user);
274+
275+
return redirect()->route('dashboard');
276+
}
277+
}
278+
```
279+
280+
Then, bind your implementation in the service container in your `AppServiceProvider`:
281+
282+
```php
283+
namespace App\Providers;
284+
285+
use App\Socialite\UserProviderRedirector;
286+
287+
class AppServiceProvider extends ServiceProvider
288+
{
289+
// ...
290+
291+
public function register()
292+
{
293+
$this->app->bind(ProviderRedirector::class, UserProviderRedirector::class);
294+
}
295+
}
296+
```

src/BartenderManager.php

+4-4
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,12 @@ protected function handler(string $driver): ProviderHandler
8787
*/
8888
public function routes(): void
8989
{
90-
Route::name('auth.redirect')
90+
Route::name('auth.driver.redirect')
9191
->whereIn('driver', array_keys($this->handlers))
92-
->get('auth/redirect/{driver}', [AuthController::class, 'redirect']);
92+
->get('auth/{driver}/redirect', [AuthController::class, 'redirect']);
9393

94-
Route::name('auth.callback')
94+
Route::name('auth.driver.callback')
9595
->whereIn('driver', array_keys($this->handlers))
96-
->get('auth/callback/{driver}', [AuthController::class, 'callback']);
96+
->get('auth/{driver}/callback', [AuthController::class, 'callback']);
9797
}
9898
}

src/BartenderServiceProvider.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public function register(): void
1313
{
1414
$this->app->singleton(BartenderManager::class);
1515

16-
$this->app->bind(ProviderQuery::class, UserProviderQuery::class);
16+
$this->app->bind(ProviderRepository::class, UserProviderRepository::class);
1717
$this->app->bind(ProviderRedirector::class, UserProviderRedirector::class);
1818
}
1919

src/Facades/Bartender.php

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use DirectoryTree\Bartender\BartenderManager;
77

88
/**
9+
* @method static void routes()
910
* @method static void useUserModel(string $userModel)
1011
* @method static \Illuminate\Database\Eloquent\Model user()
1112
* @method static void register(string $driver, string $handler)

src/ProviderQuery.php src/ProviderRepository.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use Illuminate\Contracts\Auth\Authenticatable;
66
use Laravel\Socialite\Two\User as SocialiteUser;
77

8-
interface ProviderQuery
8+
interface ProviderRepository
99
{
1010
/**
1111
* Determine if the user already exists under a different provider.

src/UserProviderHandler.php

+1-3
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class UserProviderHandler implements ProviderHandler
1414
* Constructor.
1515
*/
1616
public function __construct(
17-
protected ProviderQuery $users,
17+
protected ProviderRepository $users,
1818
protected ProviderRedirector $redirector,
1919
) {
2020
}
@@ -49,8 +49,6 @@ public function callback(Provider $provider, string $driver): RedirectResponse
4949
return $this->redirector->unableToCreateUser($e, $socialite, $driver);
5050
}
5151

52-
Auth::login($user);
53-
5452
return $this->redirector->userAuthenticated($user, $socialite, $driver);
5553
}
5654
}

src/UserProviderRedirector.php

+3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Illuminate\Contracts\Auth\Authenticatable;
77
use Illuminate\Http\RedirectResponse;
88
use Illuminate\Routing\Redirector;
9+
use Illuminate\Support\Facades\Auth;
910
use Laravel\Socialite\Two\User as SocialiteUser;
1011

1112
class UserProviderRedirector implements ProviderRedirector
@@ -56,6 +57,8 @@ public function unableToCreateUser(Exception $e, SocialiteUser $user, string $dr
5657
*/
5758
public function userAuthenticated(Authenticatable $user, SocialiteUser $socialite, string $driver): RedirectResponse
5859
{
60+
Auth::login($user);
61+
5962
return redirect('dashboard');
6063
}
6164

src/UserProviderQuery.php src/UserProviderRepository.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
use Illuminate\Support\Str;
1212
use Laravel\Socialite\Two\User as SocialiteUser;
1313

14-
class UserProviderQuery implements ProviderQuery
14+
class UserProviderRepository implements ProviderRepository
1515
{
1616
/**
1717
* Determine if the user already exists under a different provider.

0 commit comments

Comments
 (0)