Commit | Line | Data |
---|---|---|
320f4a6d MW |
1 | <?php |
2 | namespace wcf\action; | |
3 | use wcf\data\user\User; | |
4 | use wcf\data\user\UserEditor; | |
5 | use wcf\system\exception\IllegalLinkException; | |
6 | use wcf\system\exception\NamedUserException; | |
7 | use wcf\system\exception\SystemException; | |
8 | use wcf\system\request\LinkHandler; | |
9 | use wcf\system\user\authentication\UserAuthenticationFactory; | |
10 | use wcf\system\WCF; | |
11 | use wcf\util\HeaderUtil; | |
12 | use wcf\util\HTTPRequest; | |
13 | use wcf\util\JSON; | |
14 | use wcf\util\StringUtil; | |
15 | ||
16 | /** | |
17 | * Handles github auth. | |
18 | * | |
19 | * @author Tim Duesterhus | |
c839bd49 | 20 | * @copyright 2001-2018 WoltLab GmbH |
320f4a6d | 21 | * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php> |
e71525e4 | 22 | * @package WoltLabSuite\Core\Action |
320f4a6d MW |
23 | */ |
24 | class GithubAuthAction extends AbstractAction { | |
25 | /** | |
0fcfe5f6 | 26 | * @inheritDoc |
320f4a6d | 27 | */ |
058cbd6a | 28 | public $neededModules = ['GITHUB_PUBLIC_KEY', 'GITHUB_PRIVATE_KEY']; |
320f4a6d MW |
29 | |
30 | /** | |
0fcfe5f6 | 31 | * @inheritDoc |
320f4a6d MW |
32 | */ |
33 | public function execute() { | |
34 | parent::execute(); | |
35 | ||
36 | // user accepted the connection | |
37 | if (isset($_GET['code'])) { | |
38 | try { | |
39 | // fetch access_token | |
058cbd6a | 40 | $request = new HTTPRequest('https://github.com/login/oauth/access_token', [], [ |
8fa67fb3 TD |
41 | 'client_id' => StringUtil::trim(GITHUB_PUBLIC_KEY), |
42 | 'client_secret' => StringUtil::trim(GITHUB_PRIVATE_KEY), | |
320f4a6d | 43 | 'code' => $_GET['code'] |
058cbd6a | 44 | ]); |
320f4a6d MW |
45 | $request->execute(); |
46 | $reply = $request->getReply(); | |
47 | ||
48 | $content = $reply['body']; | |
49 | } | |
50 | catch (SystemException $e) { | |
ca3579ce | 51 | \wcf\functions\exception\logThrowable($e); |
320f4a6d MW |
52 | throw new IllegalLinkException(); |
53 | } | |
54 | ||
55 | // validate state, validation of state is executed after fetching the access_token to invalidate 'code' | |
56 | if (!isset($_GET['state']) || $_GET['state'] != WCF::getSession()->getVar('__githubInit')) throw new IllegalLinkException(); | |
f5f2f408 | 57 | WCF::getSession()->unregister('__githubInit'); |
320f4a6d MW |
58 | |
59 | parse_str($content, $data); | |
60 | ||
61 | // check whether the token is okay | |
62 | if (isset($data['error'])) throw new IllegalLinkException(); | |
63 | ||
e37288a1 TD |
64 | try { |
65 | // fetch userdata | |
66 | $request = new HTTPRequest('https://api.github.com/user?access_token='.$data['access_token']); | |
67 | $request->execute(); | |
68 | $reply = $request->getReply(); | |
69 | $userData = JSON::decode(StringUtil::trim($reply['body'])); | |
70 | } | |
71 | catch (SystemException $e) { | |
ca3579ce | 72 | \wcf\functions\exception\logThrowable($e); |
e37288a1 TD |
73 | throw new IllegalLinkException(); |
74 | } | |
75 | ||
320f4a6d | 76 | // check whether a user is connected to this github account |
6901535c | 77 | $user = User::getUserByAuthData('github:'.$userData['id']); |
e37288a1 | 78 | if (!$user->userID) { |
903c645f | 79 | $user = User::getUserByAuthData('github:'.$data['access_token']); |
e37288a1 | 80 | $userEditor = new UserEditor($user); |
058cbd6a | 81 | $userEditor->update(['authData' => 'github:'.$userData['id']]); |
e37288a1 | 82 | } |
320f4a6d MW |
83 | |
84 | if ($user->userID) { | |
85 | // a user is already connected, but we are logged in, break | |
86 | if (WCF::getUser()->userID) { | |
cecddd36 | 87 | throw new NamedUserException(WCF::getLanguage()->getDynamicVariable('wcf.user.3rdparty.github.connect.error.inuse')); |
320f4a6d MW |
88 | } |
89 | // perform login | |
90 | else { | |
91 | if (UserAuthenticationFactory::getInstance()->getUserAuthentication()->supportsPersistentLogins()) { | |
92 | $password = StringUtil::getRandomID(); | |
93 | $userEditor = new UserEditor($user); | |
058cbd6a | 94 | $userEditor->update(['password' => $password]); |
320f4a6d MW |
95 | |
96 | // reload user to retrieve salt | |
97 | $user = new User($user->userID); | |
98 | ||
99 | UserAuthenticationFactory::getInstance()->getUserAuthentication()->storeAccessData($user, $user->username, $password); | |
100 | } | |
101 | ||
102 | WCF::getSession()->changeUser($user); | |
103 | WCF::getSession()->update(); | |
104 | HeaderUtil::redirect(LinkHandler::getInstance()->getLink()); | |
105 | } | |
106 | } | |
107 | else { | |
f5f2f408 | 108 | WCF::getSession()->register('__3rdPartyProvider', 'github'); |
320f4a6d MW |
109 | // save data for connection |
110 | if (WCF::getUser()->userID) { | |
111 | WCF::getSession()->register('__githubUsername', $userData['login']); | |
112 | WCF::getSession()->register('__githubToken', $data['access_token']); | |
113 | ||
114 | HeaderUtil::redirect(LinkHandler::getInstance()->getLink('AccountManagement').'#3rdParty'); | |
115 | } | |
116 | // save data and redirect to registration | |
117 | else { | |
118 | WCF::getSession()->register('__githubData', $userData); | |
119 | WCF::getSession()->register('__username', $userData['login']); | |
120 | ||
e37288a1 TD |
121 | try { |
122 | $request = new HTTPRequest('https://api.github.com/user/emails?access_token='.$data['access_token']); | |
123 | $request->execute(); | |
124 | $reply = $request->getReply(); | |
125 | $emails = JSON::decode(StringUtil::trim($reply['body'])); | |
126 | ||
127 | // search primary email | |
128 | $email = $emails[0]['email']; | |
129 | foreach ($emails as $tmp) { | |
130 | if ($tmp['primary']) $email = $tmp['email']; | |
131 | break; | |
320f4a6d | 132 | } |
e37288a1 TD |
133 | WCF::getSession()->register('__email', $email); |
134 | } | |
135 | catch (SystemException $e) { | |
320f4a6d | 136 | } |
320f4a6d MW |
137 | WCF::getSession()->register('__githubToken', $data['access_token']); |
138 | ||
139 | // we assume that bots won't register on github first | |
ed7256dd | 140 | // thus no need for a captcha |
fbb526f2 | 141 | if (REGISTER_USE_CAPTCHA) { |
ed7256dd MS |
142 | WCF::getSession()->register('noRegistrationCaptcha', true); |
143 | } | |
320f4a6d MW |
144 | |
145 | WCF::getSession()->update(); | |
146 | HeaderUtil::redirect(LinkHandler::getInstance()->getLink('Register')); | |
147 | } | |
148 | } | |
149 | ||
150 | $this->executed(); | |
151 | exit; | |
152 | } | |
153 | // user declined or any other error that may occur | |
154 | if (isset($_GET['error'])) { | |
6f49de3a | 155 | throw new NamedUserException(WCF::getLanguage()->getDynamicVariable('wcf.user.3rdparty.github.login.error.'.$_GET['error'])); |
320f4a6d MW |
156 | } |
157 | ||
158 | // start auth by redirecting to github | |
159 | $token = StringUtil::getRandomID(); | |
160 | WCF::getSession()->register('__githubInit', $token); | |
8fa67fb3 | 161 | HeaderUtil::redirect("https://github.com/login/oauth/authorize?client_id=".rawurlencode(StringUtil::trim(GITHUB_PUBLIC_KEY))."&scope=".rawurlencode('user:email')."&state=".$token); |
320f4a6d MW |
162 | $this->executed(); |
163 | exit; | |
164 | } | |
320f4a6d | 165 | } |