]> BookStack Code Mirror - bookstack/blob - app/Http/Controllers/Auth/SocialController.php
Merge branch 'v21.05.x'
[bookstack] / app / Http / Controllers / Auth / SocialController.php
1 <?php
2
3 namespace BookStack\Http\Controllers\Auth;
4
5 use BookStack\Actions\ActivityType;
6 use BookStack\Auth\Access\RegistrationService;
7 use BookStack\Auth\Access\SocialAuthService;
8 use BookStack\Exceptions\SocialDriverNotConfigured;
9 use BookStack\Exceptions\SocialSignInAccountNotUsed;
10 use BookStack\Exceptions\SocialSignInException;
11 use BookStack\Exceptions\UserRegistrationException;
12 use BookStack\Facades\Theme;
13 use BookStack\Http\Controllers\Controller;
14 use BookStack\Theming\ThemeEvents;
15 use Illuminate\Http\Request;
16 use Illuminate\Support\Str;
17 use Laravel\Socialite\Contracts\User as SocialUser;
18
19 class SocialController extends Controller
20 {
21     protected $socialAuthService;
22     protected $registrationService;
23
24     /**
25      * SocialController constructor.
26      */
27     public function __construct(SocialAuthService $socialAuthService, RegistrationService $registrationService)
28     {
29         $this->middleware('guest')->only(['getRegister', 'postRegister']);
30         $this->socialAuthService = $socialAuthService;
31         $this->registrationService = $registrationService;
32     }
33
34     /**
35      * Redirect to the relevant social site.
36      *
37      * @throws SocialDriverNotConfigured
38      */
39     public function login(string $socialDriver)
40     {
41         session()->put('social-callback', 'login');
42
43         return $this->socialAuthService->startLogIn($socialDriver);
44     }
45
46     /**
47      * Redirect to the social site for authentication intended to register.
48      *
49      * @throws SocialDriverNotConfigured
50      * @throws UserRegistrationException
51      */
52     public function register(string $socialDriver)
53     {
54         $this->registrationService->ensureRegistrationAllowed();
55         session()->put('social-callback', 'register');
56
57         return $this->socialAuthService->startRegister($socialDriver);
58     }
59
60     /**
61      * The callback for social login services.
62      *
63      * @throws SocialSignInException
64      * @throws SocialDriverNotConfigured
65      * @throws UserRegistrationException
66      */
67     public function callback(Request $request, string $socialDriver)
68     {
69         if (!session()->has('social-callback')) {
70             throw new SocialSignInException(trans('errors.social_no_action_defined'), '/login');
71         }
72
73         // Check request for error information
74         if ($request->has('error') && $request->has('error_description')) {
75             throw new SocialSignInException(trans('errors.social_login_bad_response', [
76                 'socialAccount' => $socialDriver,
77                 'error'         => $request->get('error_description'),
78             ]), '/login');
79         }
80
81         $action = session()->pull('social-callback');
82
83         // Attempt login or fall-back to register if allowed.
84         $socialUser = $this->socialAuthService->getSocialUser($socialDriver);
85         if ($action === 'login') {
86             try {
87                 return $this->socialAuthService->handleLoginCallback($socialDriver, $socialUser);
88             } catch (SocialSignInAccountNotUsed $exception) {
89                 if ($this->socialAuthService->driverAutoRegisterEnabled($socialDriver)) {
90                     return $this->socialRegisterCallback($socialDriver, $socialUser);
91                 }
92
93                 throw $exception;
94             }
95         }
96
97         if ($action === 'register') {
98             return $this->socialRegisterCallback($socialDriver, $socialUser);
99         }
100
101         return redirect()->back();
102     }
103
104     /**
105      * Detach a social account from a user.
106      */
107     public function detach(string $socialDriver)
108     {
109         $this->socialAuthService->detachSocialAccount($socialDriver);
110         session()->flash('success', trans('settings.users_social_disconnected', ['socialAccount' => Str::title($socialDriver)]));
111
112         return redirect(user()->getEditUrl());
113     }
114
115     /**
116      * Register a new user after a registration callback.
117      *
118      * @throws UserRegistrationException
119      */
120     protected function socialRegisterCallback(string $socialDriver, SocialUser $socialUser)
121     {
122         $socialUser = $this->socialAuthService->handleRegistrationCallback($socialDriver, $socialUser);
123         $socialAccount = $this->socialAuthService->newSocialAccount($socialDriver, $socialUser);
124         $emailVerified = $this->socialAuthService->driverAutoConfirmEmailEnabled($socialDriver);
125
126         // Create an array of the user data to create a new user instance
127         $userData = [
128             'name'     => $socialUser->getName(),
129             'email'    => $socialUser->getEmail(),
130             'password' => Str::random(32),
131         ];
132
133         // Take name from email address if empty
134         if (!$userData['name']) {
135             $userData['name'] = explode('@', $userData['email'])[0];
136         }
137
138         $user = $this->registrationService->registerUser($userData, $socialAccount, $emailVerified);
139         auth()->login($user);
140         Theme::dispatch(ThemeEvents::AUTH_LOGIN, $socialDriver, $user);
141         $this->logActivity(ActivityType::AUTH_LOGIN, $user);
142
143         $this->showSuccessNotification(trans('auth.register_success'));
144
145         return redirect('/');
146     }
147 }