Mark TwitterAuthAction as final
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / action / GithubAuthAction.class.php
CommitLineData
320f4a6d 1<?php
a9229942 2
320f4a6d 3namespace wcf\action;
a9229942 4
8b2a995f 5use GuzzleHttp\Psr7\Request;
48e906fa 6use Laminas\Diactoros\Response\RedirectResponse;
85176ea5 7use Psr\Http\Client\ClientExceptionInterface;
320f4a6d 8use wcf\data\user\User;
1da19348 9use wcf\form\AccountManagementForm;
8b2a995f 10use wcf\form\RegisterForm;
301d5ea4 11use wcf\system\event\EventHandler;
320f4a6d 12use wcf\system\exception\NamedUserException;
320f4a6d 13use wcf\system\request\LinkHandler;
301d5ea4 14use wcf\system\user\authentication\event\UserLoggedIn;
8b2a995f 15use wcf\system\user\authentication\oauth\User as OauthUser;
320f4a6d 16use wcf\system\WCF;
320f4a6d
MW
17use wcf\util\JSON;
18use wcf\util\StringUtil;
19
20/**
8b2a995f 21 * Performs authentication against GitHub.com
a9229942
TD
22 *
23 * @author Tim Duesterhus
24 * @copyright 2001-2021 WoltLab GmbH
25 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
26 * @package WoltLabSuite\Core\Action
320f4a6d 27 */
a9229942
TD
28final class GithubAuthAction extends AbstractOauth2Action
29{
30 /**
31 * @inheritDoc
32 */
33 public $neededModules = ['GITHUB_PUBLIC_KEY', 'GITHUB_PRIVATE_KEY'];
34
35 /**
36 * @inheritDoc
37 */
38 protected function getTokenEndpoint(): string
39 {
40 return 'https://github.com/login/oauth/access_token';
41 }
42
43 /**
44 * @inheritDoc
45 */
46 protected function getClientId(): string
47 {
48 return StringUtil::trim(GITHUB_PUBLIC_KEY);
49 }
50
51 /**
52 * @inheritDoc
53 */
54 protected function getClientSecret(): string
55 {
56 return StringUtil::trim(GITHUB_PRIVATE_KEY);
57 }
58
59 /**
60 * @inheritDoc
61 */
62 protected function getScope(): string
63 {
64 return 'user:email';
65 }
66
67 /**
68 * @inheritDoc
69 */
70 protected function getAuthorizeUrl(): string
71 {
72 return 'https://github.com/login/oauth/authorize';
73 }
74
75 /**
76 * @inheritDoc
77 */
78 protected function getCallbackUrl(): string
79 {
80 return LinkHandler::getInstance()->getControllerLink(self::class);
81 }
82
83 /**
84 * @inheritDoc
85 */
86 protected function supportsState(): bool
87 {
88 return true;
89 }
90
91 /**
92 * @inheritDoc
93 */
94 protected function getUser(array $accessToken): OauthUser
95 {
96 $request = new Request('GET', 'https://api.github.com/user', [
97 'accept' => 'application/json',
98 'authorization' => \sprintf('Bearer %s', $accessToken['access_token']),
99 ]);
100 $response = $this->getHttpClient()->send($request);
101 $parsed = JSON::decode((string)$response->getBody());
102
103 $parsed['__id'] = $parsed['id'];
104 $parsed['__username'] = $parsed['login'];
105 $parsed['accessToken'] = $accessToken;
106
107 return new OauthUser($parsed);
108 }
109
110 /**
111 * @inheritDoc
112 */
113 protected function processUser(OauthUser $oauthUser)
114 {
115 $user = User::getUserByAuthData('github:' . $oauthUser->getId());
116
117 if ($user->userID) {
118 if (WCF::getUser()->userID) {
119 // This account belongs to an existing user, but we are already logged in.
120 // This can't be handled.
121
122 throw new NamedUserException(
123 WCF::getLanguage()->getDynamicVariable('wcf.user.3rdparty.github.connect.error.inuse')
124 );
125 } else {
126 // This account belongs to an existing user, we are not logged in.
127 // Perform the login.
128
129 WCF::getSession()->changeUser($user);
130 WCF::getSession()->update();
301d5ea4
TD
131 EventHandler::getInstance()->fire(
132 new UserLoggedIn($user)
133 );
134
48e906fa
TD
135 return new RedirectResponse(
136 LinkHandler::getInstance()->getLink()
137 );
a9229942
TD
138 }
139 } else {
140 WCF::getSession()->register('__3rdPartyProvider', 'github');
141
142 if (WCF::getUser()->userID) {
143 // This account does not belong to anyone and we are already logged in.
144 // Thus we want to connect this account.
145
146 WCF::getSession()->register('__oauthUser', $oauthUser);
147
48e906fa 148 return new RedirectResponse(
1da19348
TD
149 LinkHandler::getInstance()->getControllerLink(
150 AccountManagementForm::class,
151 [],
152 '#3rdParty'
153 )
48e906fa 154 );
a9229942
TD
155 } else {
156 // This account does not belong to anyone and we are not logged in.
157 // Thus we want to connect this account to a newly registered user.
158
159 try {
160 $request = new Request('GET', 'https://api.github.com/user/emails', [
161 'accept' => 'application/json',
162 'authorization' => \sprintf('Bearer %s', $oauthUser["accessToken"]["access_token"]),
163 ]);
164 $response = $this->getHttpClient()->send($request);
165 $emails = JSON::decode((string)$response->getBody());
166
167 // search primary email
168 $email = $emails[0]['email'];
169 foreach ($emails as $tmp) {
170 if ($tmp['primary']) {
171 $email = $tmp['email'];
172 break;
173 }
174 }
175 $oauthUser["__email"] = $email;
85176ea5 176 } catch (ClientExceptionInterface $e) {
a9229942
TD
177 }
178
179 WCF::getSession()->register('__oauthUser', $oauthUser);
180 WCF::getSession()->register('__username', $oauthUser->getUsername());
181 WCF::getSession()->register('__email', $oauthUser->getEmail());
182
183 // We assume that bots won't register an external account first, so
184 // we skip the captcha.
185 WCF::getSession()->register('noRegistrationCaptcha', true);
186
187 WCF::getSession()->update();
a9229942 188
48e906fa
TD
189 return new RedirectResponse(
190 LinkHandler::getInstance()->getControllerLink(RegisterForm::class)
191 );
a9229942
TD
192 }
193 }
194 }
320f4a6d 195}