Authenticating users through social media providers

duration 30 minutes

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>

Additional prerequisites

Before you begin, you must have a GitHub account to complete this guide. Register for a GitHub account, if you don’t already have one.

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.

Before you begin, make sure you have all the necessary prerequisites.

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 dev mode, dev mode 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:

cd start
mvn liberty:dev

After you see the following message, your Liberty instance is ready in dev mode:

**************************************************************
*    Liberty is running in dev mode.

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 Liberty server.xml configuration file.

Replace the Liberty server.xml configuration 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.

For more information, see the githubLogin element documentation.

server.xml

 1<server description="Social Login Guide Server">
 2    <!-- tag::featureManager[] -->
 3    <featureManager>
 4        <feature>pages-3.1</feature>
 5        <feature>appSecurity-5.0</feature>
 6        <feature>transportSecurity-1.0</feature>
 7        <feature>mpConfig-3.1</feature>
 8        <feature>restfulWSClient-3.1</feature>
 9        <feature>cdi-4.0</feature>
10        <feature>jsonb-3.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="${http.port}"
19                  httpsPort="${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 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, 2022 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 2.0
 6 * which accompanies this distribution, and is available at
 7 * http://www.eclipse.org/legal/epl-2.0/
 8 *
 9 * SPDX-License-Identifier: EPL-2.0
10 *******************************************************************************/
11// end::copyright[]
12package io.openliberty.guides.sociallogin;
13
14import jakarta.servlet.ServletException;
15import jakarta.servlet.annotation.HttpConstraint;
16import jakarta.servlet.annotation.ServletSecurity;
17import jakarta.servlet.annotation.WebServlet;
18import jakarta.servlet.http.HttpServlet;
19import jakarta.servlet.http.HttpServletRequest;
20import jakarta.servlet.http.HttpServletResponse;
21import java.io.IOException;
22
23@WebServlet(name = "HelloServlet", urlPatterns = "/hello")
24@ServletSecurity(value = @HttpConstraint(rolesAllowed = {"users"},
25        transportGuarantee = ServletSecurity.TransportGuarantee.CONFIDENTIAL))
26public class HelloServlet extends HttpServlet {
27
28    private static final long serialVersionUID = 1L;
29
30    @Override
31    protected void doGet(HttpServletRequest request, HttpServletResponse response)
32            throws IOException, ServletException {
33
34        String username = request.getUserPrincipal().getName();
35        request.setAttribute("username", username);
36
37        // tag::serve[]
38        request
39                .getRequestDispatcher("securedHello.jsp")
40                .forward(request, response);
41        // end::serve[]
42    }
43}

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

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

LogoutHandler.java

 1// tag::copyright[]
 2/*******************************************************************************
 3 * Copyright (c) 2020, 2022 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 2.0
 6 * which accompanies this distribution, and is available at
 7 * http://www.eclipse.org/legal/epl-2.0/
 8 *
 9 * SPDX-License-Identifier: EPL-2.0
10 *******************************************************************************/
11// end::copyright[]
12package io.openliberty.guides.sociallogin.logout;
13
14import com.ibm.websphere.security.social.UserProfileManager;
15
16import jakarta.enterprise.context.RequestScoped;
17import jakarta.inject.Inject;
18
19@RequestScoped
20public class LogoutHandler {
21
22    // tag::inject[]
23    @Inject
24    private GitHubLogout gitHubLogout;
25    // end::inject[]
26
27    // tag::githubLoginName[]
28    private static final String GITHUB_LOGIN = "githubLogin";
29    // end::githubLoginName[]
30
31    public ILogout getLogout() {
32        // tag::socialMediaName[]
33        String socialMediaName = UserProfileManager.getUserProfile()
34                                                   .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, 2022 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 2.0
 6 * which accompanies this distribution, and is available at
 7 * http://www.eclipse.org/legal/epl-2.0/
 8 *
 9 * SPDX-License-Identifier: EPL-2.0
10 *******************************************************************************/
11// end::copyright[]
12package io.openliberty.guides.sociallogin;
13
14import io.openliberty.guides.sociallogin.logout.ILogout;
15import io.openliberty.guides.sociallogin.logout.LogoutHandler;
16
17import jakarta.inject.Inject;
18import jakarta.servlet.ServletException;
19import jakarta.servlet.annotation.HttpConstraint;
20import jakarta.servlet.annotation.ServletSecurity;
21import jakarta.servlet.annotation.WebServlet;
22import jakarta.servlet.http.HttpServlet;
23import jakarta.servlet.http.HttpServletRequest;
24import jakarta.servlet.http.HttpServletResponse;
25import jakarta.ws.rs.core.Response;
26import java.io.IOException;
27import java.util.Map;
28import java.util.logging.Level;
29import java.util.logging.Logger;
30
31@WebServlet(name = "LogoutServlet", urlPatterns = "/logout")
32@ServletSecurity(value = @HttpConstraint(rolesAllowed = {"users"},
33        transportGuarantee = ServletSecurity.TransportGuarantee.CONFIDENTIAL))
34public class LogoutServlet extends HttpServlet {
35
36    private static final long serialVersionUID = 1L;
37
38    // tag::inject[]
39    @Inject
40    private LogoutHandler logoutHandler;
41    //end::inject[]
42
43    @Override
44    protected void doPost(HttpServletRequest request, HttpServletResponse response)
45            throws IOException, ServletException {
46
47        // tag::getLogout[]
48        ILogout logout = logoutHandler.getLogout();
49        // end::getLogout[]
50        // tag::revoke[]
51        Response logoutResponse = logout.logout();
52        // end::revoke[]
53
54        // tag::errorHandle[]
55        Response.Status.Family responseCodeFamily = logoutResponse
56                .getStatusInfo()
57                .getFamily();
58        if (!responseCodeFamily.equals(Response.Status.Family.SUCCESSFUL)) {
59            Logger.getLogger("LogoutServlet").log(Level.SEVERE,
60                    logoutResponse.readEntity(Map.class).toString());
61            throw new ServletException("Could not delete OAuth2 application grant");
62        }
63        // end::errorHandle[]
64
65        // tag::logout[]
66        request.logout();
67        // end::logout[]
68
69        // tag::redirect[]
70        response.sendRedirect("hello.html");
71        // end::redirect[]
72    }
73}

ILogout.java

 1// tag::copyright[]
 2/*******************************************************************************
 3 * Copyright (c) 2020, 2022 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 2.0
 6 * which accompanies this distribution, and is available at
 7 * http://www.eclipse.org/legal/epl-2.0/
 8 *
 9 * SPDX-License-Identifier: EPL-2.0
10 *******************************************************************************/
11// end::copyright[]
12package io.openliberty.guides.sociallogin.logout;
13
14import jakarta.ws.rs.core.Response;
15
16public interface ILogout {
17    Response logout();
18}

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 Liberty.

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.

Learn more about MicroProfile.

Guide Attribution

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

Copy file contents
Copied to clipboard

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