Merge branch '3.0'
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / action / GithubAuthAction.class.php
CommitLineData
320f4a6d
MW
1<?php
2namespace wcf\action;
3use wcf\data\user\User;
4use wcf\data\user\UserEditor;
5use wcf\system\exception\IllegalLinkException;
6use wcf\system\exception\NamedUserException;
7use wcf\system\exception\SystemException;
8use wcf\system\request\LinkHandler;
9use wcf\system\user\authentication\UserAuthenticationFactory;
10use wcf\system\WCF;
11use wcf\util\HeaderUtil;
12use wcf\util\HTTPRequest;
13use wcf\util\JSON;
14use 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 */
24class 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}