9.6. Social Login
Social login is a form of single sign-on that allows you to use credentials from a social networking site such as Facebook, Twitter or Google+, to sign in to a CUBA application instead of creating a new login account explicitly for the application.
In the following example, we will consider the social login using Facebook. Facebook uses OAuth2 authorization mechanism, for more details consult the documentation on Facebook API and Facebook Login Flow: https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow.
The source code of this sample project is available on GitHub, below are the key points of the social login implementation.
-
In order to connect your application to Facebook, you will need to create the App ID (unique application identifier) and App Secret (a kind of password that authenticates requests from your application to Facebook servers). Follow the instruction and register the generated values in
app.properties
file of the core module as thefacebook.appId
andfacebook.appSecret
application properties respectively, for example:facebook.appId = 123456789101112 facebook.appSecret = 123456789101112abcde131415fghi16
Also register the URL that you used for the Facebook app registration in the cuba.webAppUrl property in the core and web application properties files, for example:
cuba.webAppUrl = http://cuba-fb.test:8080/app
-
Extend the login screen and add the button for social login. This button will invoke the
loginFacebook()
method - the entry point to the social login flow. -
To use Facebook user accounts, we need to add one extra field to the standard CUBA user account. Extend the
User
entity and add thefacebookId
attribute of String type to it:@Column(name = "FACEBOOK_ID") protected String facebookId;
-
Create a service that will look for a user with a given
facebookId
in the application database and either return the existent user or create a new one:public interface SocialRegistrationService { String NAME = "demo_SocialRegistrationService"; User findOrRegisterUser(String facebookId, String email, String name); }
@Service(SocialRegistrationService.NAME) public class SocialRegistrationServiceBean implements SocialRegistrationService { @Inject private Metadata metadata; @Inject private Persistence persistence; @Inject private Configuration configuration; @Override @Transactional public User findOrRegisterUser(String facebookId, String email, String name) { EntityManager em = persistence.getEntityManager(); TypedQuery<SocialUser> query = em.createQuery("select u from sec$User u where u.facebookId = :facebookId", SocialUser.class); query.setParameter("facebookId", facebookId); query.setViewName(View.LOCAL); SocialUser existingUser = query.getFirstResult(); if (existingUser != null) { return existingUser; } SocialRegistrationConfig config = configuration.getConfig(SocialRegistrationConfig.class); Group defaultGroup = em.find(Group.class, config.getDefaultGroupId(), View.MINIMAL); SocialUser user = metadata.create(SocialUser.class); user.setFacebookId(facebookId); user.setEmail(email); user.setName(name); user.setGroup(defaultGroup); user.setActive(true); user.setLogin(email); em.persist(user); return user; } }
-
Create a service to manage the login process. In this example it is FacebookService that contains two methods:
getLoginUrl()
andgetUserData()
.-
getLoginUrl()
will generate the login URL based on the application URL and OAuth2 response type (code, access token or both; see more on response types in Facebook API documentation). The complete implementation of this method you can find in the FacebookServiceBean.java file. -
getUserData()
will look for the Facebook user by the given application URL and code, and either return the personal data of the existent user or create a new one. In this example we want to get the user’s id, name and email, the id will correspond thefacebookId
attribute created above.
-
-
Define the
facebook.fields
andfacebook.scope
application properties in theapp.properties
file of the core module:facebook.fields = id,name,email facebook.scope = email
-
Go back to the
loginFacebook()
method in the controller of the extended login window. The full code of the controller you can find in the ExtAppLoginWindow.java file.In this method we add the request handler to the current session, save the current URL and invoke the redirect to the Facebook authentication form in the browser:
private RequestHandler facebookCallBackRequestHandler = this::handleFacebookCallBackRequest; private URI redirectUri; @Inject private FacebookService facebookService; @Inject private GlobalConfig globalConfig; public void loginFacebook() { VaadinSession.getCurrent() .addRequestHandler(facebookCallBackRequestHandler); this.redirectUri = Page.getCurrent().getLocation(); String loginUrl = facebookService.getLoginUrl(globalConfig.getWebAppUrl(), OAuth2ResponseType.CODE); Page.getCurrent() .setLocation(loginUrl); }
The
handleFacebookCallBackRequest()
method will handle the callback after the Facebook authentication form. Firstly, we use theUIAccessor
instance to lock the UI until the login request is proceeded.Then,
FacebookService
will get the Facebook user account email and id. After that, the corresponding CUBA user will be found byfacebookId
or registered on the fly in the system.Next, the authentication is triggered, the user session on behalf of this user is loaded, and the UI is updated. After that, we remove the Facebook callback handler, as far as we no longer expect authentication.
public boolean handleFacebookCallBackRequest(VaadinSession session, VaadinRequest request, VaadinResponse response) throws IOException { if (request.getParameter("code") != null) { uiAccessor.accessSynchronously(() -> { try { String code = request.getParameter("code"); FacebookUserData userData = facebookService.getUserData(globalConfig.getWebAppUrl(), code); User user = socialRegistrationService.findOrRegisterUser( userData.getId(), userData.getEmail(), userData.getName()); App app = App.getInstance(); Connection connection = app.getConnection(); Locale defaultLocale = messages.getTools().getDefaultLocale(); connection.login(new ExternalUserCredentials(user.getLogin(), defaultLocale)); } catch (Exception e) { log.error("Unable to login using Facebook", e); } finally { session.removeRequestHandler(facebookCallBackRequestHandler); } }); ((VaadinServletResponse) response).getHttpServletResponse(). sendRedirect(ControllerUtils.getLocationWithoutParams(redirectUri)); return true; } return false; }
Now, when a user clicks the Facebook button on the login screen, the application will ask them for permission to use their Facebook profile and email, and if this permission is granted, the logged in user will be redirected to the main application page.
You can implement your own login mechanism using custom LoginProvider
, HttpRequestFilter
or events described in the Web Login section.