Authenticating users through social media providers

duration 30 minutes
New

Prerequisites:

You’ll explore how to allow users to log in to your application with their social media accounts by using the Open Liberty Social Media Login feature.

What you’ll learn

Social media login provides a form of single sign-on (SSO) that application users can use to sign in to a secured website with their existing social media account. Social media login simplifies the authentication process for users and allows you to provide a secure authentication method for your users without having to explicitly implement it.

The Social Media Login feature in Open Liberty provides configuration elements to configure application authentication by using one or more social media platforms, including GitHub, LinkedIn, Facebook, Twitter, and Google. You can also create a custom configuration for any other social media platform that implements the OAuth 2.0 standard or the OpenID Connect 1.0 standard for authorization.

The application that is provided with this guide is secured through basic authentication. The application includes a simple welcome page that includes a message and a Log in button. The welcome page is defined in the hello.html file, and the Log in button is defined in the button element of that page. When the application user clicks the Log in button, a dialog box opens requesting the user’s username and password for authenticating to the service. In this guide, you will replace the basic authentication dialog with a dialog to log in with a social media provider.

hello.html

 1<html>
 2<head>
 3    <title>Social Media Login Guide</title>
 4</head>
 5<body>
 6<h1>Social Media Login Guide</h1>
 7<p>Welcome to the social media login guide</p>
 8<p>You are currently not authenticated!</p>
 9<!-- tag::login[] -->
10<!-- tag::request[] -->
11<form method="GET" action="hello">
12<!-- tag::request[] -->
13    <!-- tag::btnLogin[] -->
14    <button type="submit">Log in</button>
15    <!-- end::btnLogin[] -->
16</form>
17<!-- end::login[] -->
18</body>
19</html>

Getting started

The fastest way to work through this guide is to clone the Git repository and use the projects that are provided inside:

git clone https://github.com/openliberty/guide-social-media-login.git
cd guide-social-media-login

The start directory contains the starting project that you will build upon.

The finish directory contains the finished project that you will build.

Creating a GitHub OAuth2 application

Obtain an OAuth 2.0 client ID and client secret credentials for accessing the GitHub API by registering a new application in your GitHub account. Register a new application on the Settings > Developer settings > OAuth Apps page of your account.

Set the Homepage URL to https://localhost:9443, and set the authorization callback URL to https://localhost:9443/ibm/api/social-login/redirect/githubLogin.

When the registration is complete, the client ID and client secret credentials are displayed. To provide your application with the credentials, export the GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET environment variables.

For more information about creating an OAuth application, see the GitHub documentation.

Replace the values of the [github-client-id] and [github-client-secret] fields in the following command:

export GITHUB_CLIENT_ID=[github-client-id]
export GITHUB_CLIENT_SECRET=[github-client-secret]
set GITHUB_CLIENT_ID=[github-client-id]
set GITHUB_CLIENT_SECRET=[github-client-secret]

Building and running the application in dev mode

First, navigate to the start directory.

When you run Open Liberty in development mode, known as dev mode, the server listens for file changes and automatically recompiles and deploys your updates whenever you save a new change. Run the following goal to start Open Liberty in dev mode:

mvn liberty:dev

After you see the following message, your application server in dev mode is ready:

Press the Enter key to run tests on demand.

Dev mode holds your command-line session to listen for file changes. Open another command-line session to continue, or open the project in your editor.

Configuring GitHub as a social media login provider

Enable the Social Media Login feature in the application by updating the server.xml configuration file.

Replace the server.xml file.
src/main/liberty/config/server.xml

The socialLogin-1.0 feature definition enables the Social Media Login feature in the application so that you can use the githubLogin configuration element to configure GitHub as a social media login provider.

The client ID and client secret credentials for your GitHub OAuth2 application are injected into the githubLogin configuration element with values of github.client.id and github.client.secret. These values are supplied by the GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET environment variables.

