Skip to content

Commit 08b11dc

Browse files
authored
[5.x] confirm account link for logged in users (#391)
1 parent da900a3 commit 08b11dc

File tree

8 files changed

+124
-92
lines changed

8 files changed

+124
-92
lines changed

config/socialstream.php

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
return [
77
'middleware' => ['web'],
88
'prompt' => 'Or Login Via',
9+
'confirmation-prompt' => null,
910
'providers' => [
1011
// Providers::github(),
1112
],
+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<!DOCTYPE html>
2+
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1">
6+
7+
<title>Laravel</title>
8+
9+
<!-- Fonts -->
10+
<link rel="preconnect" href="https://fonts.bunny.net">
11+
<link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />
12+
13+
<!-- Styles / Scripts -->
14+
<script src="https://cdn.tailwindcss.com"></script>
15+
</head>
16+
<body class="font-sans antialiased">
17+
<div class=" min-h-screen bg-gray-100 flex justify-center items-center dark:bg-gray-900">
18+
<div class="w-full sm:max-w-sm bg-white dark:bg-gray-800 overflow-hidden shadow-xl sm:rounded-lg px-6 py-4">
19+
<h2 class="mb-4 font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
20+
Confirm connection of your {{ \JoelButcher\Socialstream\Providers::name($provider) }} account.
21+
</h2>
22+
23+
<form method="POST" action="{{ route('oauth.callback.confirm', $provider) }}">
24+
@csrf
25+
<p class="text-sm text-gray-600 dark:text-gray-400">
26+
@if (config('socialstream.confirmation-prompt'))
27+
{{ config('socialstream.confirmation-prompt') }}
28+
@else
29+
To ensure you are the account owner of this {{ \JoelButcher\Socialstream\Providers::name($provider) }} account,
30+
please confirm or deny the request below to link this provider to your account.
31+
@endif
32+
</p>
33+
34+
<div class="mt-4 flex items-center justify-between">
35+
<button type="submit" name="result" value="deny" class="inline-flex items-center justify-center px-4 py-2 bg-red-600 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-red-500 active:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 transition ease-in-out duration-150">
36+
Deny
37+
</button>
38+
39+
<button type="submit" name="result" value="confirm" class="inline-flex items-center px-4 py-2 bg-gray-800 dark:bg-gray-200 border border-transparent rounded-md font-semibold text-xs text-white dark:text-gray-800 uppercase tracking-widest hover:bg-gray-700 dark:hover:bg-white focus:bg-gray-700 dark:focus:bg-white active:bg-gray-900 dark:active:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 disabled:opacity-50 transition ease-in-out duration-150">
40+
Confirm
41+
</button>
42+
</div>
43+
</form>
44+
</div>
45+
</div>
46+
</body>
47+
</html>

src/Actions/AuthenticateOAuthCallback.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ protected function login(Authenticatable $user, mixed $account, string $provider
136136
/**
137137
* Attempt to link the provider to the authenticated user.
138138
*/
139-
private function linkProvider(Authenticatable $user, string $provider, ProviderUser $providerAccount): SocialstreamResponse
139+
public function linkProvider(Authenticatable $user, string $provider, ProviderUser $providerAccount): SocialstreamResponse
140140
{
141141
$account = $this->findAccount($provider, $providerAccount);
142142

src/Filament/web.php

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
use Illuminate\Support\Facades\Route;
4+
use JoelButcher\Socialstream\Http\Controllers\OAuthController;
5+
6+
7+
Route::group(['middleware' => config('socialstream.middleware', ['web'])], function () {
8+
Route::get('/oauth/{provider}/callback/prompt', [OAuthController::class, 'prompt'])->name('oauth.callback.prompt');
9+
Route::post('/oauth/{provider}/callback/confirm', [OAuthController::class, 'confirm'])->name(
10+
'oauth.callback.confirm'
11+
);
12+
});

src/HasProfilePhoto.php

-89
This file was deleted.

src/Http/Controllers/OAuthController.php

+61
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,24 @@
22

33
namespace JoelButcher\Socialstream\Http\Controllers;
44

5+
use Illuminate\Contracts\View\View;
56
use Illuminate\Http\RedirectResponse;
67
use Illuminate\Http\Request;
78
use Illuminate\Http\Response;
89
use Illuminate\Routing\Controller;
10+
use Illuminate\Support\Facades\Session;
11+
use Illuminate\Support\MessageBag;
12+
use Illuminate\Support\ViewErrorBag;
913
use JoelButcher\Socialstream\Contracts\AuthenticatesOAuthCallback;
1014
use JoelButcher\Socialstream\Contracts\GeneratesProviderRedirect;
1115
use JoelButcher\Socialstream\Contracts\HandlesInvalidState;
1216
use JoelButcher\Socialstream\Contracts\HandlesOAuthCallbackErrors;
17+
use JoelButcher\Socialstream\Contracts\OAuthProviderLinkFailedResponse;
1318
use JoelButcher\Socialstream\Contracts\ResolvesSocialiteUsers;
1419
use JoelButcher\Socialstream\Contracts\SocialstreamResponse;
20+
use JoelButcher\Socialstream\Events\OAuthProviderLinkFailed;
21+
use JoelButcher\Socialstream\Providers;
22+
use Laravel\Jetstream\Jetstream;
1523
use Laravel\Socialite\Two\InvalidStateException;
1624
use Symfony\Component\HttpFoundation\RedirectResponse as SymfonyRedirectResponse;
1725

@@ -58,4 +66,57 @@ public function callback(Request $request, string $provider): SocialstreamRespon
5866

5967
return $this->authenticator->authenticate($provider, $providerAccount);
6068
}
69+
70+
/**
71+
* Show the oauth confirmation page.
72+
*/
73+
public function prompt(string $provider): View
74+
{
75+
return view('socialstream::oauth.prompt', [
76+
'provider' => $provider,
77+
]);
78+
}
79+
80+
public function confirm(string $provider): SocialstreamResponse|RedirectResponse
81+
{
82+
$user = auth()->user();
83+
$providerAccount = cache()->pull("socialstream.{$user->id}:$provider.provider");
84+
85+
$result = request()->input('result');
86+
87+
if ($result === 'deny') {
88+
event(new OAuthProviderLinkFailed($user, $provider, null, $providerAccount));
89+
90+
$this->flashError(
91+
__('Failed to link :provider account. User denied the request.', ['provider' => Providers::name($provider)]),
92+
);
93+
94+
return app(OAuthProviderLinkFailedResponse::class);
95+
}
96+
97+
if (!$providerAccount) {
98+
throw new \DomainException(
99+
message: 'Could not retrieve social provider information.'
100+
);
101+
}
102+
103+
return $this->authenticator->linkProvider($user, $provider, $providerAccount);
104+
}
105+
106+
private function flashError(string $error): void
107+
{
108+
if (auth()->check()) {
109+
if (class_exists(Jetstream::class)) {
110+
Session::flash('flash.banner', $error);
111+
Session::flash('flash.bannerStyle', 'danger');
112+
113+
return;
114+
}
115+
}
116+
117+
Session::flash('errors', (new ViewErrorBag())->put(
118+
'default',
119+
new MessageBag(['socialstream' => $error])
120+
));
121+
}
61122
}

stubs/jetstream/app/Models/User.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ class User extends Authenticatable
6868
/**
6969
* Get the URL to the user's profile photo.
7070
*/
71-
public function profilePhotoUrl(): Attribute
71+
protected function profilePhotoUrl(): Attribute
7272
{
7373
return filter_var($this->profile_photo_path, FILTER_VALIDATE_URL)
7474
? Attribute::get(fn () => $this->profile_photo_path)

stubs/jetstream/app/Models/UserWithTeams.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ class User extends Authenticatable
7070
/**
7171
* Get the URL to the user's profile photo.
7272
*/
73-
public function profilePhotoUrl(): Attribute
73+
protected function profilePhotoUrl(): Attribute
7474
{
7575
return filter_var($this->profile_photo_path, FILTER_VALIDATE_URL)
7676
? Attribute::get(fn () => $this->profile_photo_path)

0 commit comments

Comments
 (0)