]> BookStack Code Mirror - bookstack/blob - app/Services/SocialAuthService.php
Started social registration
[bookstack] / app / Services / SocialAuthService.php
1 <?php namespace Oxbow\Services;
2
3 use Laravel\Socialite\Contracts\Factory as Socialite;
4 use Oxbow\Exceptions\SocialDriverNotConfigured;
5 use Oxbow\Exceptions\SocialSignInException;
6 use Oxbow\Repos\UserRepo;
7 use Oxbow\SocialAccount;
8 use Oxbow\User;
9
10 class SocialAuthService
11 {
12
13     protected $userRepo;
14     protected $socialite;
15     protected $socialAccount;
16
17     protected $validSocialDrivers = ['google', 'github'];
18
19     /**
20      * SocialAuthService constructor.
21      * @param UserRepo      $userRepo
22      * @param Socialite     $socialite
23      * @param SocialAccount $socialAccount
24      */
25     public function __construct(UserRepo $userRepo, Socialite $socialite, SocialAccount $socialAccount)
26     {
27         $this->userRepo = $userRepo;
28         $this->socialite = $socialite;
29         $this->socialAccount = $socialAccount;
30     }
31
32     /**
33      * Start the social login path.
34      * @param $socialDriver
35      * @return \Symfony\Component\HttpFoundation\RedirectResponse
36      * @throws SocialDriverNotConfigured
37      */
38     public function startLogIn($socialDriver)
39     {
40         $driver = $this->validateDriver($socialDriver);
41         return $this->socialite->driver($driver)->redirect();
42     }
43
44     /**
45      * Get a user from socialite after a oAuth callback.
46      *
47      * @param $socialDriver
48      * @return User
49      * @throws SocialDriverNotConfigured
50      * @throws SocialSignInException
51      */
52     public function handleCallback($socialDriver)
53     {
54         $driver = $this->validateDriver($socialDriver);
55
56         // Get user details from social driver
57         $socialUser = $this->socialite->driver($driver)->user();
58         $socialId = $socialUser->getId();
59
60         // Get any attached social accounts or users
61         $socialAccount = $this->socialAccount->where('driver_id', '=', $socialId)->first();
62         $user = $this->userRepo->getByEmail($socialUser->getEmail());
63         $isLoggedIn = auth()->check();
64         $currentUser = auth()->user();
65
66         // When a user is not logged in and a matching SocialAccount exists,
67         // Simply log the user into the application.
68         if (!$isLoggedIn && $socialAccount !== null) {
69             return $this->logUserIn($socialAccount->user);
70         }
71
72         // When a user is logged in but the social account does not exist,
73         // Create the social account and attach it to the user & redirect to the profile page.
74         if ($isLoggedIn && $socialAccount === null) {
75             $this->fillSocialAccount($socialDriver, $socialUser);
76             $currentUser->socialAccounts()->save($this->socialAccount);
77             \Session::flash('success', title_case($socialDriver) . ' account was successfully attached to your profile.');
78             return redirect($currentUser->getEditUrl());
79         }
80
81         // When a user is logged in and the social account exists and is already linked to the current user.
82         if ($isLoggedIn && $socialAccount !== null && $socialAccount->user->id === $currentUser->id) {
83             \Session::flash('error', 'This ' . title_case($socialDriver) . ' account is already attached to your profile.');
84             return redirect($currentUser->getEditUrl());
85         }
86
87         // When a user is logged in, A social account exists but the users do not match.
88         // Change the user that the social account is assigned to.
89         if ($isLoggedIn && $socialAccount !== null && $socialAccount->user->id != $currentUser->id) {
90             \Session::flash('success', 'This ' . title_case($socialDriver) . ' account is already used buy another user.');
91             return redirect($currentUser->getEditUrl());
92         }
93
94         // Otherwise let the user know this social account is not used by anyone.
95         $message = 'This ' . $socialDriver . ' account is not linked to any users. Please attach it in your profile settings';
96         if(\Setting::get('registration-enabled')) {
97             $message .= 'or, If you do not yet have an account, You can register an account using the ' . $socialDriver . ' option';
98         }
99         throw new SocialSignInException($message . '.', '/login');
100     }
101
102     private function logUserIn($user)
103     {
104         auth()->login($user);
105         return redirect('/');
106     }
107
108     /**
109      * Ensure the social driver is correct and supported.
110      *
111      * @param $socialDriver
112      * @return string
113      * @throws SocialDriverNotConfigured
114      */
115     private function validateDriver($socialDriver)
116     {
117         $driver = trim(strtolower($socialDriver));
118
119         if (!in_array($driver, $this->validSocialDrivers)) abort(404, 'Social Driver Not Found');
120         if (!$this->checkDriverConfigured($driver)) throw new SocialDriverNotConfigured;
121
122         return $driver;
123     }
124
125     /**
126      * Check a social driver has been configured correctly.
127      * @param $driver
128      * @return bool
129      */
130     private function checkDriverConfigured($driver)
131     {
132         $upperName = strtoupper($driver);
133         $config = [env($upperName . '_APP_ID', false), env($upperName . '_APP_SECRET', false), env('APP_URL', false)];
134         return (!in_array(false, $config) && !in_array(null, $config));
135     }
136
137     /**
138      * Gets the names of the active social drivers.
139      * @return array
140      */
141     public function getActiveDrivers()
142     {
143         $activeDrivers = [];
144         foreach ($this->validSocialDrivers as $driverName) {
145             if ($this->checkDriverConfigured($driverName)) {
146                 $activeDrivers[$driverName] = true;
147             }
148         }
149         return $activeDrivers;
150     }
151
152     /**
153      * @param $socialDriver
154      * @param $socialUser
155      */
156     private function fillSocialAccount($socialDriver, $socialUser)
157     {
158         $this->socialAccount->fill([
159             'driver'    => $socialDriver,
160             'driver_id' => $socialUser->getId(),
161             'avatar'    => $socialUser->getAvatar()
162         ]);
163     }
164
165     /**
166      * Detach a social account from a user.
167      * @param $socialDriver
168      * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
169      */
170     public function detachSocialAccount($socialDriver)
171     {
172         session();
173         auth()->user()->socialAccounts()->where('driver', '=', $socialDriver)->delete();
174         \Session::flash('success', $socialDriver . ' account successfully detached');
175         return redirect(auth()->user()->getEditUrl());
176     }
177
178 }