server.xml

 1<server description="Social Login Guide Server">
 2    <!-- tag::featureManager[] -->
 3    <featureManager>
 4        <feature>jsp-2.3</feature>
 5        <feature>appSecurity-2.0</feature>
 6        <feature>transportSecurity-1.0</feature>
 7        <feature>mpConfig-1.4</feature>
 8        <feature>jaxrsClient-2.1</feature>
 9        <feature>cdi-2.0</feature>
10        <feature>jsonb-1.0</feature>
11        <feature>jwt-1.0</feature>
12        <!-- tag::socialLogin[] -->
13        <feature>socialLogin-1.0</feature>
14        <!-- end::socialLogin[] -->
15    </featureManager>
16    <!-- end::featureManager[] -->
17
18    <httpEndpoint httpPort="${default.http.port}"
19                  httpsPort="${default.https.port}"
20                  id="defaultHttpEndpoint"
21                  host="*" />
22
23    <!-- Set up default key store, trust stores for auth APIs -->
24    <!-- tag::keystore[] -->
25    <keyStore id="defaultKeyStore"
26              password="changeit" />
27    <!-- end::keystore[] -->
28
29    <!-- SSL configuration for auth services -->
30    <!-- tag::sslConfig[] -->
31    <ssl id="defaultSSLConfig"
32         keyStoreRef="defaultKeyStore"
33         trustDefaultCerts="true" />
34    <!-- end::sslConfig[] -->
35
36    <!-- Social Login Configuration -->
37    <!-- Github Login -->
38    <!-- tag::githubLogin[] -->
39    <githubLogin clientId="${github.client.id}"
40                 clientSecret="${github.client.secret}" />
41    <!-- end::githubLogin[] -->
42
43    <!-- tag::webApplication[] -->
44    <webApplication location="guide-social-login.war"
45                    contextRoot="${app.context.root}">
46        <!-- tag::applicationBnd[] -->
47        <application-bnd>
48            <!-- tag::users[] -->
49            <security-role name="users">
50                <special-subject type="ALL_AUTHENTICATED_USERS"/>
51            </security-role>
52            <!-- end::users[] -->
53        </application-bnd>
54        <!-- end::applicationBnd[] -->
55    </webApplication>
56    <!-- end::webApplication[] -->
57</server>

Running the application

You started the Open Liberty server in dev mode at the beginning of the guide, so all the changes were automatically picked up.

Check out the service that you created by going to the http://localhost:9080/api/hello.html URL.

Try logging in with your social media account. After you log in with your GitHub account, authorize access to your GitHub user data with the OAuth2 application that you created in the Creating a GitHub OAuth2 application section. After authentication, you’re redirected to the /hello endpoint that’s served by HelloServlet, which also serves the securedHello.jsp page.

The securedHello.jsp page contains a Log out button that makes a POST request to the /logout endpoint, which is served by LogoutServlet.

Because the logout feature isn’t fully implemented, an error is returned when you click the Log out button:

 Exception thrown by application class 'io.openliberty.guides.sociallogin.LogoutServlet.doPost:50'
java.lang.NullPointerException:
at io.openliberty.guides.sociallogin.LogoutServlet.doPost(LogoutServlet.java:50)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:706)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:791)
at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1230)
at [internal classes]

hello.html

 1<html>
 2<head>
 3    <title>Social Media Login Guide</title>
 4</head>
 5<body>
 6<h1>Social Media Login Guide</h1>
 7<p>Welcome to the social media login guide</p>
 8<p>You are currently not authenticated!</p>
 9<!-- tag::login[] -->
10<!-- tag::request[] -->
11<form method="GET" action="hello">
12<!-- tag::request[] -->
13    <!-- tag::btnLogin[] -->
14    <button type="submit">Log in</button>
15    <!-- end::btnLogin[] -->
16</form>
17<!-- end::login[] -->
18</body>
19</html>

