diff --git a/composer.json b/composer.json index f1d6a94a..14083dbb 100644 --- a/composer.json +++ b/composer.json @@ -30,11 +30,12 @@ "require": { "php": "^8.1", "laravel/jetstream": "^3.0|^4.0", - "laravel/socialite": "^5.6" + "laravel/socialite": "^5.8.1" }, "require-dev": { "inertiajs/inertia-laravel": "^0.6.4", "laravel/sanctum": "^3.2", + "livewire/livewire": "^3.0", "mockery/mockery": "^1.0", "orchestra/testbench": "^8.0", "pestphp/pest": "^2.0", diff --git a/src/Actions/Auth/AuthenticateOauthCallback.php b/src/Actions/Auth/AuthenticateOauthCallback.php index e9920aed..c9b776b7 100644 --- a/src/Actions/Auth/AuthenticateOauthCallback.php +++ b/src/Actions/Auth/AuthenticateOauthCallback.php @@ -133,7 +133,6 @@ protected function alreadyAuthenticated(Authenticatable $user, ?ConnectedAccount protected function alreadyRegistered(Authenticatable $user, ?ConnectedAccount $account, string $provider, ProviderUser $providerAccount): RedirectResponse|LoginResponse { if (Features::hasLoginOnRegistrationFeatures()) { - // The user exists, but they're not registered with the given provider. if (! $account) { $this->createsConnectedAccounts->create($user, $provider, $providerAccount); diff --git a/src/ConnectedAccount.php b/src/ConnectedAccount.php index d332adff..4a05bc17 100644 --- a/src/ConnectedAccount.php +++ b/src/ConnectedAccount.php @@ -29,7 +29,7 @@ public function getCredentials(): Credentials */ public function user(): BelongsTo { - return $this->belongsTo(Jetstream::userModel(), 'user_id', (Jetstream::newUserModel())->getAuthIdentifierName()); + return $this->belongsTo(Jetstream::userModel(), 'user_id', Jetstream::newUserModel()->getAuthIdentifierName()); } /** diff --git a/src/Contracts/CreatesConnectedAccounts.php b/src/Contracts/CreatesConnectedAccounts.php index f2b647e1..88da7e24 100644 --- a/src/Contracts/CreatesConnectedAccounts.php +++ b/src/Contracts/CreatesConnectedAccounts.php @@ -3,7 +3,6 @@ namespace JoelButcher\Socialstream\Contracts; use Illuminate\Contracts\Auth\Authenticatable; -use JoelButcher\Socialstream\ConnectedAccount; use Laravel\Socialite\Contracts\User as ProviderUser; interface CreatesConnectedAccounts diff --git a/src/Contracts/Credentials.php b/src/Contracts/Credentials.php index 4136275c..9369a0e1 100644 --- a/src/Contracts/Credentials.php +++ b/src/Contracts/Credentials.php @@ -2,8 +2,6 @@ namespace JoelButcher\Socialstream\Contracts; -use DateTimeInterface; - interface Credentials extends RefreshedCredentials { /** diff --git a/src/HasConnectedAccounts.php b/src/HasConnectedAccounts.php index 5a6e5639..46d02082 100644 --- a/src/HasConnectedAccounts.php +++ b/src/HasConnectedAccounts.php @@ -39,7 +39,7 @@ public function currentConnectedAccount() */ public function switchConnectedAccount(mixed $connectedAccount): bool { - if (!$this->ownsConnectedAccount($connectedAccount)) { + if (! $this->ownsConnectedAccount($connectedAccount)) { return false; } diff --git a/src/Http/Livewire/ConnectedAccountsForm.php b/src/Http/Livewire/ConnectedAccountsForm.php index 63fb7077..c2459190 100644 --- a/src/Http/Livewire/ConnectedAccountsForm.php +++ b/src/Http/Livewire/ConnectedAccountsForm.php @@ -2,7 +2,7 @@ namespace JoelButcher\Socialstream\Http\Livewire; -use Illuminate\Http\RedirectResponse; +use Illuminate\Routing\Redirector; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; @@ -11,7 +11,6 @@ use JoelButcher\Socialstream\Socialstream; use Laravel\Jetstream\InteractsWithBanner; use Livewire\Component; -use Livewire\Redirector; class ConnectedAccountsForm extends Component { diff --git a/src/Providers.php b/src/Providers.php index bbaf2c64..704afc98 100644 --- a/src/Providers.php +++ b/src/Providers.php @@ -63,6 +63,14 @@ public static function hasLinkedInSupport(): bool return static::enabled(static::linkedin()); } + /** + * Determine if the application has support for the Slack provider. + */ + public static function hasSlackSupport(): bool + { + return static::enabled(static::slack()); + } + /** * Determine if the application has support for the Twitter provider. */ @@ -91,7 +99,7 @@ public static function hasTwitterOAuth2Support(): bool /** * Enable the Bitbucket provider. */ - public static function bitbucket():string + public static function bitbucket(): string { return 'bitbucket'; } @@ -99,7 +107,7 @@ public static function bitbucket():string /** * Enable the Facebook provider. */ - public static function facebook():string + public static function facebook(): string { return 'facebook'; } @@ -107,7 +115,7 @@ public static function facebook():string /** * Enable the GitHub provider. */ - public static function github():string + public static function github(): string { return 'github'; } @@ -115,7 +123,7 @@ public static function github():string /** * Enable the GitLab provider. */ - public static function gitlab():string + public static function gitlab(): string { return 'gitlab'; } @@ -123,7 +131,7 @@ public static function gitlab():string /** * Enable the Google provider. */ - public static function google():string + public static function google(): string { return 'google'; } @@ -131,15 +139,23 @@ public static function google():string /** * Enable the LinkedIn provider. */ - public static function linkedin():string + public static function linkedin(): string { return 'linkedin'; } + /** + * Enable the Slack provider. + */ + public static function slack(): string + { + return 'slack'; + } + /** * Enable the Twitter provider. */ - public static function twitter():string + public static function twitter(): string { return 'twitter'; } @@ -147,7 +163,7 @@ public static function twitter():string /** * Enable the Twitter OAuth 1.0 provider. */ - public static function twitterOAuth1():string + public static function twitterOAuth1(): string { return 'twitter'; } @@ -155,7 +171,7 @@ public static function twitterOAuth1():string /** * Enable the Twitter OAuth 2.0 provider. */ - public static function twitterOAuth2():string + public static function twitterOAuth2(): string { return 'twitter-oauth-2'; } @@ -163,8 +179,8 @@ public static function twitterOAuth2():string /** * Dynamically handle static calls. * - * @param $name - * @param $arguments + * @param $name + * @param $arguments * @return mixed */ public static function __callStatic($name, $arguments) diff --git a/src/Resolvers/OAuth/SlackOauth2RefreshResolver.php b/src/Resolvers/OAuth/SlackOauth2RefreshResolver.php new file mode 100644 index 00000000..d9cafbb9 --- /dev/null +++ b/src/Resolvers/OAuth/SlackOauth2RefreshResolver.php @@ -0,0 +1,25 @@ +
+ - - - - + + + +
diff --git a/stubs/inertia/resources/js/Components/Socialstream.vue b/stubs/inertia/resources/js/Components/Socialstream.vue index 8763da67..4d587849 100644 --- a/stubs/inertia/resources/js/Components/Socialstream.vue +++ b/stubs/inertia/resources/js/Components/Socialstream.vue @@ -8,6 +8,7 @@ import GitLabIcon from '@/Components/SocialstreamIcons/GitLabIcon.vue'; import GoogleIcon from '@/Components/SocialstreamIcons/GoogleIcon.vue'; import InputError from '@/Components/InputError.vue'; import LinkedInIcon from '@/Components/SocialstreamIcons/LinkedInIcon.vue'; +import SlackIcon from '@/Components/SocialstreamIcons/SlackIcon.vue'; import TwitterIcon from '@/Components/SocialstreamIcons/TwitterIcon.vue'; const error = computed(() => usePage().props.errors.socialstream); @@ -22,27 +23,29 @@ const error = computed(() => usePage().props.errors.socialstream);
diff --git a/stubs/inertia/resources/js/Components/SocialstreamIcons/SlackIcon.vue b/stubs/inertia/resources/js/Components/SocialstreamIcons/SlackIcon.vue new file mode 100644 index 00000000..7ada2d20 --- /dev/null +++ b/stubs/inertia/resources/js/Components/SocialstreamIcons/SlackIcon.vue @@ -0,0 +1,26 @@ + + + diff --git a/stubs/livewire/resources/views/components/socialstream-icons/slack.blade.php b/stubs/livewire/resources/views/components/socialstream-icons/slack.blade.php new file mode 100644 index 00000000..7221e470 --- /dev/null +++ b/stubs/livewire/resources/views/components/socialstream-icons/slack.blade.php @@ -0,0 +1,20 @@ + diff --git a/stubs/livewire/resources/views/components/socialstream.blade.php b/stubs/livewire/resources/views/components/socialstream.blade.php index fb6aa171..e3e4bcdc 100644 --- a/stubs/livewire/resources/views/components/socialstream.blade.php +++ b/stubs/livewire/resources/views/components/socialstream.blade.php @@ -5,6 +5,13 @@
+ @if (JoelButcher\Socialstream\Socialstream::hasBitbucketSupport()) + + + BitBucket + + @endif + @if (JoelButcher\Socialstream\Socialstream::hasFacebookSupport()) @@ -12,24 +19,24 @@ @endif - @if (JoelButcher\Socialstream\Socialstream::hasGoogleSupport()) - - - Google + @if (JoelButcher\Socialstream\Socialstream::hasGithubSupport()) + + + GitHub @endif - @if (JoelButcher\Socialstream\Socialstream::hasTwitterOAuth1Support()) - - - Twitter + @if (JoelButcher\Socialstream\Socialstream::hasGitlabSupport()) + + + GitLab @endif - @if (JoelButcher\Socialstream\Socialstream::hasTwitterOAuth2Support()) - - - Twitter + @if (JoelButcher\Socialstream\Socialstream::hasGoogleSupport()) + + + Google @endif @@ -40,24 +47,24 @@ @endif - @if (JoelButcher\Socialstream\Socialstream::hasGithubSupport()) - - - GitHub + @if (JoelButcher\Socialstream\Socialstream::hasSlackSupport()) + + + Slack @endif - @if (JoelButcher\Socialstream\Socialstream::hasGitlabSupport()) - - - GitLab + @if (JoelButcher\Socialstream\Socialstream::hasTwitterOAuth1Support()) + + + Twitter @endif - @if (JoelButcher\Socialstream\Socialstream::hasBitbucketSupport()) - - - BitBucket + @if (JoelButcher\Socialstream\Socialstream::hasTwitterOAuth2Support()) + + + Twitter @endif
diff --git a/tests/Feature/SocialstreamTest.php b/tests/Feature/SocialstreamTest.php index 622aac1f..45a271bb 100644 --- a/tests/Feature/SocialstreamTest.php +++ b/tests/Feature/SocialstreamTest.php @@ -46,8 +46,7 @@ Socialstream::generatesProvidersRedirectsUsing(GenerateRedirectForProvider::class); }); -it('redirects users', function (): void -{ +it('redirects users', function (): void { $response = $this->get(route('oauth.redirect', 'github')); $response->assertRedirect() @@ -58,14 +57,15 @@ Config::set('services.github.manage_repos', $manageRepos); Socialstream::generatesProvidersRedirectsUsing( - callback: fn () => new class() implements GeneratesProviderRedirect { + callback: fn () => new class() implements GeneratesProviderRedirect + { public function generate(string $provider): RedirectResponse { ['provider' => $provider] = Route::current()->parameters(); $scopes = ['*']; - $scopes = match($provider) { + $scopes = match ($provider) { 'github' => array_merge($scopes, [ 'repos.manage', ]), @@ -95,8 +95,7 @@ public function generate(string $provider): RedirectResponse 'do not manage repos' => [false], ]); -test('users can register', function (): void -{ +test('users can register', function (): void { $user = (new SocialiteUser()) ->map([ 'id' => $githubId = $this->faker->numerify('########'), @@ -129,8 +128,7 @@ public function generate(string $provider): RedirectResponse ]); }); -test('existing users can login', function (): void -{ +test('existing users can login', function (): void { $user = User::create([ 'name' => 'Joel Butcher', 'email' => 'joel@socialstream.dev', @@ -175,8 +173,7 @@ public function generate(string $provider): RedirectResponse $this->assertAuthenticated(); }); -test('authenticated users can link to provider', function (): void -{ +test('authenticated users can link to provider', function (): void { $this->actingAs(User::create([ 'name' => 'Joel Butcher', 'email' => 'joel@socialstream.dev', @@ -215,8 +212,7 @@ public function generate(string $provider): RedirectResponse ]); }); -test('new users can register from login page', function (): void -{ +test('new users can register from login page', function (): void { Config::set('socialstream.features', [ Features::createAccountOnFirstLogin(), ]); @@ -253,8 +249,7 @@ public function generate(string $provider): RedirectResponse ]); }); -test('users can login on registration', function (): void -{ +test('users can login on registration', function (): void { Config::set('socialstream.features', [ Features::loginOnRegistration(), ]); @@ -299,8 +294,7 @@ public function generate(string $provider): RedirectResponse ]); }); -it('generates missing emails', function (): void -{ +it('generates missing emails', function (): void { Config::set('socialstream.features', [ Features::generateMissingEmails(), ]); diff --git a/tests/Pest.php b/tests/Pest.php index 609d5f12..91bdc53a 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -1,6 +1,5 @@ in('Feature', 'Unit'); diff --git a/tests/Unit/CredentialsTest.php b/tests/Unit/CredentialsTest.php index 4f6dd668..10044de6 100644 --- a/tests/Unit/CredentialsTest.php +++ b/tests/Unit/CredentialsTest.php @@ -7,12 +7,10 @@ use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Support\Carbon; use JoelButcher\Socialstream\Credentials; -use JoelButcher\Socialstream\Tests\TestCase; uses(RefreshDatabase::class); -it('can be created from a connected account instance', function (): void -{ +it('can be created from a connected account instance', function (): void { Carbon::setTestNow(DateTime::createFromFormat('Y-m-d H:i:s', '2023-04-13 00:00:00')); $credentials = new Credentials( @@ -36,8 +34,7 @@ $this->assertEquals('2023-04-13 01:00:00', $credentials->getExpiry()); }); -it('can be cast to an array', function (): void -{ +it('can be cast to an array', function (): void { Carbon::setTestNow(DateTime::createFromFormat('Y-m-d H:i:s', '2023-04-13 00:00:00')); $credentials = new Credentials( @@ -63,8 +60,7 @@ ], $credentials->toArray()); }); -it('can be json encoded', function (): void -{ +it('can be json encoded', function (): void { Carbon::setTestNow(DateTime::createFromFormat('Y-m-d H:i:s', '2023-04-13 00:00:00')); $credentials = new Credentials( diff --git a/tests/Unit/ProvidersTest.php b/tests/Unit/ProvidersTest.php index c1d5c80b..4ba8bca5 100644 --- a/tests/Unit/ProvidersTest.php +++ b/tests/Unit/ProvidersTest.php @@ -4,80 +4,68 @@ use Illuminate\Support\Facades\Config; use JoelButcher\Socialstream\Providers; -use JoelButcher\Socialstream\Tests\TestCase; -it('supports the \'bitbucket\' provider', function (): void -{ +it('supports the \'bitbucket\' provider', function (): void { Config::set('socialstream.providers', [Providers::bitbucket()]); $this->assertTrue(Providers::hasBitbucketSupport()); }); -it('supports the \'facebook\' provider', function (): void -{ +it('supports the \'facebook\' provider', function (): void { Config::set('socialstream.providers', [Providers::facebook()]); $this->assertTrue(Providers::hasFacebookSupport()); }); -it('supports the \'github\' provider', function (): void -{ +it('supports the \'github\' provider', function (): void { Config::set('socialstream.providers', [Providers::github()]); $this->assertTrue(Providers::hasGithubSupport()); }); -it('supports the \'gitlab\' provider', function (): void -{ +it('supports the \'gitlab\' provider', function (): void { Config::set('socialstream.providers', [Providers::gitlab()]); $this->assertTrue(Providers::hasGitlabSupport()); }); -it('supports the \'google\' provider', function (): void -{ +it('supports the \'google\' provider', function (): void { Config::set('socialstream.providers', [Providers::google()]); $this->assertTrue(Providers::hasGoogleSupport()); }); -it('supports the \'linked\' in_provider', function (): void -{ +it('supports the \'linked\' in_provider', function (): void { Config::set('socialstream.providers', [Providers::linkedin()]); $this->assertTrue(Providers::hasLinkedInSupport()); }); -it('supports the \'twitter\' provider', function (): void -{ +it('supports the \'twitter\' provider', function (): void { Config::set('socialstream.providers', [Providers::twitter()]); $this->assertTrue(Providers::hasTwitterSupport()); }); -it('supports the \'twitter\' OAuth 1.0 provider', function (): void -{ +it('supports the \'twitter\' OAuth 1.0 provider', function (): void { Config::set('socialstream.providers', [Providers::twitterOAuth1()]); $this->assertTrue(Providers::hasTwitterOAuth1Support()); }); -it('supports the \'twitter\' OAuth 2.0 provider', function (): void -{ +it('supports the \'twitter\' OAuth 2.0 provider', function (): void { Config::set('socialstream.providers', [Providers::twitterOAuth2()]); $this->assertTrue(Providers::hasTwitterOAuth2Support()); }); -it('supports custom providers', function (): void -{ +it('supports custom providers', function (): void { Config::set('socialstream.providers', ['my-custom-provider']); $this->assertTrue(Providers::enabled('my-custom-provider')); }); -it('supports dynamic calls for custom providers', function (): void -{ +it('supports dynamic calls for custom providers', function (): void { Config::set('socialstream.providers', ['a-custom-provider', 'another-provider', 'and-another']); $this->assertTrue(Providers::hasACustomProviderSupport()); diff --git a/tests/Unit/RefreshedCredentialsTest.php b/tests/Unit/RefreshedCredentialsTest.php index e3f0c15a..6b459fad 100644 --- a/tests/Unit/RefreshedCredentialsTest.php +++ b/tests/Unit/RefreshedCredentialsTest.php @@ -6,10 +6,8 @@ use DateTime; use Illuminate\Support\Carbon; use JoelButcher\Socialstream\RefreshedCredentials; -use JoelButcher\Socialstream\Tests\TestCase; -test('the \'$token\' property cannot be empty', function (): void -{ +test('the \'$token\' property cannot be empty', function (): void { $credentials = new RefreshedCredentials('token'); $this->assertEquals('token', $credentials->getToken()); @@ -17,8 +15,7 @@ new RefreshedCredentials(); }); -test('the \'$tokenSecret\' property can be nullable', function (): void -{ +test('the \'$tokenSecret\' property can be nullable', function (): void { $credentials = new RefreshedCredentials( 'token', 'token-secret', @@ -33,8 +30,7 @@ $this->assertNull($credentials->getTokenSecret()); }); -test('the \'$refreshToken\' property can be nullable', function (): void -{ +test('the \'$refreshToken\' property can be nullable', function (): void { $credentials = new RefreshedCredentials( 'token', 'token-secret', @@ -50,8 +46,7 @@ $this->assertNull($credentials->getRefreshToken()); }); -test('the \'$expiry\' property can be nullable', function (): void -{ +test('the \'$expiry\' property can be nullable', function (): void { $credentials = new RefreshedCredentials( 'some-token', 'some-token-secret', @@ -71,8 +66,7 @@ $this->assertNull($credentials->getExpiry()); }); -it('can be cast to an array', function (): void -{ +it('can be cast to an array', function (): void { Carbon::setTestNow(DateTime::createFromFormat('Y-m-d H:i:s', '2023-04-13 00:00:00')); $credentials = new RefreshedCredentials( @@ -90,8 +84,7 @@ ], $credentials->toArray()); }); -it('can be json encoded', function (): void -{ +it('can be json encoded', function (): void { Carbon::setTestNow(DateTime::createFromFormat('Y-m-d H:i:s', '2023-04-13 00:00:00')); $credentials = new RefreshedCredentials( diff --git a/tests/Unit/RefreshesOauthTokensTest.php b/tests/Unit/RefreshesOauthTokensTest.php index 52d170d9..88140b60 100644 --- a/tests/Unit/RefreshesOauthTokensTest.php +++ b/tests/Unit/RefreshesOauthTokensTest.php @@ -9,11 +9,9 @@ use Illuminate\Support\Str; use JoelButcher\Socialstream\RefreshedCredentials; use JoelButcher\Socialstream\Socialstream; -use JoelButcher\Socialstream\Tests\TestCase; use Laravel\Socialite\Two\User as OAuth2User; -it('can refresh expired tokens', function (): void -{ +it('can refresh expired tokens', function (): void { $this->migrate(); Socialstream::refreshesTokensForProviderUsing('github', function () { @@ -44,8 +42,7 @@ $this->assertEquals(null, $connectedAccount->secret); }); -it('does not refresh active tokens', function (): void -{ +it('does not refresh active tokens', function (): void { $this->migrate(); Socialstream::refreshesTokensForProviderUsing('github', function () { @@ -78,8 +75,7 @@ $this->assertEquals(null, $connectedAccount->secret); }); -it('does not refresh tokens if the feature is disabled', function (): void -{ +it('does not refresh tokens if the feature is disabled', function (): void { $this->migrate(); Config::set('socialstream.features', []); diff --git a/tests/Unit/ResolveSocialiteUsersTest.php b/tests/Unit/ResolveSocialiteUsersTest.php index fddc8f8c..36c58b1b 100644 --- a/tests/Unit/ResolveSocialiteUsersTest.php +++ b/tests/Unit/ResolveSocialiteUsersTest.php @@ -5,7 +5,6 @@ use JoelButcher\Socialstream\Contracts\ResolvesSocialiteUsers; use JoelButcher\Socialstream\Socialstream; use JoelButcher\Socialstream\Tests\Fixtures\ResolveUser; -use JoelButcher\Socialstream\Tests\TestCase; beforeEach(fn () => Socialstream::resolvesSocialiteUsersUsing(ResolveUser::class)); diff --git a/tests/Unit/SetPasswordTest.php b/tests/Unit/SetPasswordTest.php index 9a32a02b..49bf15ef 100644 --- a/tests/Unit/SetPasswordTest.php +++ b/tests/Unit/SetPasswordTest.php @@ -5,7 +5,6 @@ use App\Actions\Socialstream\SetUserPassword; use Illuminate\Support\Facades\Hash; use JoelButcher\Socialstream\Tests\Fixtures\User; -use JoelButcher\Socialstream\Tests\TestCase; test('users password can be set', function (): void { $this->migrate();