Skip to content

Commit 26abdc8

Browse files
authored
Merge pull request #12 from DirectoryTree/provider-tokens
Add ability to store Socialite provider tokens
2 parents 9c70549 + cfbf422 commit 26abdc8

7 files changed

+139
-6
lines changed

README.md

+66-3
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ Almost everything in Bartender can be customized.
2525
- [Installation](#installation)
2626
- [Setup](#setup)
2727
- [Usage](#usage)
28+
- [Soft Deletes](#soft-deletes)
29+
- [Email Verification](#email-verification)
30+
- [Access/Refresh Tokens](#accessrefresh-tokens)
2831
- [Extending & Customizing](#extending--customizing)
2932

3033
## Requirements
@@ -41,9 +44,14 @@ You can install the package via composer:
4144
composer require directorytree/bartender
4245
```
4346

44-
Then, publish the migration:
47+
Then, publish the migrations. They will create the required columns on the `users` table:
4548

46-
> It creates the `provider_id` and `provider_name` column on the `users` table.
49+
- `provider_id`
50+
- `provider_name`
51+
- `provider_access_token`
52+
- `provider_refresh_token`
53+
54+
> If your application does not need to store/access provider tokens, you may delete the `2024_10_27_131354_add_provider_token_columns_to_users_table.php` migration.
4755
4856
```bash
4957
php artisan vendor:publish --provider="DirectoryTree\Bartender\BartenderServiceProvider"
@@ -164,10 +172,63 @@ To change this behaviour, [swap out the repository](#user-creation--updating).
164172

165173
### Email Verification
166174

167-
With the default `UserProviderRepository`, users will emails will be automatically verified (via the `email_verified_at` column) if it is not already set.
175+
With the default `UserProviderRepository`, users with emails will be automatically verified (via the `email_verified_at` column) if it is not already set.
168176

169177
To change this behaviour, [swap out the repository](#user-creation--updating).
170178

179+
### Access/Refresh Tokens
180+
181+
To enable storing the authentication provider access and refresh tokens
182+
on your user so that you can access them later, you may apply the
183+
`StoresProviderTokens` interface on your model:
184+
185+
```php
186+
// app/Models/User.php
187+
188+
namespace App\Models;
189+
190+
use DirectoryTree\Bartender\StoresProviderTokens;
191+
192+
class User extends Authenticatable implements StoresProviderTokens
193+
{
194+
// ...
195+
}
196+
```
197+
198+
You may also want to add these columns to your model's `$hidden` attributes, as well as `encrypted` casts for additional security:
199+
200+
```php
201+
// app/Models/User.php
202+
203+
class User extends Authenticatable implements StoresProviderTokens
204+
{
205+
/**
206+
* The attributes that should be hidden for serialization.
207+
*/
208+
protected $hidden = [
209+
'provider_access_token',
210+
'provider_refresh_token'
211+
];
212+
213+
/**
214+
* Get the attributes that should be cast.
215+
*/
216+
protected function casts(): array
217+
{
218+
return [
219+
'provider_access_token' => 'encrypted',
220+
'provider_refresh_token' => 'encrypted',
221+
];
222+
}
223+
}
224+
```
225+
226+
Otherwise, if you do not need to store these tokens, you are free to delete the
227+
published `2024_10_27_131354_add_provider_token_columns_to_users_table.php`
228+
migration file and omit applying the `StoresProviderTokens` interface.
229+
Bartender will skip storing these tokens as it does not
230+
require them to successfully authenticate users.
231+
171232
## Extending & Customizing
172233

173234
Almost everything can be swapped out in Bartender.
@@ -242,6 +303,8 @@ You may also extend the built-in `UserProviderHandler` implementation if you pre
242303
For example, if you need to adjust the scopes for a single provider:
243304

244305
```php
306+
// app/Socialite/MicrosoftUserHandler.php
307+
245308
namespace App\Socialite;
246309

247310
use Illuminate\Http\RedirectResponse;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration
8+
{
9+
/**
10+
* Run the migrations.
11+
*/
12+
public function up(): void
13+
{
14+
Schema::table('users', function (Blueprint $table) {
15+
$table->after('provider_name', function (Blueprint $table) {
16+
$table->string('provider_access_token')->nullable();
17+
$table->string('provider_refresh_token')->nullable();
18+
});
19+
});
20+
}
21+
22+
/**
23+
* Reverse the migrations.
24+
*/
25+
public function down(): void
26+
{
27+
Schema::table('users', function (Blueprint $table) {
28+
$table->dropColumn([
29+
'provider_access_token',
30+
'provider_refresh_token',
31+
]);
32+
});
33+
}
34+
};

src/BartenderServiceProvider.php

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public function boot(): void
2424
{
2525
$this->publishes([
2626
__DIR__.'/../database/migrations/2024_03_31_000001_add_provider_columns_to_users_table.php' => database_path('migrations/2024_03_31_000001_add_provider_columns_to_users_table.php'),
27+
__DIR__.'/../database/migrations/2024_10_27_131354_add_provider_token_columns_to_users_table.php' => database_path('migrations/2024_10_27_131354_add_provider_token_columns_to_users_table.php'),
2728
], 'bartender-migrations');
2829
}
2930

src/StoresProviderTokens.php

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php
2+
3+
namespace DirectoryTree\Bartender;
4+
5+
interface StoresProviderTokens {}

src/UserProviderRepository.php

+25-2
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,12 @@ public function updateOrCreate(string $driver, SocialiteUser $user): Authenticat
5454
: [],
5555
$this->isVerifyingEmails($model)
5656
? ['email_verified_at' => $eloquent->email_verified_at ?? now()]
57-
: []
57+
: [],
58+
$this->isStoringTokens($model)
59+
? [
60+
'provider_access_token' => $user->token,
61+
'provider_refresh_token' => $this->getRefreshToken($user, $eloquent->provider_refresh_token),
62+
] : [],
5863
)
5964
)->save();
6065

@@ -77,6 +82,16 @@ protected function getNewPassword(): string
7782
return Str::random();
7883
}
7984

85+
/**
86+
* Get the refresh token from the Socialite user.
87+
*/
88+
protected function getRefreshToken(SocialiteUser $user, ?string $default = null): ?string
89+
{
90+
return $user->refreshToken
91+
?? $user->tokenSecret
92+
?? $default;
93+
}
94+
8095
/**
8196
* Get a new user query instance.
8297
*
@@ -92,7 +107,15 @@ protected function newUserQuery(string $model): Builder
92107
}
93108

94109
/**
95-
* Determine if the given model uses soft deletes.
110+
* Determine if the given model is storing Socialite tokens.
111+
*/
112+
protected function isStoringTokens(string $model): bool
113+
{
114+
return in_array(StoresProviderTokens::class, class_implements($model));
115+
}
116+
117+
/**
118+
* Determine if the given model is using soft deletes.
96119
*/
97120
protected function isUsingSoftDeletes(string $model): bool
98121
{

tests/User.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22

33
namespace DirectoryTree\Bartender\Tests;
44

5+
use DirectoryTree\Bartender\StoresProviderTokens;
56
use Illuminate\Auth\MustVerifyEmail;
67
use Illuminate\Foundation\Auth\User as Authenticatable;
78

8-
class User extends Authenticatable
9+
class User extends Authenticatable implements StoresProviderTokens
910
{
1011
use MustVerifyEmail;
1112

tests/UserProviderRepositoryTest.php

+6
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@
4848
$user->id = '1';
4949
$user->name = 'foo';
5050
$user->email = '[email protected]';
51+
$user->token = '1234';
52+
$user->refreshToken = '2345';
5153
});
5254

5355
$user = (new UserProviderRepository)->updateOrCreate('foo', $socialite);
@@ -57,6 +59,8 @@
5759
expect($user->email)->toBe('[email protected]');
5860
expect($user->provider_id)->toBe('1');
5961
expect($user->provider_name)->toBe('foo');
62+
expect($user->provider_access_token)->toBe('1234');
63+
expect($user->provider_refresh_token)->toBe('2345');
6064
});
6165

6266
it('updates user not associated to provider', function () {
@@ -81,6 +85,8 @@
8185
expect($user->email)->toBe('[email protected]');
8286
expect($user->provider_id)->toBe('1');
8387
expect($user->provider_name)->toBe('foo');
88+
expect($user->provider_access_token)->toBeNull();
89+
expect($user->provider_refresh_token)->toBeNull();
8490
});
8591

8692
it('throws exception when attempting to create existing user with null provider', function () {

0 commit comments

Comments
 (0)