HelloServlet.java

 1// tag::copyright[]
 2/*******************************************************************************
 3 * Copyright (c) 2020 IBM Corporation and others.
 4 * All rights reserved. This program and the accompanying materials
 5 * are made available under the terms of the Eclipse Public License v1.0
 6 * which accompanies this distribution, and is available at
 7 * http://www.eclipse.org/legal/epl-v10.html
 8 *
 9 * Contributors:
10 *     IBM Corporation - Initial implementation
11 *******************************************************************************/
12// end::copyright[]
13package io.openliberty.guides.sociallogin;
14
15import javax.servlet.ServletException;
16import javax.servlet.annotation.HttpConstraint;
17import javax.servlet.annotation.ServletSecurity;
18import javax.servlet.annotation.WebServlet;
19import javax.servlet.http.HttpServlet;
20import javax.servlet.http.HttpServletRequest;
21import javax.servlet.http.HttpServletResponse;
22import java.io.IOException;
23
24@WebServlet(name = "HelloServlet", urlPatterns = "/hello")
25@ServletSecurity(value = @HttpConstraint(rolesAllowed = {"users"},
26        transportGuarantee = ServletSecurity.TransportGuarantee.CONFIDENTIAL))
27public class HelloServlet extends HttpServlet {
28
29    private static final long serialVersionUID = 1L;
30
31    @Override
32    protected void doGet(HttpServletRequest request, HttpServletResponse response)
33            throws IOException, ServletException {
34
35        String username = request.getUserPrincipal().getName();
36        request.setAttribute("username", username);
37
38        // tag::serve[]
39        request
40                .getRequestDispatcher("securedHello.jsp")
41                .forward(request,response);
42        // end::serve[]
43    }
44}

securedHello.jsp

 1<%@ page contentType="text/html;charset=UTF-8"%>
 2<html>
 3<head>
 4    <title>Social Media Login Guide</title>
 5</head>
 6<body>
 7<h1>Social Media Login Guide</h1>
 8<p>Welcome to the social media login guide</p>
 9<p>You are currently authenticated!</p>
10<!-- tag::username[] -->
11<p>Hello, ${username}</p>
12<!-- end::username[] -->
13<!-- tag::logout[] -->
14<form method="post" action="logout">
15    <!-- tag::btnLogout[] -->
16    <button type="submit">Log out</button>
17    <!-- end::btnLogout -->
18</form>
19<!-- end::logout[] -->
20</body>
21</html>

LogoutServlet.java

 1// tag::copyright[]
 2/*******************************************************************************
 3 * Copyright (c) 2020 IBM Corporation and others.
 4 * All rights reserved. This program and the accompanying materials
 5 * are made available under the terms of the Eclipse Public License v1.0
 6 * which accompanies this distribution, and is available at
 7 * http://www.eclipse.org/legal/epl-v10.html
 8 *
 9 * Contributors:
10 *     IBM Corporation - Initial implementation
11 *******************************************************************************/
12// end::copyright[]
13package io.openliberty.guides.sociallogin;
14
15import io.openliberty.guides.sociallogin.logout.ILogout;
16import io.openliberty.guides.sociallogin.logout.LogoutHandler;
17
18import javax.inject.Inject;
19import javax.servlet.ServletException;
20import javax.servlet.annotation.HttpConstraint;
21import javax.servlet.annotation.ServletSecurity;
22import javax.servlet.annotation.WebServlet;
23import javax.servlet.http.HttpServlet;
24import javax.servlet.http.HttpServletRequest;
25import javax.servlet.http.HttpServletResponse;
26import javax.ws.rs.core.Response;
27import java.io.IOException;
28import java.util.Map;
29import java.util.logging.Level;
30import java.util.logging.Logger;
31
32@WebServlet(name = "LogoutServlet", urlPatterns = "/logout")
33@ServletSecurity(value = @HttpConstraint(rolesAllowed = {"users"},
34        transportGuarantee = ServletSecurity.TransportGuarantee.CONFIDENTIAL))
35public class LogoutServlet extends HttpServlet {
36
37    private static final long serialVersionUID = 1L;
38
39    // tag::inject[]
40    @Inject
41    private LogoutHandler logoutHandler;
42    //end::inject[]
43
44    @Override
45    protected void doPost(HttpServletRequest request, HttpServletResponse response)
46            throws IOException, ServletException {
47
48        // tag::getLogout[]
49        ILogout logout = logoutHandler.getLogout();
50        // end::getLogout[]
51        // tag::revoke[]
52        Response logoutResponse = logout.logout();
53        // end::revoke[]
54
55        // tag::errorHandle[]
56        Response.Status.Family responseCodeFamily = logoutResponse
57                .getStatusInfo()
58                .getFamily();
59        if (!responseCodeFamily.equals(Response.Status.Family.SUCCESSFUL)) {
60            Logger.getLogger("LogoutServlet").log(Level.SEVERE,
61                    logoutResponse.readEntity(Map.class).toString());
62            throw new ServletException("Could not delete OAuth2 application grant");
63        }
64        // end::errorHandle[]
65
66        // tag::logout[]
67        request.logout();
68        // end::logout[]
69
70        // tag::redirect[]
71        response.sendRedirect("hello.html");
72        // end::redirect[]
73    }
74}

