This chapter covers the following recipes:
- Preparing the environment
- Reading the user's contacts from Facebook on the client side
- Reading the user's contacts from Facebook on the server side
- Accessing OAuth 2.0 LinkedIn protected resources
- Accessing OAuth 2.0 Google protected resources bound to the user's session
The main purpose of this chapter is to help you integrate with popular web applications and social media, although at the same time allow you to get familiarized with the foundational principles of OAuth 2.0 specification.
Before diving into the recipes for several use cases, let's look at the big picture of the most scenarios which will be covered. This will give you the opportunity to review some important concepts about OAuth 2.0 specification so we can stay on the same page with the terminologies used throughout the book.
The preceding diagram shows the four main components of the OAuth 2.0 specification:
- Resource Owner
- Authorization Server
- Resource Server
- Client
Just to review the purpose of these components, remember that the Resource Owner is the user which delegates authority for third-party applications to use resources on its behalf. The third-party application mentioned is represented by the client which I depicted as Mobile client and Web Client. The user's resources are usually maintained and protected by the Resource Server which might be implemented together with the Authorization Server as a single component, for example. The composition of the Authorization Server and Resource Server are referred to as the OAuth 2.0 Provider to simplify the terminology given to the application which is protected by OAuth 2.0.
As most examples are written in Java, we will also need an Integrated Development Environment (IDE) and a good framework to help us write simple web applications (as the OAuth 2.0 protocol was designed for HTTP usage), which will be Spring. To simplify the usage of Spring related technologies, this recipe will help you prepare an application using Spring Boot, providing an example endpoint and how to run this project using Maven.
As I previously mentioned, we will run most of the recipes using the Spring Boot Framework which eases the development of applications based on the Spring Framework. So to run this recipe, you just need an environment where you can download some files from the internet, Java 8 properly configured on your machine, and the CURL
tool.
Note
CURL
is a tool which allows you to run HTTP requests through the command line. It is available by default in Linux and Mac OS environments, so if you are running the recipes on Windows you should install it first. This tool can be downloaded fromhttps://curl.haxx.se/download.htmland to install it, you just have to unpack it and add the path for binaries to the PATH environment variable of Windows.
The following steps describe how to prepare the environment and show how to generate a simple project from the Spring Initializr website which will be executed using the appropriate Maven commands:
- Generate a project using Spring Initializr service by visiting https://start.spring.io/. Spring Initializr provides lots of options to start setting up your project, such as if you want to use Maven or Gradle to manage your project dependencies, which version of Spring Boot to use, which dependencies to use, and even changing the language from Java to Groovy or Kotlin.
- For this simple test, just use the default values for the project manager,
Maven Project
, withJava
language and version1.5.7
of the Spring Boot. - At
Project Metadata
, change the value of the fieldGroup
tocom.packt.example
. - Still on
Project Metadata
, change the name of theArtifact
tosimplemvc
. - In the
Dependencies
section, typeweb
and selectFull-stack web development with Tomcat and Spring MVC
. After selecting the right choice, you will see the tagWeb
underneathSelected Dependencies
as follows:
- After setting up all the requirements for this simple example, click on the
Generate Project
button and your browser will start downloading the ZIP file into yourDownloads
folder. - After downloading this file, you can unzip it and import it to your IDE just to explore the structure of the created project. For Eclipse users, just import the project as a Maven project.
- Open the class
SimplemvcApplication
and you would see the following code in your IDE:
@SpringBootApplication public class SimplemvcApplication { public static void main(String[] args) { SpringApplication.run(SimplemvcApplication.class, args); } }
- Let's turn the class
SimplemvcApplication
into a controller by adding the annotation@Controller
as presented in the following code:
@Controller @SpringBootApplication public class SimplemvcApplication { public static void main(String[] args) { SpringApplication.run(SimplemvcApplication.class, args); } }
- Now that our class is declared as a controller, we can define an endpoint so we can see if the project is running properly. Add the method
getMessage
as follows, within the classSimplemvcApplication
:
@GetMapping("/message") public ResponseEntity<String> getMessage() { return ResponseEntity.ok("Hello!"); }
- If you want to run your project inside the Eclipse IDE, you should just run the class
SimplemvcApplication
as a Java application by right-clicking at the class and selecting the menu optionRun As |
Java Application.
- After the application is started you should see something like the following message at the end of the output presented in your console:
Started SimplemvcApplication in 13.558 seconds (JVM running for 14.011)
- Execute the following command to know if your application works properly (just check if the output prints
Hello
):
curl "http://localhost:8080/message"
- If you would like to use the command line you can also start your application by running the following Maven command (to run the application with Maven through the command line, you must install Maven, as explained in the next sections):
mvn spring-boot:run
- If you don't have Maven installed on your machine, the first thing to do is to start downloading the latest version from https://maven.apache.org/download.cgi which at the time of this writing was
apache-maven-3.5.0-bin.tar.gz
. - After the file has downloaded, just unpack it into any folder you want and start running Maven commands.
- Copy the full path of the Maven directory, which was created when you unpacked the downloaded file from the Maven website. If you are running macOS or Linux, run
pwd
at the command line to discover the full path. - After that, you must add the path for Maven's directory to the
PATH
environment variable. If you are using Linux or macOS, create the variableMVN_HOME
within the.bash_profile
file and append the content ofMVN_HOME
to the end of thePATH
environment variable, as presented in the following code:
MVN_HOME=/Users/{your_user_name}/maven-3.5.0 export PATH=$PATH:$MVN_HOME/bin
Note
The file.bash_profile
should be found at the user's directory. So, to edit this file, you should open the file/Users/{your_user_name}/.bash_profile
, or in a shorter way, by using~/.bash_profile
.If you are using Windows, all the environment variables can be edited through the visual interface.
- After editing this file, run the command
source ~/.bash_profile
to reload all the contents. - To check if Maven is perfectly running on your environment, run the following command:
mvn --version.
- The OAuth 2.0 specification is available as RFC 6749 at https://tools.ietf.org/html/rfc6749
- You can read more about Spring Boot at https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/
Because of the usage of Spring Boot we can take advantage of projects like Spring MVC and Spring Security. These Spring projects help us to write web applications, REST APIs, and help us to secure our applications. By using the Spring Security OAuth2 project, for example, we can configure our own OAuth 2.0 Providers in addition, to act like clients. This is important because someone trying to write his own OAuth Provider will have to deal with too many details which could easily lead to an insecure OAuth Provider. Spring Security OAuth2 already addresses the main concerns any developer would have to think about.
In addition, Spring Boot eases the initial steps for the bootstrap of the application. When creating a Spring project without Spring Boot we need to deal with dependencies manually by taking care of possible library conflicts. To solve this problem, Spring Boot has some pre-configured modules provided by starters. As an example of a useful starter, let's consider an application with Spring Data JPA. Instead of declaring all the dependencies for hibernate
, entity-manager
, and transaction-api
, just by declaring spring-boot-starter-data-jpa
all the dependencies will be imported automatically.
While starting using Spring Boot, things can still become easier by using the Spring Initializr service provided by Pivotal (the Spring maintainer now).
All the examples presented in Java can be imported and executed on any Java IDE, but we will use Eclipse just because it is a large, accepted tool among developers around the world. Although this book presents recipes using Eclipse, you can also stick with your preferred tool if you want.
Nowadays, many projects have been designed using Gradle, but many developers are still used to creating their projects using Maven to manage dependencies and the project itself. So, to avoid trick bugs with IDE plugins or any other kind of issue, the recipes using Spring Boot will be managed by Maven. In addition, Eclipse IDE already comes with a Maven plugin which at the time of writing this book was not true for Gradle. To run projects with Gradle in Eclipse, you must install a specific plugin.
Spring Boot provides a lot of starters to help you develop applications using a plethora of tools and libraries. If you want to search for more just go to http://docs.spring.io/spring-boot/docs/1.5.7.RELEASE/reference/htmlsingle/#using-boot-starter.
This recipe will present you with how you can integrate with Facebook using the Implicit grant type which is the better choice for public clients and runs directly on the user's web browser.
Note
Grant types as you may already know, defines different methods for an application to retrieve access tokens from an Authorization Server. A grant type may apply for a given scenario regarding the client type being developed. Just as a reminder, OAuth 2.0 specification defines two types of client types: public and confidential.
To run this recipe, you must create a web application using Spring Boot, which will help the development of the application. In addition, we also need to register our application on Facebook. That's one important step when using OAuth 2.0, because as an OAuth Provider, Facebook needs to know which clients are asking for access token and, of course, the Resource Owner (the user) would want to know who is to be granted access to her profile.
Follow these steps to create a client application to integrate with Facebook using client-side flow from OAuth 2.0:
- First of all, remember that you must have a Facebook account, and have to register a new application. Go to https://developers.facebook.com/apps/ and you should see something like this:
- Click on
Create a New App
to start registering your application, and you should see the following interface which allows you to define the name of the application:
- Click on
Create App ID
and you will be redirected to the newly created application's dashboard as follows:
- To start using the Facebook's Graph API and retrieve the user's contacts, we first need to select one product from several provided by Facebook. For what we need now, you must click on the
Set Up
button from theFacebook Login
box. - After clicking on
Set Up
, you must choose one platform, which must beWeb
for this recipe. - After choosing the
Web platform
, enter theSite URL
for your application. I am using a fictitious URL namedhttp://clientimplicit.test
. - After saving the URL of your site, just click on
Continue
. - Now you are ready to set up the redirection URI for the application by clicking on
Settings
in the left panel, as follows. As this application isn't running for production, I have set up the redirect URI ashttp://localhost:8080/callback
. Don't forget to save the changes:
- Now you can click on
Dashboard
at the left side of the panel so you can grab theApp ID
andApp Secret
, which maps to theclient_id
andclient_secret
from OAuth 2.0 specifications respectively. - Copy the
App ID
andApp Secret
from the dashboard as represented by the following screenshot as follows, so we can use in the code which comes in the next steps:
- Once we have our client registered on Facebook, we are ready to start writing code to retrieve the OAuth 2.0 access token using the Implicit grant type (requesting the access token from the client side).
- Create a new web application using Spring Initializr at https://start.spring.io/ and define the following data:
- Set up the group as
com.packt.example
- Define the artifact as
client-implicit
- Add
Web
andThymeleaf
as dependencies for this project
- Set up the group as
- Create the file
client.html
within the foldertemplates
which resides inside thesrc/main/resources
project's directory.
- Add the following content within the file
client.html
:
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head><title>Facebook - client side integration</title></head> <body> Press the following button to start the implicit flow. <button id="authorize" type="button">Authorize</button> <div id="box"></div> </body> </html>
- The button we have added to the
client.html
page does not have any behavior attached. So, to allow the user to start the Implicit grant type flow, add the following JavaScript code after the body tag:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script> <script type="text/javascript" th:inline="javascript"> /*<![CDATA[*/ $(document).ready(function() { $("#authorize").click(makeRequest); }); function makeRequest() { var auth_endpoint = "https://www.facebook.com/v2.9/dialog/oauth", response_type = "token", client_id = "1948923582021549", redirect_uri = "http://localhost:8080/callback", scope = "public_profile user_friends"; var request_endpoint = auth_endpoint + "?" + "response_type=" + response_type + "&" + "client_id=" + client_id + "&" + "redirect_uri=" + encodeURI(redirect_uri) + "&" + "scope=" + encodeURI(scope); window.location.href = request_endpoint; } /*]]>*/ </script>
- Before starting the application, we need to map a URL pattern so the HTML code we wrote before can be rendered. To do so, open the class
ClientImplicitApplication.java
and assure your code looks like the following:
@Controller @SpringBootApplication public class ClientImplicitApplication { public static void main(String[] args) { SpringApplication.run(ClientImplicitApplication.class, args); } @GetMapping("/") public String client() { return "client"; } }
- In the previous code, we've mapped the root of our application to the
client.html
web page. But all of this is now serving the purpose of sending to the user the Authorization Server (in this case Facebook) so she could grant our application access to protected resources (which are her friends). Try to start the application and go tohttp://localhost:8080/
. - Click on the ;
Authorize
button that will be provided byclient.html
to start the Implicit grant flow, log in with your Facebook account, and accept the permissions requested by theclient-implicit
application (make sure that jquery is properly declared insideclient.html
file). - If you grant all the permissions at the consent user page you shall be redirected to
http://localhost:8080/callback
URL that was specified at the client registration phase on Facebook. Click onContinue
and pay attention to the content received and the URL fragment in the browser's address bar. It should be something like the following:
http://localhost:8080/callback#access_token=EAAbsiSHMZC60BANUwKBDCYeySZCjcBpvFuUO1gXsfTGwWjnZAFTAZBIJB62jdUroAcNuZAVWO24yeqo0iazWYytVgrQ1bgNWI8vm07Ws4ZCHXpGridHfZB6PQ1rzM4BzP29IljgTTuBLZBFQBEnEn2LJiOWJjA8J6Y73BLcjIe2vVMZB9c2GnZBpiK4iZAWEtkTsMEZD&expires_in=7152
- Now we need to extract both the
access_token
and theexpires_in
parameters which comes after#character
, and start using the Facebook Graph API to retrieve the user's friends.
- The first thing we can do is to create another URL mapping through our default controller, which is
ClientImplicitApplication
. Open this class and add the following method so we can deal with Facebook's redirection:
@GetMapping("/callback") public String callback() { return "callback_page"; }
- As we can see, the method
callback
is returning thecallback_page
string, which will automatically be mapped to the filecallback_page.html
. So, let's create this file inside the templates folder which resides in thesrc/main/resources
project directory. At first just add the following HTML content to thecallback_page.html
file:
<!DOCTYPE html> <html> <head><title>Insert title here</title></head> <body> Friends who has also granted client-implicit <div id="friends"> <ul></ul> </div> </body> </html>
- After receiving the
acces_token
as a URL fragment, this file will use JavaScript code to interact with Facebook's graph API to retrieve the user's friends, and will populate the tag<ul>
with each friend received within the respective<li>
tag. Let's start writing our JavaScript code by adding the following content after the body tag:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script> <script type="text/javascript"> /*<![CDATA[*/ $(document).ready(function() { var fragment = window.location.hash; }); /*]]>*/ </script>
- As we have the fragment content in the fragment variable, add the following function at the end of the JavaScript code:
function getResponse(fragment) { var attributes = fragment.slice(1).split('&'); var response = {}; $(attributes).each(function(idx, attr) { var keyValue = attr.split('='); response[keyValue[0]] = keyValue[1]; }); response.hasError = function() { return !response['access_token']; }; return response; }
- The code presented before creates an object named
response
which might contain anaccess_token
or error description. Unfortunately, Facebook returns all the error data as URL query parameters instead of using the fragment as per OAuth 2.0's specifications. At least the object returned by thegetResponse
function can tell if the response has an error. - Now let's update the main JavaScript code to the following. The following code extracts the response from the URL fragment, clears the fragment of the URL as a security measure, and in case of an error, just presents a message to the user through a
<div>
HTML tag:
$(document).ready(function() { var fragment = window.location.hash; var res = getResponse(fragment); window.location.hash = '_#'; if (res.hasError()) { $("<div>Error trying to obtain user's authorization!</div>").insertBefore('#friends'); return; } });
- Now let's create the most expected function, which is responsible for using the
access_token
to interact with the Facebook Graph API. Add the following function at the end of the JavaScript code:
function getFriends(accessToken, callback) { var baseUrl = 'https://graph.facebook.com/v2.9/'; var endpoint = 'me/friends'; var url = baseUrl + endpoint; $.ajax({ url: url, beforeSend: function(xhr) { xhr.setRequestHeader("Authorization", "Bearer " + accessToken); }, success: function(result){ var friends = result.data; callback(friends); }, error: function(jqXHR, textStatus, errorThrown) { console.log(textStatus); } }); }
- And to finish, just update the main JavaScript code which is using all the declared functions as follows:
$(document).ready(function() { var fragment = window.location.hash; var res = getResponse(fragment); window.location.hash = '_#'; if (res.hasError()) { $("<div>Error trying to obtain user's authorization!</div>").insertBefore('#friends'); return; } getFriends(res['access_token'], function(friends) { $(friends).each(function(index, friend) { $('#friends').find('ul').append('<li>' + friend.name + '</li>'); }); }); });
- Now it's time to run the
client-implicit
application to see the usage of OAuth 2.0 and the Facebook Graph API in practice.
- Start the application.
- Go to
http://localhost:8080/
and click on theAuthorize
button. - Grant the requested permissions.
- When you are redirected back to
client-implicit
, you should see something like the following in your web browser:
- As you might notice, your application might not retrieve any users yet. That's because Facebook just allows you to present friends who also authorized your application. In our case, another user should be the
client-implicit
user and you have to register her as a tester for your application.
To start using OAuth 2.0's protected resources, before requesting the access_token
through the user's grant, we registered the application client-implicit
through the OAuth 2.0 Provider (Facebook). The responsibility of maintaining the client's data belongs to the Authorization Server. In using Facebook the boundaries between the Authorization Server and the Resource Server is not so clear. The most important thing to understand here is that Facebook is acting as an OAuth 2.0 Provider.
As per the specifications, we have performed the three important steps in client registration process, which was to choose the client type, register the redirection URI, and enter the application's information.
By registering the application, we've received the client_id
and client_secret
, but as we are using the Implicit grant flow the client_secret
won't be needed. That's because this first recipe presents an application which runs directly on the web browser. So, there is no way to safely protect the client_secret
. When not using the client_secret
we must try not to expose the received access_token
that comes with the URL fragment after the user grants permission to access her resources. Another measure to help the application to not expose the access token was to clear the URL fragment.
Another measure that might be applied is to not use any external JavaScript code as such that was used to send usage metrics (in such a way that the access_token
could be sent to the external service).
After the registration process we got into the code to effectively interact with the Facebook Graph API, which at the time of this writing was version 2.9. Facebook offers two ways to log a user with a valid account:
- By using the Facebook SDK
- By manually building a login flow
To make the OAuth 2.0 usage explicit, this recipe was written by manually building a login flow. So to start the process of user's authentication and authorization, the client-implicit
application builds the URL for the Authorization Server manually as follows:
var request_endpoint = auth_endpoint + "?" + "response_type=" + response_type + "&" + "client_id=" + client_id + "&" + "redirect_uri=" + encodeURI(redirect_uri) + "&" + "scope=" + encodeURI(scope); window.location.href = request_endpoint;
After simply redirecting the user to the Authorization Server's endpoint, the flow is transferred to Facebook, which authenticates the user if needed, and the user authorizes whether or not the client application can make use of its resources. Once the user authorizes the client, she is redirected back to the registered redirection URI, which in our case was http://localhost:8080/callback
.
When receiving the access_token
all we need to do is extract the token from the URL fragment and start using the Facebook Graph API.
As an exercise, you might try to use Facebook SDK, which should be simpler to use for abstracting what we did into the SDK's API. Besides, using the SDK or not, one important thing that should be added to our code is the usage of the state parameter to avoid Cross Site Request Forgery (CSRF) attacks.
Note
A CSRF attack allows a malicious user to execute operations in the name of another user (a victim). Regarding web applications, a valid approach to avoid CSRF is to make the client send a variable to the server with some random string which might be checked after receiving it back from the server's response, so the first value and the second (received) must be the same.
Regarding security issues, one other valuable suggestion is to send the access_token
to the server side so you don't have to request a new access token on every web page of your application (but take care with the expiration time).
Note
All the recipes that follow in this chapter will be using Spring Social project. For more information about the project, you can read the official documents at https://projects.spring.io/spring-social/.
Now you are perfectly familiarized with the Facebook login process and Graph API usage. But to allow for a safer approach to get user authorization to retrieve contacts (or friends) from Facebook, this chapter presents how to use the server side approach which maps directly to the Authorization Code grant type from the OAuth 2.0 specifications.
For this recipe, we need to create a simple web application in the same way we did for client-implicit
. As we will develop an application which interacts with Facebook at the server side, we are supposed to write a lot of code. But instead of writing too much code, let's use the Spring Social Facebook project.
There is an important step to perform, similar to what we did for client-implicit
; as the application is a Facebook client we need to register a new application.
Follow the steps below to create a client application to integrate with Facebook using the server-side flow from OAuth 2.0:
- Go to https://developers.facebook.com/apps/ and add a new application by clicking on
Add a New App
. - Register a new client application on Facebook with the
Display Name
social-authcode
.
- You will be guided to select one Facebook product. So, choose
Facebook Login
by clicking onSet Up
and then chooseWeb
as a platform. - You will be asked to enter the site URL, which might be
http://socialauthcode.test/
. - After creating the application on Facebook, click on
Facebook Login
on the left panel to configure a valid redirect URI, which should behttp://localhost:8080/connect/facebook
. - Click on
Dashboard
on the left panel so you can retrieve the App ID and App Secret which map toclient_id
andclient_secret
, as you may already know, and grab the credentials to use later when implementing the client application. - Now let's create the initial project using Spring Initializr, as we did for other recipes in this book. Go to https://start.spring.io/ and define the following data:
- Set up the
Group
ascom.packt.example
- Define the
Artifact
associal-authcode
- Add
Web
andThymeleaf
as the dependencies for this project
- Set up the
- Import the project to your IDE. When using Eclipse, just import it as a Maven project.
- Now add the following dependencies into the
pom.xml
file to add support for Spring Social Facebook:
<dependency> <groupId>org.springframework.social</groupId> <artifactId>spring-social-config</artifactId> </dependency> <dependency> <groupId>org.springframework.social</groupId> <artifactId>spring-social-core</artifactId> </dependency> <dependency> <groupId>org.springframework.social</groupId> <artifactId>spring-social-web</artifactId> </dependency> <dependency> <groupId>org.springframework.social</groupId> <artifactId>spring-social-facebook</artifactId> </dependency>
- Create an HTML file named
friends.html
inside the templates directory located withinsrc/main/resources
, as follows:
- Open the file
friends.html
and add the following content:
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <title>Friends</title> </head> <body> <h3>Hello, <span th:text="${facebookProfile.name}">User</span>!</h3> <h4>Your friends which also allowed social-authcode:</h4> <div th:each="friend:${friends}"> <b th:text="${friend.id}">[id]</b> - <b th:text="${friend.name}">[name]</b> <hr/> </div> </body> </html>
- Now, we need URL mapping so the previous HTML file can be rendered. To do this, create a new Java class named
FriendsController.java
inside the packagecom.packt.example.socialauthcode
, with the following content:
@Controller @RequestMapping("/") public class FriendsController { @GetMapping public String friends(Model model) { return "friends"; } }
- As you may realize, the template
friends.html
relies on an object namedfacebookProfile
and another namedfriends
. The objectfacebookProfile
must have the name attribute and thefriends
object must be a list of objects which haveid
andname
properties. The good news is that we don't have to declare classes for these objects because Spring Social already provides them. We just need to have a valid user connection to start using these objects, so add the following attributes inside theFriendsController
class:
@Autowired private Facebook facebook; @Autowired private ConnectionRepository connectionRepository;
- With the class
ConnectionRepository
we can save or retrieve user connections with any provider (only Facebook matters now). Let's take advantage of this class to know if there is any user connected with Facebook and if not, we must redirect the user so she can authorize thesocial-authcode
of the application to retrieve protected resources (her friends). Replace the code fromfriends
method of theFriendsController
class with the code presented in the following code:
@GetMapping public String friends(Model model) { if (connectionRepository.findPrimaryConnection(Facebook.class) == null) { return "redirect:/connect/facebook"; } return "friends"; }
- Now, add the following source code after the if block, that checks for a connection. This new block of code will be executed when there is a user connected to Facebook (when importing
User
andReference
classes, make sure to import fromorg.springframework.social.facebook.api
package):
String [] fields = { "id", "email", "name" }; User userProfile = facebook.fetchObject("me", User.class, fields); model.addAttribute("facebookProfile", userProfile); PagedList<Reference> friends = facebook.friendOperations().getFriends(); model.addAttribute("friends", friends);
- Although this short method executes all that's needed to retrieve the user's profile and contacts using the Authorization Code grant type, you must create some configuration classes. To better group the configuration classes, create a new package called
facebook
insidecom.packt.example.socialauthcode
, which will accommodate the following classes:
- Create the class
EnhancedFacebookProperties
, as presented in the following code, inside the inner packagefacebook
, so we can configure the application properties asclient_id
andclient_secret
(don't forget to create the respective getters and setters for each attribute):
@Component @ConfigurationProperties(prefix = "facebook") public class EnhancedFacebookProperties { private String appId; private String appSecret; private String apiVersion; // getters and setters omitted for brevity }
- Before continuing creating the other classes, you must configure the
appSecret
andapiVersion
values so the applicationsocial-authcode
is able to request anaccess_token
. As you may realize, the classEnhancedFacebookProperties
is annotated with@ConfigurationProperties
which allows for defining the properties inside theapplication.properties
file, as follows:
facebook.app-id=1948923582021549 facebook.app-secret=1b4b0f882b185094a903e76a661c7c7c facebook.api-version=2.9
- Now create the class
CustomFacebookServiceProvider
, as follows. This class is responsible for creating a custom instance ofOAuth2Template
allowing us to effectively configure the Facebook API version which at the time of this writing was 2.9:
public class CustomFacebookServiceProvider extends AbstractOAuth2ServiceProvider<Facebook> { private String appNamespace; private String apiVersion; public CustomFacebookServiceProvider( String appId, String appSecret, String apiVersion) { super(getOAuth2Template(appId, appSecret, apiVersion)); this.apiVersion = apiVersion; } private static OAuth2Template getOAuth2Template( String appId, String appSecret, String apiVersion) { String graphApiURL = "https://graph.facebook.com/v" + apiVersion + "/"; OAuth2Template template = new OAuth2Template( appId, appSecret, "https://www.facebook.com/v" + apiVersion + "/dialog/oauth", graphApiURL + "oauth/access_token"); template.setUseParametersForClientAuthentication(true); return template; } @Override public Facebook getApi(String accessToken) { FacebookTemplate template = new FacebookTemplate( accessToken, appNamespace); template.setApiVersion(apiVersion); return template; } }
- So that the
CustomFacebookServiceProvider
can be properly created, create the classCustomFacebookConnectionFactory
as presented in the following code:
public class CustomFacebookConnectionFactory extends OAuth2ConnectionFactory<Facebook> { public CustomFacebookConnectionFactory(String appId, String appSecret, String apiVersion) { super("facebook", new CustomFacebookServiceProvider(appId, appSecret, apiVersion), new FacebookAdapter()); } }
- And finally create the class
FacebookConfiguration
with the following content:
@Configuration @EnableSocial @EnableConfigurationProperties(FacebookProperties.class) public class FacebookConfiguration extends SocialAutoConfigurerAdapter { @Autowired private EnhancedFacebookProperties properties; @Override protected ConnectionFactory<?> createConnectionFactory() { return new CustomFacebookConnectionFactory(this.properties.getAppId(), this.properties.getAppSecret(), this.properties.getApiVersion()); } }
- If you look at the content of
FriendsController
, you should see that this class is using an instance ofFacebook
which provides the API to interact with Facebook Graph API. The instance ofFacebook
must be created through a Spring bean declared as follows inside theFacebookConfiguration
(When importing theConnection
class, make sure you import fromorg.springframework.social.connect
package):
@Bean @ConditionalOnMissingBean(Facebook.class) @Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES) public Facebook facebook(ConnectionRepository repository) { Connection<Facebook> connection = repository .findPrimaryConnection(Facebook.class); return connection != null ? connection.getApi() : null; }
- As we are using Spring Social, most redirection will be handled by the
ConnectController
class which is declared by Spring Social. But how does Spring Social know to build the redirect URI? We have not provided the application's domain. By default, Spring Social uses request data to build the redirect URL automatically. But as the application might be deployed behind a proxy, the provider won't be capable of redirecting the user back to the callback URL defined insideConnectController
. To overcome this issue, declare the following method inside theFacebookConfiguration
class:
@Bean public ConnectController connectController( ConnectionFactoryLocator factoryLocator, ConnectionRepository repository) { ConnectController controller = new ConnectController(factoryLocator, repository); controller.setApplicationUrl("http://localhost:8080"); return controller; }
- This controller provides all we need to handle OAuth 2.0's authorization flow. It also allows the rendering of two views which by default are named
{provider}Connect
and{provider}Connected
where the provider in this case isfacebook
. To satisfy both views, create the following HTML files inside the foldertemplates/connect
within thesrc/main/resources
project's directory, as follows:
- Now add the following content to
facebookConnect.html
:
<html> <head> <title>Social Authcode</title> </head> <body> <h2>Connect to Facebook to see your contacts</h2> <form action="/connect/facebook" method="POST"> <input type="hidden" name="scope" value="public_profile user_friends" /> <input type="hidden" name="response_type" value="code" /> <div class="formInfo"> Click the button to share your contacts with <b>social-authcode</b> </div> <p><button type="submit">Connect to Facebook</button></p> </form> </body> </html>
- And now add the following content to
facebookConnected.html
:
<html> <head><title>Social Authcode</title></head> <body> <h2>Connected to Facebook</h2> <p>Click <a href="/">here</a> to see your friends.</p> </body> </html>
- That's it. Now you can start the application by running the class
SocialAuthcodeApplication
.
This chapter presented you with how to register your application and how to connect with Facebook through the use of the Authorization Code grant type. Because it's a server side flow, it is supposed to be more secure than using the client-side approach (that is, to use the Implicit grant type). But instead of writing the code to handle all the conversations between social-authcode
and Facebook (the OAuth 2.0 dance) we are using Spring Social, which provides the ConnectController
class which has the capability of starting the authorization flow as well as receiving all callbacks that must be mapped when registering the application.
To better understand how this application works, run the class SocialAuthcodeApplication
as Java code and go to http://localhost:8080/
to see the page that will present you with the possibility of connecting to Facebook. Click on Connect to Facebook
and you will be redirected to the Facebook authentication page (as per OAuth 2.0's specifications).
After authenticating the user, Facebook presents the user consent page presenting the scope the client application is asking for. Click on continue to grant the requested permission.
After granting permission for public_profile
and friend_list
scopes, the user must be redirected back to localhost:8080/connect
with the authorization code embedded (which will be extracted and validated by ConnectController
automatically).
Note that ConnectController
will render the facebookConnected
view by presenting the following page:
Click on the link here
so the application can retrieve the friends which have also authorized social-authcode
. You are supposed to see the following page with different content:
When registering the application on Facebook, we also configured the redirect URI to be http://localhost:8080/connect
. Why not use http://localhost:8080/callback
? By using the /connect
endpoint, we take advantage of the endpoints defined by ConnectController
. If you do not want to use Spring Social, you are supposed to validate the authorization code through the use of state parameters by yourself. When using Spring Social, we also take advantage of callbacks which are particular to the Facebook provider as De-authorize Callback URL's which might be set up in the settings from the Facebook Login product.
Even though we are using Spring Social Facebook we are still creating some classes that are also provided by Spring Social Facebook. As you could realize, the name of some classes begin with Custom
. That's because we can customize how to create an instance of OAuth2Template
as well as the FacebookTemplate
class. It's important because the version supported at the time of this writing was 2.5, which was to be deprecated soon, and that's the version defined inside the Facebook provider for Spring Social.
There is an important thing to be mentioned about the interactions between the client and the OAuth 2.0 Provider, which in this case is Facebook. As you may realize, we are registering the redirect URI without using TLS/SSL. The URI we've registered is HTTP instead of HTTPS. All the recipes in this book are using such an approach just to ease the creation of the examples. Be sure to use HTTPS in production to protect the integrity and confidentiality of data transferred between your application and any other provider.
Another valuable improvement which might be done is to use a Relational Database Management System (RDBMS) to persist connections with providers. As the application does not explicitly define the strategy for connection persistence, Spring Social provides the in-memory version, so whenever you restart your server the user's connections will be lost. If you want to try using a database, you might declare a bean of type JdbcUsersConnectionRepository
, and create the following table within the database of your choice:
create table UserConnection (userId varchar(255) not null, providerId varchar(255) not null, providerUserId varchar(255), rank int not null, displayName varchar(255), profileUrl varchar(512), imageUrl varchar(512), accessToken varchar(512) not null, secret varchar(512), refreshToken varchar(512), expireTime bigint, primary key (userId, providerId, providerUserId)); create unique index UserConnectionRank on UserConnection(userId, providerId, rank);
To get more details about this, look at Spring Social's official documentation at http://docs.spring.io/spring-social/docs/1.1.4.RELEASE/reference/htmlsingle/#section_establishingConnections.
This recipe presents you with how to retrieve a LinkedIn user's profile through the user's authorization using OAuth 2.0 and Spring Social to abstract all Authorization Code grant type from the OAuth 2.0 protocol.
To run this recipe, create a simple web application using Spring Boot which will help the development of the application. To abstract and ease the OAuth 2.0 grant type implementation, and to help in using the LinkedIn API, this recipe also relies on the spring-social-linkedin
project.
As described by OAuth 2.0's protocol, the client application must be registered at the Authorization Server which in this case is LinkedIn:
- So to satisfy this condition, the first thing to do is to register the application on LinkedIn by accessing https://www.linkedin.com/developer/apps/.
- When accessing the previous URL, click on
Create Application
and you will be redirected to the following page which will ask you for basic information about the application being created:
- Unlike many other OAuth 2.0 Providers, LinkedIn requires an application logo as you might have seen in the previous image. LinkedIn also asks for more business data such as the website URL, business email, and business phone.
- As we are using Spring Social, let's add a Redirect URL which follows the pattern regarding the endpoint which was defined as
connect/linkedin
. After entering theRedirect URL
, click on theAdd
and then click onUpdate
button. - Now, make sure to grab the
Authorization Keys
(that is,client_id
andclient_secret
) to use in the application that we will create in the next step.
- Create the initial project using Spring Initializr as we did for the other recipes in this book. Go to https://start.spring.io/ and define the following data:
- Set up the
Group
ascom.packt.example
- Define the
Artifact
associal-linkd
(you can use different names if you prefer, but do not forget to change all the references forlinkd
that were used throughout this recipe) - Add
Web
andThymeleaf
as the dependencies for this project
- Set up the
- Import the project to your IDE (if using Eclipse, import as a Maven Project).
- Add the following dependency to the
pom.xml
file:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-social-linkedin</artifactId> </dependency>
- For this recipe, the Spring Social provider implementation already provides a well-defined auto configuration support for Spring Boot. So, it's easier to create the application and just having to worry about the client credential settings. Open the
application.properties
file and add the following content (using the credentials generated for your application):
spring.social.linkedin.app-id=77a1bnosz2wdm8 spring.social.linkedin.app-secret=STHgwbfPSg0Hy8bO
- Now create the controller class
ProfileController
which has the responsibility of retrieving the user's profile through the use of the LinkedIn API. This class should be created within the packagecom.packt.linkedin.example.sociallinkd
. - Make sure the class
ProfileController
looks like the following:
@Controller public class ProfileController { @Autowired private LinkedIn linkedin; @Autowired private ConnectionRepository connectionRepository; @GetMapping public String profile(Model model) { if (connectionRepository.findPrimaryConnection(LinkedIn.class) == null) { return "redirect:/connect/linkedin"; } String firstName = linkedin.profileOperations() .getUserProfile().getFirstName(); model.addAttribute("name", firstName); return "profile"; } }
- As you might expect, the application will be able to retrieve the user's profile only when the user has made the connection between LinkedIn and the
social-linkd
client application. - So, if there is no connection available, the user will be redirected to
/connect/linkedin
which is mapped by theConnectController
class from Spring Social. Such an endpoint will redirect the user to the view, defined by the namelinkedinConnect
which maps directly to thelinkedinConnect.html
file that might be created undertemplates/connect
directory, located within thesrc/main/resources
project directory as follows:
- Looking at the previous screenshot, you can see that there is also
linkedinConnected.html
, which will be presented when a user's connection is available for thesocial-linkd
application. - All the logic to decide when to present
linkedinConnect.html
orlinkedinConnected.html
is defined inside the methodconnectionStatus
from theConnectController
class. The main logic is defined as presented in the following code:
if (connections.isEmpty()) { return connectView(providerId); } else { model.addAttribute("connections", connections); return connectedView(providerId); }
- Add the following HTML content to
linkedinConnect.html
:
<html> <head><title>Social LinkedIn</title></head> <body> <h2>Connect to LinkedIn to see your profile</h2> <form action="/connect/linkedin" method="POST"> <input type="hidden" name="scope" value="r_basicprofile" /> <div class="formInfo"> Click the button to share your profile with <b>social-linkedin</b> </div> <p><button type="submit">Connect to LinkedIn</button></p> </form> </body> </html>
- Now add the following HTML content to
linkedinConnected.html
:
<html> <head> <title>Social LinkedIn</title> </head> <body> <h2>Connected to LinkedIn</h2> <p>Click <a href="/">here</a> to see your profile.</p> </body> </html>
- To present the user's profile, create the file
profile.html
inside the templates directory with the following content:
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <title>LinkedIn integration</title> </head> <body> <h3>Hello, <span th:text="${name}">User</span>!</h3> <br/> </body> </html>
- Now that everything is perfectly configured, start the application and go to
http://localhost:8080
to start running the authorization flow.
This recipe presented you with how you can create an application that interacts with LinkedIn to retrieve the user's profile using OAuth 2.0 protocol. This recipe relies on Spring Social Provider for LinkedIn, which saves us from having to create a controller to deal with OAuth 2.0 callbacks as well as building URLs for authorization and token requests. This recipe differs from other recipes using Spring Social because it presents one provider implementation which support Spring Boot's auto-configuration feature, so we don't need to create any configuration classes.
Besides the fact that a lot of OAuth 2.0's details are abstracted behind Spring Social, all the steps happen when we run the application and start the authorization flow. In fact, as we are using the Authorization Code grant type, the application retrieves the access token through two steps, which are authorization and token request.
To start the authorization flow you must go to http://localhost:8080/
which, in case of being not connected the user's LinkedIn account with the social-linkd
application, should be redirected to /connect/linkedin
:
The redirection is performed by the method profile
from the ProfileController
class. As the controller and this method do not define any paths for a request, it will be defined as /
by default. As you may notice in the following code, the first thing the method repositories do is to check if the current user has connected her account with the application, which is social-linkedin
:
if (connectionRepository.findPrimaryConnection(LinkedIn.class) == null) { return "redirect:/connect/linkedin"; }
The endpoint /connect/linkedin
maps directly to the method connectionStatus
from the ConnectController
class of Spring Social. If there is no connection, this method calls the private method connectView
which builds the name {providerId}Connect
, which in LinkedIn's case is linkedinConnect
. This is exactly the name of the view we created as linkedinConnect.html
.
Open the file linkedinConnect.html
to see which scope the application is asking for LinkedIn, and you must realize that it is r_basicprofile
. All the available scopes defined by LinkedIn should be retrieved by accessing the application dashboard which is present in the section Default Application Permissions
.
Back to the page generated by linkedinConnect
view, if you click on the Connect to LinkedIn
button, you will be redirected to LinkedIn, which will ask you for your credentials and for your consent.
Notice that LinkedIn, unlike many other OAuth 2.0 Providers, asks for permission at the same time it authenticates the user. If you click on Allow Access
and send your credentials at the authentication form, social-linkd
will receive the authorization code and will use it to retrieve an access token and create the connection for the current user within the application. Then, if there is a connection, the private method connectedView
from ConnectController
will be called, which will render the following HTML page defined by linkedinConnected.html
:
Clicking on the link here,
you will then be redirected to the main page, where your profile name will be presented as follows:
For this recipe, we didn't create any configuration class. But if you want to define the base URL for callback redirection that happens when using the OAuth 2.0 protocol, you need to create a configuration class to define a custom ConnectController
bean, as presented in the following code:
@Configuration public class LinkedInConfiguration { @Bean public ConnectController connectController(ConnectionFactoryLocator locator, ConnectionRepository repository) { ConnectController controller = new ConnectController(locator, repository); controller.setApplicationUrl("http://localhost:8080"); return controller; } }
By doing such configurations, you avoid issues when the application runs behind a proxy. The redirect URI will be automatically generated using the request info which will be based on the application which might be running behind a proxy. This way, the OAuth 2.0 Provider won't be able to redirect to the right callback URL because it will be hidden by the proxy. The configuration presented previously allows you to define the proxy's URL.
Do not forget to define the same redirect URL at the OAuth 2.0 Provider and make all the communication through TLS/SSL.
This recipe presents you with how to retrieve a user's profile from Google Plus, through the user's Google account. This recipe relies on Spring Social to abstract the authorization and the usage of the Google Plus API and tightens the user's Google connection with Spring Security to allow managing connections per logged user.
To run this recipe, create a web application using Spring Boot which will help with the development of the application. To abstract and ease the OAuth 2.0 grant type implementation and help with using Google Plus's API, this recipe also relies on the spring-social-google
project and spring-security
project.
Follow the steps that basically present you with how to register your application with Google and how to interact with the Google Plus API through the use of the Spring Social Google provider with Spring Security.
- Go to the Google Developers Console located at https://console.developers.google.com to start registering the application.
- You should see the following screen if you still do not have any project created:
- To create a new application, click on
Select a project
and you will see the following screen:
- Therefore, click on the
+
button to start registering your application and you will see the following interface:
- Define the name of your project. I have defined the name
social-google1
but you can use any of your preference (but don't forget to change all references to this name throughout the recipe). Then, after setting the name, just click onCreate
and you will be redirected to the dashboard of your new application as presented in the following screenshot:
- Now, to be able to retrieve a user's profile, you must enable an API which in the case of this recipe will be the Google Plus API. Click on the
ENABLE API
link or onLibrary
at the left side of the dashboard presented in the previous screenshot to see the following Google API portfolio:
- Then select
Google+ API
by searching through theSearch all
input text, or by clicking on the link presented in the Libraries page as follows:
- Click on the
ENABLE
link at the top of the page as presented in the following screenshot:
- After enabling the API, you need to create OAuth 2.0 credentials to be able to interact with the Google Plus API, so you must click on
Credentials
at the left side of the panel:
- Click on
Create credentials
and select the OAuth client ID as follows:
- Select the
Application Type
which must beWeb application
for this recipe:
- Then enter the URL settings for JavaScript origins and Authorized redirect URIs, as presented in the following screenshot:
- Click on
Create
and grab the client ID and client secret that will be prompted by Google as follows:
- Now, you have all that's needed to create a
social-google1
project.
- Create the initial project using Spring Initializr as we did for other recipes in this book. Go to https://start.spring.io/ and define the following data:
- Set up the group as
com.packt.example
. - Define the Artifact as
social-google1
(you can use different names if you prefer, but do not forgot to change all the references tosocial-google1
used throughout this recipe). - Add
Web
,Thymeleaf
andSecurity
as the dependencies for this project.
- Set up the group as
- Import the project to your IDE (if using Eclipse, import as a Maven Project).
- Open the
pom.xml
file and add the following dependencies:
<dependency> <groupId>org.springframework.social</groupId> <artifactId>spring-social-google</artifactId> <version>1.0.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.social</groupId> <artifactId>spring-social-security</artifactId> </dependency>
- Create the class
GoogleProperties
within the packagecom.packt.example.socialgoogle1.config
and add the following content:
@ConfigurationProperties("spring.social.google") public class GoogleProperties { private String appId; private String appSecret; public String getAppId() { return this.appId; } public void setAppId(String appId) { this.appId = appId; } public String getAppSecret() { return this.appSecret; } public void setAppSecret(String appSecret) { this.appSecret = appSecret; } }
- Now add the respective attributes within the file
application.properties
which maps to the attributes defined in theGoogleProperties
class (change the credentials to those received when registering the application):
spring.social.google.appId=688645170704 spring.social.google.appSecret=OIRTUxhs
- Create the class
GoogleConfigurerAdapter
insidecom.packt.example.socialgoogle1.config
with the following content:
@Configuration @EnableSocial @EnableConfigurationProperties(GoogleProperties.class) public class GoogleConfigurerAdapter extends SocialConfigurerAdapter { @Autowired private GoogleProperties properties; }
- Now add the following method to configure the
ConnectionFactory
for the Google provider (this method must be declared insideGoogleConfigurerAdapter
):
@Override public void addConnectionFactories(ConnectionFactoryConfigurer configurer, Environment environment) { GoogleConnectionFactory factory = new GoogleConnectionFactory( this.properties.getAppId(), this.properties.getAppSecret()); configurer.addConnectionFactory(factory); }
- Add the following method to declare the bean responsible for providing the (DSL) Domain Specific Language for Google API (when importing the
Scope
class, import it fromorg.springframework.context.annotation
package and theConnection
class, you must import fromorg.springframework.social.connect
package):
@Bean @Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES) public Google google(final ConnectionRepository repository) { final Connection<Google> connection = repository.findPrimaryConnection(Google.class); return connection != null ? connection.getApi() : null; }
- As per the documentation of Spring Social, we need to configure the
ConnectionRepository
bean using a Session scope, or in other words, aConnectionRepository
must be created on a per user basis. To do so, add the following two method declarations insideGoogleConfigurerAdapter
:
@Override public UsersConnectionRepository getUsersConnectionRepository( ConnectionFactoryLocator connectionFactoryLocator) { return new InMemoryUsersConnectionRepository(connectionFactoryLocator); } @Override public UserIdSource getUserIdSource() { return new AuthenticationNameUserIdSource(); }
- Now, as the class
AuthenticationNameUserIdSource
retrieves the logged user from the Spring Security context, you need to configure Spring Security, defining how to protect the application as well as declaring how to authenticate users. Create the classSecurityConfiguration
within the packagecom.packt.example.socialgoogle1.security
, containing the following initial code:
@EnableWebSecurity public class SecurityConfiguration extends WebSecurityConfigurerAdapter { }
- Then add the following method inside
SecurityConfiguration
to define what should be protected, what does not need to be protected, and how the authentication must be performed (which is defined to use form basis authentication):
@Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/connect/google?*").permitAll() .anyRequest().authenticated().and() .formLogin().and() .logout().permitAll().and() .csrf().disable(); }
- And to declare some predefined users, add the following method to
SecurityConfiguration
.
@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser("adolfo").password("123").authorities("USER") .and() .withUser("jujuba").password("123").authorities("USER"); }
- Therefore, create the main controller that decides when to redirect a user to the provider's authentication and authorization page and which retrieves the user's profile by interacting with the Google Plus API. Create the class
GooglePlusController
inside thecom.packt.example.socialgoogle1
package as presented in the following code:
@Controller public class GooglePlusController { @Autowired private Google google; @Autowired private ConnectionRepository connectionRepository; @GetMapping public String profile(Model model) { if (connectionRepository .findPrimaryConnection(Google.class) == null) { return "redirect:/connect/google"; } String name = google.plusOperations() .getGoogleProfile() .getDisplayName(); model.addAttribute("name", name); return "profile"; } }
- Now let's start creating all the views, primarily defining
profile.html
inside thetemplates
directory, which resides within thesrc/main/resources
project directory. Add the following content toprofile.html
:
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <title>LinkedIn integration</title> </head> <body> <h3> Hello, <span th:text="${name}">User</span>! </h3> <br /> </body> </html>
- Create the
googleConnect.html
andgoogleConnected.html
files insidetemplates/connect
as presented in the following screenshot:
<html> <head> <title>Social Google+</title> </head> <body> <h2>Connect to Google+ to see your profile</h2> <form action="/connect/google" method="POST"> <input type="hidden" name="scope" value="https://www.googleapis.com/auth/plus.me" /> <div class="formInfo"> Click the button to share your profile with <b>google plus</b> </div> <p> <button type="submit">Connect to Google</button> </p> </form> </body> </html>
- Add the following content to
googleConnected.html
:
<html> <head><title>Social Google Plus</title></head> <body> <h2>Connected to Google</h2> <p>Click <a href="/">here</a> to see your profile.</p> </body> </html>
- Now the application is ready to be executed. Start the application and go to
http://localhost:8080
to start the authorization process to interact with the Google Plus API.
An important thing that we did for this recipe was to bind the application users to their respective connection with the OAuth 2.0 Provider (Google in this case). It's important because by doing so, we have a connection per user, unlike the other recipes using Spring Social. But instead of allowing users to register themselves to the social-google1
application, we are using an in-memory model using pre-defined user credentials, as presented in the following code:
auth.inMemoryAuthentication() .withUser("adolfo").password("123").authorities("USER") .and() .withUser("jujuba").password("123").authorities("USER");
So, when running the application and pointing your browser to http://localhost:8080
, you must be prompted by an authentication form, as follows.
Enter one of the credentials we declared within the SecurityConfiguration
class and click on the Login
button, which will lead you to the following page:
This is the page where you might choose to connect with Google by clicking on Connect to Google
, which will redirect you to Google's authentication and authorization form as presented in the following screenshot:
Authenticate yourself and grant all the requested permissions and you will be redirected back to the connected page:
Click on the link here
and you will be redirected to the profile's HTML view which will retrieve your name from the Google Plus API. Now, if you go to http://localhost:8080/logout
, you will be logged out, as you might expect, and if you try to log in with another user you will have to start a new connection flow proving that you have a connection per logged user.
For this recipe, we did not configure the base URL, which must be done to avoid issues when running your application behind a proxy. To do so, you might add the following bean declaration inside GoogleConfigurerAdapter
:
@Bean public ConnectController connectController( ConnectionFactoryLocator locator, ConnectionRepository repository) { ConnectController controller = new ConnectController(locator, repository); controller.setApplicationUrl("http://localhost:8080"); return controller; }
Do not forget to define the same redirect URL for the OAuth 2.0 Provider and to make all the communications through TLS/SSL.
To improve security configurations, you might use a database to store all users and respective credentials being held in a cryptographically manner (these features are provided by Spring Security and can be read about in the official documents at https://projects.spring.io/spring-security/).