Completed
Push — master ( fdb3a6...16cc60 )
by PROSPER
02:36
created

OauthController::findByProviderIdOrCreate()   B

Complexity

Conditions 4
Paths 8

Size

Total Lines 23
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 23
rs 8.7972
cc 4
eloc 14
nc 8
nop 2
1
<?php
2
3
namespace App\Http\Controllers;
4
5
use Illuminate\Http\Request;
6
7
use App\Http\Requests;
8
use App\Http\Controllers\Controller;
9
use Illuminate\Contracts\Auth\Guard;
10
use Laravel\Socialite\Contracts\Factory as Socialite;
11
use App\User;
12
13
class OauthController extends Controller
14
{
15
    protected $socialite;
16
    protected $auth;
17
18
    public function __construct(Socialite $socialite, Guard $auth)
19
    {
20
        $this->socialite = $socialite;
21
        $this->auth = $auth;
22
    }
23
24
    public function authenticate(Request $request, $provider)
25
    {
26
        return $this->execute(($request->has('code') || $request->has('oauth_token')), $provider);
27
    }
28
29
    public function execute($request, $provider)
30
    {
31
        if (! $request) {
32
            return $this->getAuthorizationFirst($provider);
33
        }
34
35
        $user = $this->findByProviderIdOrCreate($this->getSocialUser($provider), $provider);
36
        $this->auth->loginUsingId($user->id);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Illuminate\Contracts\Auth\Guard as the method loginUsingId() does only exist in the following implementations of said interface: Illuminate\Auth\SessionGuard.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
37
38
        return redirect('/api');
39
    }
40
41
    /**
42
     * Find a user by username or create a new user
43
     * @param
44
     * @return
45
     */
46
    public function findByProviderIdOrCreate($userData, $provider)
47
    {
48
        $user = User::where('provider_id', '=', $userData->id)->first();
49
50
        $email = $this->isEmailExists($userData->getEmail()) ? null : $userData->getEmail();
51
52
        $username = $this->isEmailExists($userData->getNickName()) ? null : $userData->getEmail();
53
54
        if (empty($user))  {
55
            $user = User::create([
56
                'fullname'      => $userData->getName(),
57
                'username'      => $username,
58
                'provider_id'   => $userData->getId(),
59
                'avatar'        => $userData->getAvatar(),
60
                'email'         => $email,
61
                'provider'      => $provider,
62
            ]);
63
        }
64
65
        $this->checkIfUserNeedsUpdating($userData, $user);
66
67
        return $user;
68
    }
69
70
    private function isUsernameExists($username = null)
71
    {
72
        $username = User::whereUsername($username)->first()['username'];
73
74
        return (! is_null($username)) ? true : false;
75
    }
76
77
    private function isEmailExists($email = null)
78
    {
79
        $email = User::whereEmail($email)->first()['email'];
80
81
        return (! is_null($email)) ? true : false;
82
    }
83
84
    /**
85
     * Check if the user's info needs updating
86
     * @param
87
     * @return
88
     */
89
    public function checkIfUserNeedsUpdating($userData, $user)
90
    {
91
        $socialData = [
92
            'avatar' => $userData->getAvatar(),
93
            'fullname' => $userData->getName(),
94
            'username' => $userData->getNickName(),
95
        ];
96
97
        $dbData = [
98
            'avatar' => $user->avatar,
99
            'fullname' => $user->fullname,
100
            'username' => $user->username,
101
        ];
102
103
        if (! empty(array_diff($dbData, $socialData))) {
104
            $user->avatar = $userData->getAvatar();
105
            $user->fullname = $userData->getName();
106
            $user->username = $userData->getNickName();
107
            $user->save();
108
        }
109
    }
110
111
    /**
112
     * Redirect the user to the Social Media Account authentication page
113
     * @param $provider
114
     * @return \Symfony\Component\HttpFoundation\RedirectResponse
115
     */
116
    private function getAuthorizationFirst($provider)
117
    {
118
        return $this->socialite->driver($provider)->redirect();
119
    }
120
121
    /**
122
     * Get Data from Social Media Account
123
     * @param  string $provider
124
     * @return collection
125
     */
126
    private function getSocialUser($provider)
127
    {
128
        return $this->socialite->driver($provider)->user();
129
    }
130
131
}
132
133