Implementing logout

The LogoutServlet provided in the start directory uses the logout() method in the HttpServletRequest class to log the user out of the application. After a user is logged out, the user is redirected to the hello.html page.

You are also provided with a LogoutHandler class and ILogout interface to add custom logout logic that revokes the application permissions from a user’s account.

The logoutHandler.getLogout() method gets an implementation of the ILogout interface for GitHub and uses the logout() method of that interface to revoke the OAuth2 permissions that are granted to the application by the user.

The response that’s returned by this logout() method is verified by the application, which checks whether it has a 2xx series status code.

The request.logout() method is then called to log the user out of the application.

The logoutHandler.getLogout() method returns the implementation of the ILogout interface based on the name of the social media provider that a user chooses. The social media provider’s name is retrieved by using the UserProfileManager class. The UserProfileManager class returns the ID of the social media login configuration. In this application, when the application user selects GitHub, the name of the social media provider is githubLogin.

Implement the ILogout interface to revoke permissions for the application from the user’s GitHub account.

Replace this GitHubLogout class.
src/main/java/io/openliberty/guides/sociallogin/logout/GitHubLogout.java

GitHub’s REST API provides a DELETE endpoint, which is stored in the unauthorizeUrl variable. The unauthorizeUrl variable is used to revoke application permissions from the user. The JAX-RS Client is used to send a request to this DELETE endpoint.

Your GitHub application client ID and client secret credentials are obtained from the github.client.id and github.client.secret configuration properties. These credentials are encoded and stored in the encodedAuth variable and added to the request as an Authorization header.

The GitHub access token for a user, which is stored in the accessToken variable, is retrieved from their user profile that is modelled by the UserProfile class. The UserProfile object can be retrieved with the UserProfileManager class. The access token is used in the DELETE request body, which is stored in the requestBody variable.

