SocialiteController::handleProviderCallback()   B
last analyzed

Complexity

Conditions 7
Paths 7

Size

Total Lines 27
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 15
c 1
b 0
f 0
nc 7
nop 2
dl 0
loc 27
rs 8.8333
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Xetaravel\Http\Controllers\Auth;
6
7
use Exception;
8
use Illuminate\Auth\Events\Registered;
9
use Illuminate\Contracts\View\Factory;
10
use Illuminate\Contracts\View\View;
11
use Illuminate\Foundation\Application;
12
use Illuminate\Http\RedirectResponse;
13
use Illuminate\Http\Request;
14
use Illuminate\Foundation\Auth\RedirectsUsers;
15
use Illuminate\Support\Facades\Auth;
16
use Illuminate\Support\Str;
17
use Laravel\Socialite\Facades\Socialite;
18
use Laravel\Socialite\Two\User as ProviderUser;
19
use Masmerise\Toaster\Toaster;
20
use Spatie\MediaLibrary\MediaCollections\Exceptions\FileCannotBeAdded;
21
use Spatie\MediaLibrary\MediaCollections\Exceptions\FileDoesNotExist;
22
use Spatie\MediaLibrary\MediaCollections\Exceptions\FileIsTooBig;
23
use Symfony\Component\HttpFoundation\RedirectResponse as RedirectResponseSF;
24
use Xetaravel\Events\Badges\RegisterEvent;
25
use Xetaravel\Http\Controllers\Controller;
26
use Xetaravel\Http\Requests\Socialite\CreateRequest;
27
use Xetaravel\Models\User;
28
use Xetaravel\Models\Repositories\UserRepository;
29
use Xetaravel\Models\Role;
30
use Xetaravel\Models\Validators\UserValidator;
31
32
class SocialiteController extends Controller
33
{
34
    use RedirectsUsers;
35
36
    /**
37
     * Where to redirect users after login.
38
     *
39
     * @var string
40
     */
41
    protected string $redirectTo = '/';
42
43
    /**
44
     * The driver used.
45
     *
46
     * @var string
47
     */
48
    protected string $driver;
49
50
    /**
51
     * Login the user and trigger the authenticated function.
52
     *
53
     * @param Request $request The request object.
54
     * @param User $user The user to login.
55
     *
56
     * @return RedirectResponse
57
     */
58
    protected function login(Request $request, User $user): RedirectResponse
59
    {
60
        if (!$user->hasVerifiedEmail()) {
61
            return redirect(route('auth.verification.notice', sha1($user->getEmailForVerification())));
62
        }
63
64
        Auth::login($user, true);
65
66
        $this->authenticated($request, $user);
67
68
        return redirect()->intended($this->redirectPath());
0 ignored issues
show
Bug introduced by
The method intended() does not exist on Illuminate\Routing\Redirector. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

68
        return redirect()->/** @scrutinizer ignore-call */ intended($this->redirectPath());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
69
    }
70
71
    /**
72
     * Handle the registration.
73
     *
74
     * @param Request $request The request object.
75
     * @param ProviderUser $user The user to register.
76
     *
77
     * @return RedirectResponse|User
78
     *
79
     * @throws FileCannotBeAdded
80
     */
81
    protected function handleRegister(Request $request, ProviderUser $user)
82
    {
83
        $validator = UserValidator::createWithProvider([
84
            'username' => $user->nickname,
85
            'email' => $user->email
86
        ]);
87
88
        if ($validator->fails()) {
89
            $request->session()->put('socialite', [
90
                'driver' => $this->driver,
91
                'token' => $user->token
92
            ]);
93
94
            return redirect()
95
                ->route('auth.driver.register', ['driver' => $this->driver])
0 ignored issues
show
Bug introduced by
The method route() does not exist on Illuminate\Routing\Redirector. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

95
                ->/** @scrutinizer ignore-call */ route('auth.driver.register', ['driver' => $this->driver])

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
96
                ->withErrors($validator)
97
                ->withInput([
98
                    'username' => $user->nickname,
99
                    'email' => $user->email
100
                ]);
101
        }
102
103
        return $this->registered($user);
104
    }
105
106
    /**
107
     * Create the user.
108
     *
109
     * @param ProviderUser $user The user to create.
110
     *
111
     * @return User
112
     */
113
    protected function createUser(ProviderUser $user)
114
    {
115
        return UserRepository::create(
116
            [
117
                'username' => $user->nickname,
118
                'email' => $user->email
119
            ],
120
            [
121
                $this->driver . '_id' => $user->id
122
            ],
123
            true
124
        );
125
    }
126
127
    /**
128
     * The user has been authenticated.
129
     *
130
     * @param Request $request The request object.
131
     * @param User $user The user that has been logged in.
132
     *
133
     * @return void
134
     */
135
    protected function authenticated(Request $request, User $user)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

135
    protected function authenticated(/** @scrutinizer ignore-unused */ Request $request, User $user)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
136
    {
137
        event(new RegisterEvent($user));
138
139
        Toaster::success("Welcome back <strong>{$user->username}</strong>! You\'re successfully connected !");
140
    }
141
142
    /**
143
     * The user has been registered.
144
     *
145
     * @param ProviderUser $providerUser The user that has been registered.
146
     *
147
     * @return User
148
     * @throws FileCannotBeAdded
149
     */
150
    protected function registered(ProviderUser $providerUser): User
151
    {
152
        event(new Registered($user = $this->createUser($providerUser)));
153
154
        $this->assignDefaultRole($user);
155
        $this->setUserAvatar($user, $providerUser->avatar);
156
157
        return $user;
158
    }
159
160
    /**
161
     * Assign the default role to the new user.
162
     *
163
     * @param User $user
164
     *
165
     * @return void
166
     */
167
    protected function assignDefaultRole(User $user): void
168
    {
169
        $role = Role::firstWhere('name', 'User');
170
        $user->assignRole($role);
171
    }
172
173
    /**
174
     * Set the avatar for the new user.
175
     *
176
     * @param User $user
177
     * @param string|null $avatar
178
     * @return void
179
     *
180
     * @throws FileCannotBeAdded
181
     * @throws FileDoesNotExist
182
     * @throws FileIsTooBig
183
     */
184
    protected function setUserAvatar(User $user, ?string $avatar): void
185
    {
186
        $user->clearMediaCollection('avatar');
187
188
        $mediaAdder = $avatar
189
            ? $user->addMediaFromUrl($avatar)
190
            : $user->addMedia(public_path('images/avatar.png'))->preservingOriginal();
191
192
        $hashedName = mb_substr(md5($user->username), 0, 10);
193
        $mediaAdder
194
            ->setName($hashedName)
195
            ->setFileName("{$hashedName}.png")
196
            ->toMediaCollection('avatar');
197
    }
198
199
    /**
200
     * Show the registration form.
201
     *
202
     * @param Request $request The request object.
203
     * @param string $driver The driver used.
204
     *
205
     * @return Factory|View|Application|\Illuminate\View\View|object|RedirectResponse
206
     */
207
    public function showRegistrationForm(Request $request, string $driver)
208
    {
209
        if (is_null($request->session()->get('socialite.driver'))) {
210
            return redirect()
211
                ->route('auth.login')
212
                ->error('You are not authorized to view this page!');
213
        }
214
        return view('Auth.socialite', compact('driver'));
215
    }
216
217
    /**
218
     * Register an user that has been forced to modify his email or
219
     * username due to a conflit with the database.
220
     *
221
     * @param CreateRequest $request The request object.
222
     * @param string $driver The driver used.
223
     *
224
     * @return RedirectResponse
225
     *
226
     * @throws FileCannotBeAdded
227
     */
228
    public function register(CreateRequest $request, string $driver): RedirectResponse
229
    {
230
        $request->validated();
231
        $this->driver = $driver;
232
233
        $user = Socialite::driver($driver)->userFromToken($request->session()->get('socialite.token'));
0 ignored issues
show
Bug introduced by
The method userFromToken() does not exist on Laravel\Socialite\Contracts\Provider. It seems like you code against a sub-type of Laravel\Socialite\Contracts\Provider such as Laravel\Socialite\Two\AbstractProvider. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

233
        $user = Socialite::driver($driver)->/** @scrutinizer ignore-call */ userFromToken($request->session()->get('socialite.token'));
Loading history...
234
235
        $user->nickname = $request->input('username');
236
        $user->email = $request->input('email');
237
238
        $user = $this->registered($user);
239
240
        $request->session()->forget('socialite');
241
242
        return $this->login($request, $user);
243
    }
244
245
    /**
246
     * Redirect the user to the Provider authentication page.
247
     *
248
     * @param Request $request The request object.
249
     * @param string $driver The driver used.
250
     *
251
     * @return RedirectResponseSF
252
     */
253
    public function redirectToProvider(Request $request, string $driver): RedirectResponseSF
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

253
    public function redirectToProvider(/** @scrutinizer ignore-unused */ Request $request, string $driver): RedirectResponseSF

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
254
    {
255
        return Socialite::driver($driver)
256
                ->redirectUrl(route('auth.driver.callback', ['driver' => $driver]))
0 ignored issues
show
Bug introduced by
The method redirectUrl() does not exist on Laravel\Socialite\Contracts\Provider. Did you maybe mean redirect()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

256
                ->/** @scrutinizer ignore-call */ redirectUrl(route('auth.driver.callback', ['driver' => $driver]))

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
257
                ->redirect();
258
    }
259
260
    /**
261
     * Obtain the user information from the Provider and process to the
262
     * registration or login regarding the type of callback.
263
     *
264
     * @param Request $request The request object.
265
     * @param string $driver The driver used.
266
     *
267
     * @return RedirectResponse
268
     * @throws FileCannotBeAdded
269
     */
270
    public function handleProviderCallback(Request $request, string $driver): RedirectResponse
271
    {
272
        $this->driver = $driver;
273
274
        try {
275
            $user = Socialite::driver($driver)->user();
276
        } catch (Exception) {
277
            $driver = Str::title($driver);
278
279
            return redirect()
280
                ->route('auth.login')
281
                ->error("An error occurred while getting your information from {$driver} !");
282
        }
283
284
        // Check if the user is already registered
285
        if (!$member = User::where($driver . '_id', $user->id)->first()) {
0 ignored issues
show
Bug introduced by
Accessing id on the interface Laravel\Socialite\Contracts\User suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
286
            $register = $this->handleRegister($request, $user);
287
        }
288
289
        if (isset($register) && $register instanceof RedirectResponse) {
290
            return $register;
291
        }
292
        if (!isset($register) || !$register instanceof User) {
293
            $register = $member;
294
        }
295
296
        return $this->login($request, $register);
297
    }
298
}
299