Merge pull request #881 from opensupports/google-social-login

Google social login
This commit is contained in:
Maximiliano Redigonda 2020-08-24 09:34:29 -03:00 committed by GitHub
commit 6e6d2d83e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 98 additions and 9 deletions

View File

@ -68,11 +68,13 @@ export default {
logout() {
return {
type: 'LOGOUT',
payload: API.call({
path: '/user/logout',
data: {}
})
};
payload: Promise.resolve()
.then(() => gapi && gapi.auth2 && gapi.auth2.getAuthInstance().signOut())
.then(() => API.call({
path: '/user/logout',
data: {}
}))
}
},
getUserData(userId, token, staff) {

View File

@ -35,6 +35,11 @@ describe('Login/Recover Widget', function () {
let dispatch = stub();
function renderComponent(props = {session: {pending: false, failed: false}}) {
window.gapi = {
load() {
return null;
}
};
component = reRenderIntoDocument(
<MainHomePageLoginWidget dispatch={dispatch} {...props}/>
);
@ -116,6 +121,11 @@ describe('Login/Recover Widget', function () {
let dispatch = stub();
beforeEach(function () {
window.gapi = {
load() {
return null;
}
};
component = TestUtils.renderIntoDocument(
<MainHomePageLoginWidget dispatch={dispatch} session={{pending: false, failed: false}} />
);

View File

@ -1,12 +1,10 @@
import React from 'react';
import ReactDOM from 'react-dom';
import {connect} from 'react-redux';
import classNames from 'classnames';
import _ from 'lodash';
import SessionActions from 'actions/session-actions';
import API from 'lib-app/api-call';
import focus from 'lib-core/focus';
import i18n from 'lib-app/i18n';
import PasswordRecovery from 'app-components/password-recovery';
@ -35,6 +33,10 @@ class MainHomePageLoginWidget extends React.Component {
}
}
componentDidMount() {
this.renderGoogleButton();
}
render() {
return (
<WidgetTransition sideToShow={this.state.sideToShow} className={classNames('login-widget__container', this.props.className)}>
@ -49,6 +51,7 @@ class MainHomePageLoginWidget extends React.Component {
<Widget className="main-home-page__widget" title={i18n('LOG_IN')} ref="loginWidget">
<Form {...this.getLoginFormProps()}>
<div className="login-widget__inputs">
<div id="google-oauth-id">Loading Google Login ...</div>
<FormField placeholder={i18n('EMAIL_LOWERCASE')} name="email" className="login-widget__input" validation="EMAIL" required/>
<FormField placeholder={i18n('PASSWORD_LOWERCASE')} name="password" className="login-widget__input" required fieldProps={{password: true}}/>
<FormField name="remember" label={i18n('REMEMBER_ME')} className="login-widget__input" field="checkbox"/>
@ -64,6 +67,23 @@ class MainHomePageLoginWidget extends React.Component {
);
}
renderGoogleButton() {
window.gapi.load('auth2', () => {
gapi.auth2.init({client_id: '50174278643-gtvjdpm5rmkv75lf3jsp95iv77a2usgu.apps.googleusercontent.com'})
gapi.signin2.render('google-oauth-id', {
scope: 'email',
width: 200,
height: 30,
longtitle: true,
theme: 'dark',
onsuccess: this.onGoogleLoginSuccess.bind(this),
onfailure: (response) => {
console.log(response);
}
})
})
}
renderPasswordRecovery() {
return (
<PasswordRecovery ref="passwordRecovery" recoverSent={this.state.recoverSent} formProps={this.getRecoverFormProps()} onBackToLoginClick={this.onBackToLoginClick.bind(this)}/>
@ -126,6 +146,12 @@ class MainHomePageLoginWidget extends React.Component {
this.props.dispatch(SessionActions.login(formState));
}
onGoogleLoginSuccess(googleUser) {
let id_token = googleUser.getAuthResponse().id_token;
console.log(id_token);
this.props.dispatch(SessionActions.login({'googleId': id_token, 'remember': 1}));
}
onForgotPasswordSubmit(formState) {
this.setState({
loadingRecover: true,

View File

@ -16,6 +16,7 @@
<script>
window.customTicketList = [];
</script>
<script src="https://apis.google.com/js/platform.js" async defer></script>
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=String.prototype.startsWith,Array.from,Array.prototype.fill,Array.prototype.keys,Array.prototype.find,Array.prototype.findIndex,Array.prototype.includes,String.prototype.repeat,Number.isInteger,Promise&flags=gated"></script>
</body>
</html>

View File

@ -10,7 +10,7 @@ build:
@docker network create os-net || true
install:
@docker exec -it opensupports-srv bash -c "cd /var/www/html && composer install" || echo "${red}Please execute 'make run' first${reset}"
@docker exec -it opensupports-srv bash -c "cd /var/www/html && composer update && composer install" || echo "${red}Please execute 'make run' first${reset}"
@docker exec -it opensupports-db bash -c "mysql -u root -e \"CREATE DATABASE IF NOT EXISTS development;\" " || echo "${red}Please execute 'make run' first${reset}"
run: stop

View File

@ -10,7 +10,8 @@
"codeguy/upload": "^1.3",
"php-imap/php-imap": "^3.0",
"willdurand/email-reply-parser": "^2.8",
"ext-fileinfo": "^1.0"
"ext-fileinfo": "^1.0",
"google/apiclient": "^2.7"
},
"require-dev": {
"phpunit/phpunit": "^5.7"

View File

@ -19,6 +19,7 @@ use RedBeanPHP\Facade as RedBean;
* @apiParam {Boolean} remember Indicates if the session wants to be remembered.
* @apiParam {Number} userId The id of the user to login.
* @apiParam {String} rememberToken Token to login automatically. It replaces the password.
* @apiParam {String} googleId Token to log in with Google.
*
* @apiUse UNVERIFIED_USER
* @apiUse INVALID_CREDENTIALS
@ -49,6 +50,24 @@ class LoginController extends Controller {
public function handler() {
$this->clearOldRememberTokens();
if ($this->checkGoogleLogin()) {
$client = new Google_Client(['client_id' => '50174278643-gtvjdpm5rmkv75lf3jsp95iv77a2usgu.apps.googleusercontent.com']);
$payload = $client->verifyIdToken(Controller::request('googleId'));
if ($payload && $payload['email_verified']) {
$this->userInstance = User::getUser($payload['email'], 'email');
if ($this->userInstance->isNull()) {
$this->userInstance = $this->createGoogleUser($payload);
}
Session::getInstance()->createSession($this->userInstance->id, false);
Response::respondSuccess($this->getUserData());
return;
} else {
throw new Exception("Invalid GoogleID token or unverified Google account");
}
}
if ($this->checkInputCredentials() || $this->checkRememberToken()) {
if($this->userInstance->verificationToken !== null) {
@ -73,6 +92,36 @@ class LoginController extends Controller {
}
}
private function checkGoogleLogin() {
return !!Controller::request('googleId');
}
private function createGoogleUser($payload) {
Controller::setDataRequester(function ($key) use ($payload) {
switch ($key) {
case 'email':
return $payload['email'];
case 'password':
return Hashing::generateRandomToken();
case 'name':
return $payload['name'];
}
return null;
});
$signupController = new SignUpController(true);
try {
$signupController->validate();
$signupController->handler();
} catch (\Exception $exception) {
throw new Exception("OpenSupports doesn't accept this Google account, failed validations: " . $exception);
}
return User::getUser($payload['email'], 'email');
}
private function checkInputCredentials() {
$this->userInstance = $this->getUserByInputCredentials();