GitHubLogout.java

 1// tag::copyright[]
 2/*******************************************************************************
 3 * Copyright (c) 2020 IBM Corporation and others.
 4 * All rights reserved. This program and the accompanying materials
 5 * are made available under the terms of the Eclipse Public License v1.0
 6 * which accompanies this distribution, and is available at
 7 * http://www.eclipse.org/legal/epl-v10.html
 8 *
 9 * Contributors:
10 *     IBM Corporation - Initial implementation
11 *******************************************************************************/
12// end::copyright[]
13package io.openliberty.guides.sociallogin.logout;
14
15import com.ibm.websphere.security.social.UserProfileManager;
16import org.eclipse.microprofile.config.inject.ConfigProperty;
17
18import javax.enterprise.context.RequestScoped;
19import javax.inject.Inject;
20import javax.ws.rs.client.ClientBuilder;
21import javax.ws.rs.client.Entity;
22import javax.ws.rs.core.Response;
23import java.nio.charset.StandardCharsets;
24import java.util.Base64;
25import java.util.HashMap;
26import java.util.Map;
27
28@RequestScoped
29public class GitHubLogout implements ILogout {
30
31    @Inject
32    @ConfigProperty(name="github.client.id")
33    private String clientId;
34
35    @Inject
36    @ConfigProperty(name="github.client.secret")
37    private String clientSecret;
38
39    public Response logout() {
40
41        // tag::unauthorizeUrl[]
42        final String unauthorizeUrl = "https://api.github.com/" +
43                "applications/{client_id}/grant";
44        // end::unauthorizeUrl[]
45
46        // tag::accessToken[]
47        String accessToken = UserProfileManager
48                .getUserProfile()
49                .getAccessToken();
50        // end::accessToken[]
51
52        // tag::createAccessTokenBody[]
53        Map<String, String> requestBody = new HashMap<>();
54        requestBody.put("access_token", accessToken);
55        // end::createAccessTokenBody[]
56
57        // tag::encodeAuth[]
58        String auth = clientId + ":" + clientSecret;
59        byte[] encodedAuthStream = Base64
60                .getEncoder()
61                .encode(auth.getBytes(StandardCharsets.ISO_8859_1));
62        String encodedAuth = new String(encodedAuthStream);
63        // end::encodeAuth[]
64
65        // tag::delete[]
66        return ClientBuilder
67                .newClient()
68                .target(unauthorizeUrl)
69                .resolveTemplate("client_id", clientId)
70                .request()
71                // tag::authHeader[]
72                .header("Authorization", "Basic " + encodedAuth)
73                // end::authHeader[]
74                // tag::addBody[]
75                .method("DELETE", Entity.json(requestBody));
76                // end::addBody[]
77        // end::delete[]
78    }
79}

LogoutHandler.java

 1// tag::copyright[]
 2/*******************************************************************************
 3 * Copyright (c) 2020 IBM Corporation and others.
 4 * All rights reserved. This program and the accompanying materials
 5 * are made available under the terms of the Eclipse Public License v1.0
 6 * which accompanies this distribution, and is available at
 7 * http://www.eclipse.org/legal/epl-v10.html
 8 *
 9 * Contributors:
10 *     IBM Corporation - Initial implementation
11 *******************************************************************************/
12// end::copyright[]
13package io.openliberty.guides.sociallogin.logout;
14
15import com.ibm.websphere.security.social.UserProfileManager;
16
17import javax.enterprise.context.RequestScoped;
18import javax.inject.Inject;
19
20@RequestScoped
21public class LogoutHandler {
22
23    // tag::inject[]
24    @Inject
25    private GitHubLogout gitHubLogout;
26    // end::inject[]
27
28    // tag::githubLoginName[]
29    private static final String GITHUB_LOGIN = "githubLogin";
30    // end::githubLoginName[]
31
32    public ILogout getLogout() {
33        // tag::socialMediaName[]
34        String socialMediaName = UserProfileManager.getUserProfile().getSocialMediaName();
35        // end::socialMediaName[]
36        // tag::switch[]
37        switch (socialMediaName) {
38            // tag::handleGithubLogout[]
39            case GITHUB_LOGIN:
40                return gitHubLogout;
41            // end::handleGithubLogout[]
42            default:
43                throw new UnsupportedOperationException("Cannot find the right logout " +
44                        "service for social media name " + socialMediaName);
45        // end::switch[]
46        }
47    }
48}

