Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add social sign in functionality (Google, Facebook, Twitter) #2155

Merged
merged 21 commits into from
Oct 21, 2015
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 53 additions & 5 deletions app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ JhipsterGenerator.prototype.askFor = function askFor() {
console.log(chalk.white('Welcome to the JHipster Generator ') + chalk.yellow('v' + packagejs.version + '\n'));
var insight = this.insight();
this.javaVersion = '8'; // Java version is forced to be 1.8. We keep the variable as it might be useful in the future.
var questions = 14; // making questions a variable to avoid updating each question by hand when adding additional options
var questions = 15; // making questions a variable to avoid updating each question by hand when adding additional options

var prompts = [
{
Expand Down Expand Up @@ -311,6 +311,12 @@ JhipsterGenerator.prototype.askFor = function askFor() {
name: 'enableTranslation',
message: '(14/' + questions + ') Would you like to enable translation support with Angular Translate?',
default: true
},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't put this in the end, but after the security question, so we have all the authentication questions at the same place.
Besides, does this work with the 3 different authent mechanisms?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the number 4 position?
I only test with the default one, I will test it!

{
type: 'confirm',
name: 'enableSocialSignIn',
message: '(15/' + questions + ') Would you like to enable social sign in (Google, Facebook, Twitter)?',
default: false
}
];

Expand Down Expand Up @@ -342,6 +348,7 @@ JhipsterGenerator.prototype.askFor = function askFor() {
this.frontendBuilder = this.config.get('frontendBuilder');
this.rememberMeKey = this.config.get('rememberMeKey');
this.enableTranslation = this.config.get('enableTranslation'); // this is enabled by default to avoid conflicts for existing applications
this.enableSocialSignIn = this.config.get('enableSocialSignIn');
this.packagejs = packagejs;

if (this.baseName != null &&
Expand All @@ -368,6 +375,11 @@ JhipsterGenerator.prototype.askFor = function askFor() {
this.enableTranslation = true;
}

// If social sign in is not defined, it is desable by default
if (this.enableSocialSignIn == null) {
this.enableSocialSignIn = false;
}

console.log(chalk.green('This is an existing project, using the configuration from your .yo-rc.json file \n' +
'to re-generate the project...\n'));

Expand All @@ -391,6 +403,7 @@ JhipsterGenerator.prototype.askFor = function askFor() {
this.buildTool = props.buildTool;
this.frontendBuilder = props.frontendBuilder;
this.enableTranslation = props.enableTranslation;
this.enableSocialSignIn = props.enableSocialSignIn;
this.rememberMeKey = crypto.randomBytes(20).toString('hex');

if (this.databaseType == 'mongodb') {
Expand Down Expand Up @@ -426,6 +439,7 @@ JhipsterGenerator.prototype.app = function app() {
insight.track('app/buildTool', this.buildTool);
insight.track('app/frontendBuilder', this.frontendBuilder);
insight.track('app/enableTranslation', this.enableTranslation);
insight.track('app/enableSocialSignIn', this.enableSocialSignIn);

var packageFolder = this.packageName.replace(/\./g, '/');
var javaDir = 'src/main/java/' + packageFolder + '/';
Expand Down Expand Up @@ -549,6 +563,9 @@ JhipsterGenerator.prototype.app = function app() {
// Create mail templates
this.copy(resourceDir + '/mails/activationEmail.html', resourceDir + 'mails/activationEmail.html');
this.copy(resourceDir + '/mails/passwordResetEmail.html', resourceDir + 'mails/passwordResetEmail.html');
if (this.enableSocialSignIn) {
this.copy(resourceDir + '/mails/socialRegistrationValidationEmail.html', resourceDir + 'mails/socialRegistrationValidationEmail.html');
}

// Create Java files
this.template('src/main/java/package/_Application.java', javaDir + '/Application.java', this, {});
Expand Down Expand Up @@ -756,6 +773,13 @@ JhipsterGenerator.prototype.app = function app() {
this.template('src/main/java/package/web/websocket/dto/_ActivityDTO.java', javaDir + 'web/websocket/dto/ActivityDTO.java', this, {});
}

if (this.enableSocialSignIn) {
this.template('src/main/java/package/social/_SocialController.java', javaDir + 'social/SocialController.java', this, {});
this.template('src/main/java/package/social/_SocialService.java', javaDir + 'social/SocialService.java', this, {});
this.template('src/main/java/package/social/config/_SimpleSignInAdapter.java', javaDir + 'social/config/SimpleSignInAdapter.java', this, {});
this.template('src/main/java/package/social/config/_SocialConfiguration.java', javaDir + 'social/config/SocialConfiguration.java', this, {});
}

// Create Test Java files
var testDir = 'src/test/java/' + packageFolder + '/';
var testResourceDir = 'src/test/resources/';
Expand Down Expand Up @@ -783,6 +807,10 @@ JhipsterGenerator.prototype.app = function app() {
this.template(testResourceDir + '_ehcache.xml', testResourceDir + 'ehcache.xml', this, {});
}

if (this.enableSocialSignIn) {
this.template('src/test/java/package/social/_SocialServiceTest.java', testDir + 'social/SocialServiceTest.java', this, {});
}

// Create Gatling test files
this.copy('src/test/gatling/conf/gatling.conf', 'src/test/gatling/conf/gatling.conf');
mkdirp('src/test/gatling/data');
Expand Down Expand Up @@ -858,6 +886,7 @@ JhipsterGenerator.prototype.app = function app() {
this.template(webappDir + '/scripts/components/navbar/_navbar.controller.js', webappDir + 'scripts/components/navbar/navbar.controller.js', this, {});
this.template(webappDir + '/scripts/components/user/_user.service.js', webappDir + 'scripts/components/user/user.service.js', this, {});
this.template(webappDir + '/scripts/components/util/_base64.service.js', webappDir + 'scripts/components/util/base64.service.js', this, {});
this.template(webappDir + '/scripts/components/util/_capitalize.filter.js', webappDir + 'scripts/components/util/capitalize.filter.js', this, {});
this.template(webappDir + '/scripts/components/util/_parse-links.service.js', webappDir + 'scripts/components/util/parse-links.service.js', this, {});
this.template(webappDir + '/scripts/components/util/_truncate.filter.js', webappDir + 'scripts/components/util/truncate.filter.js', this, {});
this.template(webappDir + '/scripts/components/util/_dateutil.service.js', webappDir + 'scripts/components/util/dateutil.service.js', this, {});
Expand Down Expand Up @@ -932,6 +961,16 @@ JhipsterGenerator.prototype.app = function app() {
this.copyJs(webappDir + '/scripts/app/main/_main.js', webappDir + 'scripts/app/main/main.js', this, {});
this.template(webappDir + '/scripts/app/main/_main.controller.js', webappDir + 'scripts/app/main/main.controller.js', this, {});

// Social
if (this.enableSocialSignIn) {
this.copyHtml(webappDir + '/scripts/app/account/social/directive/_social.html', webappDir + 'scripts/app/account/social/directive/social.html');
this.template(webappDir + '/scripts/app/account/social/directive/_social.directive.js', webappDir + 'scripts/app/account/social/directive/social.directive.js', this, {});
this.copyHtml(webappDir + '/scripts/app/account/social/_social-register.html', webappDir + 'scripts/app/account/social/social-register.html');
this.template(webappDir + '/scripts/app/account/social/_social-register.controller.js', webappDir + 'scripts/app/account/social/social-register.controller.js', this, {});
this.template(webappDir + '/scripts/app/account/social/_social.service.js', webappDir + 'scripts/app/account/social/social.service.js', this, {});
this.copyJs(webappDir + '/scripts/app/account/social/_social-register.js', webappDir + 'scripts/app/account/social/social-register.js', this, {});
}

// interceptor code
this.template(webappDir + '/scripts/components/interceptor/_auth.interceptor.js', webappDir + 'scripts/components/interceptor/auth.interceptor.js', this, {});
this.template(webappDir + '/scripts/components/interceptor/_errorhandler.interceptor.js', webappDir + 'scripts/components/interceptor/errorhandler.interceptor.js', this, {});
Expand Down Expand Up @@ -994,6 +1033,7 @@ JhipsterGenerator.prototype.app = function app() {
'scripts/components/user/user.service.js',
'scripts/components/util/truncate.filter.js',
'scripts/components/util/base64.service.js',
'scripts/components/util/capitalize.filter.js',
'scripts/components/alert/alert.service.js',
'scripts/components/alert/alert.directive.js',
'scripts/components/util/parse-links.service.js',
Expand Down Expand Up @@ -1043,6 +1083,13 @@ JhipsterGenerator.prototype.app = function app() {
'scripts/components/language/language.service.js',
'scripts/components/language/language.controller.js']);
}
if (this.enableSocialSignIn) {
appScripts = appScripts.concat([
'scripts/app/account/social/directive/social.directive.js',
'scripts/app/account/social/social-register.js',
'scripts/app/account/social/social-register.controller.js',
'scripts/app/account/social/social.service.js']);
}
if (this.authenticationType == 'xauth') {
appScripts = appScripts.concat([
'scripts/components/auth/provider/auth.xauth.service.js']);
Expand Down Expand Up @@ -1116,6 +1163,7 @@ JhipsterGenerator.prototype.app = function app() {
this.config.set('buildTool', this.buildTool);
this.config.set('frontendBuilder', this.frontendBuilder);
this.config.set('enableTranslation', this.enableTranslation);
this.config.set('enableSocialSignIn', this.enableSocialSignIn);
this.config.set('rememberMeKey', this.rememberMeKey);
};

Expand All @@ -1140,10 +1188,10 @@ function removefolder(folder) {
}

JhipsterGenerator.prototype.install = function install() {
this.installDependencies({
skipInstall: this.options['skip-install'],
callback: this._injectDependenciesAndConstants.bind(this)
});
this.installDependencies({
skipInstall: this.options['skip-install'],
callback: this._injectDependenciesAndConstants.bind(this)
});
};

JhipsterGenerator.prototype._injectDependenciesAndConstants = function _injectDependenciesAndConstants() {
Expand Down
12 changes: 10 additions & 2 deletions app/templates/_bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,24 +36,32 @@
<%_ if (websocket == 'spring-websocket') { _%>
"sockjs-client": "1.0.3",
"stomp-websocket": "2.3.4",
<%_ } _%> <%_ if (enableSocialSignIn) { _%>
"bootstrap-social": "4.10.1",
<%_ } _%>
"font-awesome": "4.4.0",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding font-awesome is probably going to add a lot of resources, at the moment we only use "normal" bootstrap -> the idea is that we provide something "light" by default, but it's easy for people to extend (just do a Bower install). Can you remove this, and add the Bootstrap class instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, two options:

  • I remove totally the font-awesome lib, and the icon of the social button too (because icon like Twitter, Facebook dont exist in bootstrap icon package)
  • I add font awesome only when social is enabled

Wich one you prefer?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Normally we use icons from Bootstrap, have a look at http://getbootstrap.com/components/

  • can you find something good enough?
  • anyway there might be copyright issues with using brand logos, I'd rather not go there

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No there are no brand icon on bootstrap icon set :(. I will remove font-awesome, and add to the documentation how to add the icon on the button.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jdubois I remove the font-awesome, but there is a problem, the social button is not compatible without icon. I can just create a simple bootstrap button, but we loose the user experience. On all the website it's the same type of button (icon and color).

My opinion it's keep font-awesome, I think the ratio weight/utility is good, like you said it's add more ressource but allow user to use more icons and some special effect like loading icon. I prefer to keep it for a better user experience.

But if you prefer to not use it I can just create a simple button with the label.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for Font Awesome, even if it's only included when someone wants social
authentication. I use it on all my client projects.
On Sat, Oct 17, 2015 at 07:38 Thibaut Mottet [email protected]
wrote:

In app/templates/_bower.json
#2155 (comment)
:

     <%_ } _%>
  •    "font-awesome": "4.4.0",
    

@jdubois https://github.com/jdubois I remove the font-awesome, but
there is a problem, the social button is not compatible without icon. I can
just create a simple bootstrap button, but we loose the user experience. On
all the website it's the same type of button (icon and color).

My opinion it's keep font-awesome, I think the ratio weight/utility is
good, like you said it's add more ressource but allow user to use more
icons and some special effect like loading icon. I prefer to keep it for a
better user experience.

But if you prefer to not use it I can just create a simple button with the
label.


Reply to this email directly or view it on GitHub
https://github.com/jhipster/generator-jhipster/pull/2155/files#r42309339
.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it's good to use Font Awesome, that's what I use for the main JHipster website :-)
But here I don't want to add lots of CSS/images for everyone -> it should be easy to add, but not here by default
@moifort could you just use a String? Or just some specific images?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I create button with string and css:
capture d ecran 2015-10-19 a 10 45 24

"swagger-ui": "2.1.2"
},
"devDependencies": {
"angular-mocks": "1.4.5",
"angular-scenario": "1.4.5"
},
<%_ if (!useSass) { _%>
"overrides": {
<%_ if (!useSass) { _%>
"bootstrap": {
"main": [
"dist/js/bootstrap.js",
"dist/css/bootstrap.css",
"less/bootstrap.less"
]
},
<%_ } _%>
"font-awesome": {
"main": [
"css/font-awesome.css"
]
}
},
<%_ } _%>
"resolutions": {
"angular": "1.4.5",
"angular-cookies": "1.4.5",
Expand Down
36 changes: 36 additions & 0 deletions app/templates/_pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@
<spring-security-oauth2.version>2.0.7.RELEASE</spring-security-oauth2.version>
<%_ } _%>
<springfox.version>2.0.3</springfox.version>
<%_ if (enableSocialSignIn) { _%>
<!-- Spring social -->
<httpclient.version>4.5.1</httpclient.version>
<spring-social-security.version>1.1.2.RELEASE</spring-social-security.version>
<spring-social-google.version>1.0.0.RELEASE</spring-social-google.version>
<spring-social-facebook.version>2.0.2.RELEASE</spring-social-facebook.version>
<spring-social-twitter.version>1.1.1.RELEASE</spring-social-twitter.version>
<%_ } _%>
<!-- Sonar properties -->
<project.testresult.directory>${project.build.directory}/test-results</project.testresult.directory>
<sonar-maven-plugin.version>2.6</sonar-maven-plugin.version>
Expand Down Expand Up @@ -530,6 +538,34 @@
<artifactId>metrics-spark-reporter</artifactId>
<version>${metrics-spark-reporter.version}</version>
</dependency>
<%_ if (enableSocialSignIn) { _%>
<!-- social -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${httpclient.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-security</artifactId>
<version>${spring-social-security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-google</artifactId>
<version>${spring-social-google.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-facebook</artifactId>
<version>${spring-social-facebook.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-twitter</artifactId>
<version>${spring-social-twitter.version}</version>
</dependency>
<%_ } _%>
</dependencies>
<build>
<defaultGoal>spring-boot:run</defaultGoal>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ public class JHipsterProperties {

private final Metrics metrics = new Metrics();

<%_ if (enableSocialSignIn) { _%>
private final Social social = new Social();
<%_ } _%>


public Async getAsync() {
return async;
}
Expand Down Expand Up @@ -62,6 +67,12 @@ public Metrics getMetrics() {
return metrics;
}

<%_ if (enableSocialSignIn) { _%>
public Social getSocial() {
return social;
}
<%_ } _%>

public static class Async {

private int corePoolSize = 2;
Expand Down Expand Up @@ -527,4 +538,17 @@ public void setPrefix(String prefix) {
}
}
}
<%_ if (enableSocialSignIn) { _%>
public static class Social {

private String redirectAfterSignIn = "/#/home";

public String getRedirectAfterSignIn() {
return redirectAfterSignIn;
}

public void setRedirectAfterSignIn(String redirectAfterSignIn) {
this.redirectAfterSignIn = redirectAfterSignIn;
}
}<%_ } _%>
}
6 changes: 4 additions & 2 deletions app/templates/src/main/java/package/domain/_User.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,12 @@ public class User<% if (databaseType == 'sql' || databaseType == 'mongodb') { %>
@PartitionKey
private String id;<% } %>

@NotNull
@NotNull<% if (enableSocialSignIn) { %>
@Size(min = 1, max = 100)<% if (databaseType == 'sql') { %>
@Column(length = 100, unique = true, nullable = false)<% } %><% } else { %>
@Pattern(regexp = "^[a-z0-9]*$|(anonymousUser)")
@Size(min = 1, max = 50)<% if (databaseType == 'sql') { %>
@Column(length = 50, unique = true, nullable = false)<% } %>
@Column(length = 50, unique = true, nullable = false)<% } %><% } %>
private String login;

@JsonIgnore
Expand Down
14 changes: 14 additions & 0 deletions app/templates/src/main/java/package/service/_MailService.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import org.springframework.stereotype.Service;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring4.SpringTemplateEngine;
<% if (enableSocialSignIn) { %>import org.apache.commons.lang.WordUtils;<% } %>


import javax.inject.Inject;
import javax.mail.internet.MimeMessage;
Expand Down Expand Up @@ -90,4 +92,16 @@ public void sendPasswordResetMail(User user, String baseUrl) {
String subject = messageSource.getMessage("email.reset.title", null, locale);
sendEmail(user.getEmail(), subject, content, false, true);
}
<% if (enableSocialSignIn) { %>
@Async
public void sendSocialRegistrationValidationEmail(User user, String provider) {
log.debug("Sending social registration validation e-mail to '{}'", user.getEmail());
Locale locale = Locale.forLanguageTag(user.getLangKey());
Context context = new Context(locale);
context.setVariable("user", user);
context.setVariable("provider", WordUtils.capitalize(provider));
String content = templateEngine.process("socialRegistrationValidationEmail", context);
String subject = messageSource.getMessage("email.social.registration.title", null, locale);
sendEmail(user.getEmail(), subject, content, false, true);
}<% } %>
}
44 changes: 44 additions & 0 deletions app/templates/src/main/java/package/social/_SocialController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package <%=packageName%>.social;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.web.ProviderSignInUtils;
import org.springframework.social.support.URIBuilder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.view.RedirectView;

import javax.inject.Inject;

@Controller
@RequestMapping("/social")
public class SocialController {
private final Logger log = LoggerFactory.getLogger(SocialController.class);

@Inject
private SocialService socialService;

@Inject
private ProviderSignInUtils providerSignInUtils;

@RequestMapping(value = "/signup", method = RequestMethod.GET)
public RedirectView signUp(WebRequest webRequest, @CookieValue("NG_TRANSLATE_LANG_KEY") String langKey) {
try {
final Connection<?> connection = providerSignInUtils.getConnectionFromSession(webRequest);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not the only place, you have lots of "final" keywords, they don't do anything and add more code -> can you remove them? So it's consistent with the rest of the code, which doesn't use them

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes sure!

socialService.createSocialUser(connection, langKey.replace("\"", ""));
return new RedirectView(URIBuilder.fromUri("/#/social-register/" + connection.getKey().getProviderId())
.queryParam("success", "true")
.build().toString(), true);
} catch (Exception e) {
log.error("Exception creating social user: ", e);
return new RedirectView(URIBuilder.fromUri("/#/social-register/no-provider")
.queryParam("success", "false")
.build().toString(), true);
}
}

}
Loading