LogoutServlet.java

 1// tag::copyright[]
 2/*******************************************************************************
 3 * Copyright (c) 2020 IBM Corporation and others.
 4 * All rights reserved. This program and the accompanying materials
 5 * are made available under the terms of the Eclipse Public License v1.0
 6 * which accompanies this distribution, and is available at
 7 * http://www.eclipse.org/legal/epl-v10.html
 8 *
 9 * Contributors:
10 *     IBM Corporation - Initial implementation
11 *******************************************************************************/
12// end::copyright[]
13package io.openliberty.guides.sociallogin;
14
15import io.openliberty.guides.sociallogin.logout.ILogout;
16import io.openliberty.guides.sociallogin.logout.LogoutHandler;
17
18import javax.inject.Inject;
19import javax.servlet.ServletException;
20import javax.servlet.annotation.HttpConstraint;
21import javax.servlet.annotation.ServletSecurity;
22import javax.servlet.annotation.WebServlet;
23import javax.servlet.http.HttpServlet;
24import javax.servlet.http.HttpServletRequest;
25import javax.servlet.http.HttpServletResponse;
26import javax.ws.rs.core.Response;
27import java.io.IOException;
28import java.util.Map;
29import java.util.logging.Level;
30import java.util.logging.Logger;
31
32@WebServlet(name = "LogoutServlet", urlPatterns = "/logout")
33@ServletSecurity(value = @HttpConstraint(rolesAllowed = {"users"},
34        transportGuarantee = ServletSecurity.TransportGuarantee.CONFIDENTIAL))
35public class LogoutServlet extends HttpServlet {
36
37    private static final long serialVersionUID = 1L;
38
39    // tag::inject[]
40    @Inject
41    private LogoutHandler logoutHandler;
42    //end::inject[]
43
44    @Override
45    protected void doPost(HttpServletRequest request, HttpServletResponse response)
46            throws IOException, ServletException {
47
48        // tag::getLogout[]
49        ILogout logout = logoutHandler.getLogout();
50        // end::getLogout[]
51        // tag::revoke[]
52        Response logoutResponse = logout.logout();
53        // end::revoke[]
54
55        // tag::errorHandle[]
56        Response.Status.Family responseCodeFamily = logoutResponse
57                .getStatusInfo()
58                .getFamily();
59        if (!responseCodeFamily.equals(Response.Status.Family.SUCCESSFUL)) {
60            Logger.getLogger("LogoutServlet").log(Level.SEVERE,
61                    logoutResponse.readEntity(Map.class).toString());
62            throw new ServletException("Could not delete OAuth2 application grant");
63        }
64        // end::errorHandle[]
65
66        // tag::logout[]
67        request.logout();
68        // end::logout[]
69
70        // tag::redirect[]
71        response.sendRedirect("hello.html");
72        // end::redirect[]
73    }
74}

ILogout.java

 1// tag::copyright[]
 2/*******************************************************************************
 3 * Copyright (c) 2020 IBM Corporation and others.
 4 * All rights reserved. This program and the accompanying materials
 5 * are made available under the terms of the Eclipse Public License v1.0
 6 * which accompanies this distribution, and is available at
 7 * http://www.eclipse.org/legal/epl-v10.html
 8 *
 9 * Contributors:
10 *     IBM Corporation - Initial implementation
11 *******************************************************************************/
12// end::copyright[]
13package io.openliberty.guides.sociallogin.logout;
14
15import javax.ws.rs.core.Response;
16
17public interface ILogout {
18    Response logout();
19}

Check out the service that you created by going to the http://localhost:9080/api/hello.html URL.

Try logging in with your GitHub account. After authenticating, you are redirected to the https://localhost:9080/api/hello URL. When you click the Log out button, you are unauthenticated and redirected back to the http://localhost:9080/api/hello.html URL.

If you try logging in again, you are asked to reauthorize your application with GitHub. Then, you’re authenticated and redirected to the https://localhost:9080/api/hello URL. While you stay logged in to GitHub, the application doesn’t prompt you to enter your GitHub credentials.

When you are done checking out the service, exit dev mode by pressing CTRL+C in the command-line session where you ran the server, or by typing q and then pressing the enter/return key.

Next steps

As an exercise, configure Facebook as a second social media provider for social media login for your application. If you add more than one social media login provider, a selection form is presented to the user. This form contains all of the social media providers that are configured in your application.

For information about setting up an OAuth2 application and revoking permissions, see the Facebook documentation:

Great work! You’re done!

You secured a web application in Open Liberty by using the Social Media Login feature.

Guide Attribution

Authenticating users through social media providers by Open Liberty is licensed under CC BY-ND 4.0

Copied to clipboard
Copy code block
Copy file contents

Prerequisites:

Nice work! Where to next?

What did you think of this guide?

Extreme Dislike Dislike Like Extreme Like

What could make this guide better?

Raise an issue to share feedback

Create a pull request to contribute to this guide

Need help?

Ask a question on Stack Overflow

Like Open Liberty? Star our repo on GitHub.

Star