Home Web Development OAuth 2.0 Cookbook

OAuth 2.0 Cookbook

By Adolfo Eloy Nascimento
books-svg-icon Book
eBook $39.99 $27.98
Print $48.99
Subscription $15.99 $10 p/m for three months
$10 p/m for first 3 months. $15.99 p/m after that. Cancel Anytime!
What do you get with a Packt Subscription?
This book & 7000+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook + Subscription?
Download this book in EPUB and PDF formats, plus a monthly download credit
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook?
Download this book in EPUB and PDF formats
Access this title in our online reader
DRM FREE - Read whenever, wherever and however you want
Online reader with customised display settings for better reading experience
What do you get with video?
Download this video in MP4 format
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with video?
Stream this video
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with Audiobook?
Download a zip folder consisting of audio files (in MP3 Format) along with supplementary PDF
What do you get with Exam Trainer?
Flashcards, Mock exams, Exam Tips, Practice Questions
Access these resources with our interactive certification platform
Mobile compatible-Practice whenever, wherever, however you want
BUY NOW $10 p/m for first 3 months. $15.99 p/m after that. Cancel Anytime!
eBook $39.99 $27.98
Print $48.99
Subscription $15.99 $10 p/m for three months
What do you get with a Packt Subscription?
This book & 7000+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook + Subscription?
Download this book in EPUB and PDF formats, plus a monthly download credit
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook?
Download this book in EPUB and PDF formats
Access this title in our online reader
DRM FREE - Read whenever, wherever and however you want
Online reader with customised display settings for better reading experience
What do you get with video?
Download this video in MP4 format
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with video?
Stream this video
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with Audiobook?
Download a zip folder consisting of audio files (in MP3 Format) along with supplementary PDF
What do you get with Exam Trainer?
Flashcards, Mock exams, Exam Tips, Practice Questions
Access these resources with our interactive certification platform
Mobile compatible-Practice whenever, wherever, however you want
  1. Free Chapter
    OAuth 2.0 Foundations
About this book
OAuth 2.0 is a standard protocol for authorization and focuses on client development simplicity while providing specific authorization flows for web applications, desktop applications, mobile phones, and so on. This book also provides useful recipes for solving real-life problems using Spring Security and creating Android applications. The book starts by presenting you how to interact with some public OAuth 2.0 protected APIs such as Facebook, LinkedIn and Google. You will also be able to implement your own OAuth 2.0 provider with Spring Security OAuth2. Next, the book will cover practical scenarios regarding some important OAuth 2.0 profiles such as Dynamic Client Registration, Token Introspection and how to revoke issued access tokens. You will then be introduced to the usage of JWT, OpenID Connect, and how to safely implement native mobile OAuth 2.0 Clients. By the end of this book, you will be able to ensure that both the server and client are protected against common vulnerabilities.
Publication date:
October 2017
Publisher
Packt
Pages
420
ISBN
9781788295963

 

Chapter 1. OAuth 2.0 Foundations

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
 

Introduction


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.

 

Preparing the environment


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.

Getting ready

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.

How to do it...

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:

  1. 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.
  2. For this simple test, just use the default values for the project manager, Maven Project, with Java language and version 1.5.7 of the Spring Boot.
  3. AtProject Metadata, change the value of the fieldGrouptocom.packt.example.
  4. Still on Project Metadata, change the name of the Artifact to simplemvc.
  5. In the Dependencies section, type web and select Full-stack web development with Tomcat and Spring MVC. After selecting the right choice, you will see the tag Web underneath Selected Dependencies as follows:
  1. 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 your Downloads folder.
  2. 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.
  1. 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); 
   } 
} 
  1. 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); 
   } 
} 
  1. 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 class SimplemvcApplication:
@GetMapping("/message") 
public ResponseEntity<String> getMessage() { 
   return ResponseEntity.ok("Hello!"); 
} 
  1. 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 option Run As |Java Application.
  2. 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)
  1. Execute the following command to know if your application works properly (just check if the output prints Hello):

 

curl "http://localhost:8080/message"
  1. 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
  1. 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.
  2. After the file has downloaded, just unpack it into any folder you want and start running Maven commands.
  3. 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 pwdat the command line to discover the full path.
  4. After that, you must add the path for Maven's directory to the PATHenvironment variable. If you are using Linux or macOS, create the variableMVN_HOMEwithin the.bash_profilefile and append the content ofMVN_HOMEto the end of thePATHenvironment 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_profileshould 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.

  1. After editing this file, run the command source ~/.bash_profile to reload all the contents.
  2. To check if Maven is perfectly running on your environment, run the following command:
mvn --version.

See also

How it works...

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

There's more...

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.

See also

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.

 

Reading the user's contacts from Facebook on the client side


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.

Getting ready

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.

How to do it...

Follow these steps to create a client application to integrate with Facebook using client-side flow from OAuth 2.0:

  1. 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:
  1. 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:

  1. Click on Create App ID and you will be redirected to the newly created application's dashboard as follows:
  1. 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 the Facebook Login box.
  2. After clicking on Set Up, you must choose one platform, which must be Web for this recipe.
  3. After choosing the Web platform, enter the Site URL for your application. I am using a fictitious URL named http://clientimplicit.test.
  4. After saving the URL of your site, just click on Continue.
  5. 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 as http://localhost:8080/callback. Don't forget to save the changes:
  1. Now you can click on Dashboard at the left side of the panel so you can grab the App ID and App Secret, which maps to the client_id and client_secret from OAuth 2.0 specifications respectively.
  2. Copy the App ID and App 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:
  1. 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).
  2. 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 and Thymeleaf as dependencies for this project
  1. Create the file client.html within the folder templates which resides inside the src/main/resources project's directory.
  1. 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>  
  1. 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>
  1. 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"; } 
} 
  1. 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 to http://localhost:8080/.
  2. Click on the ;Authorize button that will be provided by client.html to start the Implicit grant flow, log in with your Facebook account, and accept the permissions requested by the client-implicit application (make sure that jquery is properly declared inside client.html file).
  3. 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 on Continue 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 
  1. Now we need to extract both the access_token and the expires_in parameters which comes after #character, and start using the Facebook Graph API to retrieve the user's friends.
  1. 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"; } 
  1. As we can see, the method callback is returning the callback_pagestring, which will automatically be mapped to the file callback_page.html. So, let's create this file inside the templates folder which resides in the src/main/resources project directory. At first just add the following HTML content to the callback_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> 
  1. 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>
  1. 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; 
} 
  1. The code presented before creates an object named response which might contain an access_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 the getResponse function can tell if the response has an error.
  2. 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; 
   } 
});
  1. 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); 
        } 
    }); 
} 
  1. 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>'); 
       }); 
   }); 
}); 
  1. 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.
  1. Start the application.
  2. Go to http://localhost:8080/ and click on the Authorize button.
  3. Grant the requested permissions.
  4. When you are redirected back to client-implicit, you should see something like the following in your web browser:
  1. 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.

Note

When running on Firefox which version is over 42, you must need to disable Tracking Protection that is a feature provided by Firefox to block content loaded from domains that track users across sites).

How it works...

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.

There's more...

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

See also

  • Preparing the environment
  • Reading the user's contacts from Facebook on the server side
 

Reading the user's contacts from Facebook on the server side


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.

Getting ready

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.

How to do it...

Follow the steps below to create a client application to integrate with Facebook using the server-side flow from OAuth 2.0:

  1. Go to https://developers.facebook.com/apps/ and add a new application by clicking on Add a New App.
  2. Register a new client application on Facebook with the Display Namesocial-authcode.
  1. You will be guided to select one Facebook product. So, choose Facebook Login by clicking on Set Up and then choose Web as a platform.
  2. You will be asked to enter the site URL, which might be http://socialauthcode.test/.
  3. After creating the application on Facebook, click on Facebook Login on the left panel to configure a valid redirect URI, which should be http://localhost:8080/connect/facebook.
  4. Click on Dashboard on the left panel so you can retrieve the App ID and App Secret which map to client_id and client_secret, as you may already know, and grab the credentials to use later when implementing the client application.
  5. 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 as com.packt.example
    • Define the Artifact as social-authcode
    • Add Web and Thymeleaf as the dependencies for this project
  1. Import the project to your IDE. When using Eclipse, just import it as a Maven project.
  1. 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> 
  1. Create an HTML file named friends.html inside the templates directory located within src/main/resources, as follows:
  1. 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>
  1. 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 package com.packt.example.socialauthcode, with the following content:
@Controller @RequestMapping("/") 
public class FriendsController { 
    @GetMapping 
    public String friends(Model model) { return "friends"; } 
}  
  1. As you may realize, the template friends.html relies on an object named facebookProfile and another named friends. The object facebookProfile must have the name attribute and the friends object must be a list of objects which have id and name 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 the FriendsController class:
@Autowired 
private Facebook facebook; 
@Autowired 
private ConnectionRepository connectionRepository;
  1. 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 the social-authcode of the application to retrieve protected resources (her friends). Replace the code from friends method of the FriendsController 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"; 
}
  1. 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 and Reference classes, make sure to import from org.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); 
  1. 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 inside com.packt.example.socialauthcode, which will accommodate the following classes:
  1. Create the class EnhancedFacebookProperties, as presented in the following code, inside the inner package facebook, so we can configure the application properties as client_id and client_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 
}
  1. Before continuing creating the other classes, you must configure the appSecret and apiVersion values so the application social-authcode is able to request an access_token. As you may realize, the class EnhancedFacebookProperties is annotated with @ConfigurationProperties which allows for defining the properties inside the application.properties file, as follows:
facebook.app-id=1948923582021549 
facebook.app-secret=1b4b0f882b185094a903e76a661c7c7c 
facebook.api-version=2.9 
  1. Now create the class CustomFacebookServiceProvider, as follows. This class is responsible for creating a custom instance of OAuth2Template 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; 
   } 
 
} 
  1. So that the CustomFacebookServiceProvider can be properly created, create the class CustomFacebookConnectionFactory 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()); 
   } 
} 
  1. 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()); 
   } 
}
  1. If you look at the content of FriendsController, you should see that this class is using an instance of Facebook which provides the API to interact with Facebook Graph API. The instance of Facebook must be created through a Spring bean declared as follows inside the FacebookConfiguration (When importing the Connection class, make sure you import from org.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; 
} 
  1. 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 inside ConnectController. To overcome this issue, declare the following method inside the FacebookConfiguration class:
@Bean 
public ConnectController connectController( 
         ConnectionFactoryLocator factoryLocator, 
         ConnectionRepository repository) { 
   ConnectController controller = new ConnectController(factoryLocator, repository); 
   controller.setApplicationUrl("http://localhost:8080"); 
   return controller; 
}
  1. 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 is facebook. To satisfy both views, create the following HTML files inside the folder templates/connect within the src/main/resources project's directory, as follows:
  1. 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>
  1. 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> 
  1. That's it. Now you can start the application by running the class SocialAuthcodeApplication.

How it works...

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:

There's more...

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.

See also

  • Preparing the environment
  • Reading the user's contacts from Facebook on the client side
  • Accessing OAuth 2.0 Google protected resources bound to user's session
 

Accessing OAuth 2.0 LinkedIn protected resources


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.

Getting ready

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.

How to do it...

As described by OAuth 2.0's protocol, the client application must be registered at the Authorization Server which in this case is LinkedIn:

  1. 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/.
  1. 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:
  1. 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.
  1. Fill out the form and click on the Submit button. You will be redirected to the application's dashboard as shown in the following screenshot which presents you with the Authentication Keys and the field to define the redirection URL:
  1. 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 the Redirect URL, click on the Add and then click on Update button.
  2. Now, make sure to grab the Authorization Keys (that is, client_id and client_secret) to use in the application that we will create in the next step.
  1. 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 as com.packt.example
    • Define the Artifact as social-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 WebandThymeleafas the dependencies for this project
  2. Import the project to your IDE (if using Eclipse, import as a Maven Project).
  3. Add the following dependency to the pom.xml file:
<dependency> 
   <groupId>org.springframework.boot</groupId> 
   <artifactId>spring-boot-starter-social-linkedin</artifactId> 
</dependency> 
  1. 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 
  1. 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 package com.packt.linkedin.example.sociallinkd.
  2. 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"; 
   } 
} 
  1. 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.
  2. So, if there is no connection available, the user will be redirected to /connect/linkedin which is mapped by the ConnectController class from Spring Social. Such an endpoint will redirect the user to the view, defined by the name linkedinConnect which maps directly to the linkedinConnect.html file that might be created under templates/connect directory, located within the src/main/resources project directory as follows:
  1. 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 the social-linkd application.
  2. All the logic to decide when to present linkedinConnect.html or linkedinConnected.html is defined inside the method connectionStatus from the ConnectController 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);                
}
  1. 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> 
  1. 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> 
  1. 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> 
  1. Now that everything is perfectly configured, start the application and go to http://localhost:8080 to start running the authorization flow.

How it works...

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:

There's more...

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.

See also

  • Preparing the environment
  • Reading the user's contacts from Facebook on the server side
  • Accessing OAuth 2.0 Google protected resources bound to the user's session
 

Accessing OAuth 2.0 Google protected resources bound to the user's session


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.

Getting ready

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.

How to do it...

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.

  1. Go to the Google Developers Console located at https://console.developers.google.com to start registering the application.
  1. You should see the following screen if you still do not have any project created:
  1. To create a new application, click on Select a project and you will see the following screen:

  1. Therefore, click on the + button to start registering your application and you will see the following interface:
  1. 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 on Create and you will be redirected to the dashboard of your new application as presented in the following screenshot:

  1. 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 on Library at the left side of the dashboard presented in the previous screenshot to see the following Google API portfolio:
  1. Then select Google+ API by searching through the Search all input text, or by clicking on the link presented in the Libraries page as follows:

  1. Click on the ENABLE link at the top of the page as presented in the following screenshot:
  1. 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:

  1. Click on Create credentials and select the OAuth client ID as follows:
  1. Select the Application Type which must be Web application for this recipe:

  1. Then enter the URL settings for JavaScript origins and Authorized redirect URIs, as presented in the following screenshot:
  1. Click on Create and grab the client ID and client secret that will be prompted by Google as follows:
  1. Now, you have all that's needed to create a social-google1 project.
  1. 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-google1used throughout this recipe).
    • Add Web,ThymeleafandSecurityas the dependencies for this project.
  2. Import the project to your IDE (if using Eclipse, import as a Maven Project).
  3. 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> 
  1. Create the class GoogleProperties within the package com.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; } 
} 
  1. Now add the respective attributes within the file application.properties which maps to the attributes defined in the GoogleProperties class (change the credentials to those received when registering the application):
spring.social.google.appId=688645170704 
spring.social.google.appSecret=OIRTUxhs
  1. Create the class GoogleConfigurerAdapter inside com.packt.example.socialgoogle1.config with the following content:
@Configuration @EnableSocial 
@EnableConfigurationProperties(GoogleProperties.class) 
public class GoogleConfigurerAdapter extends SocialConfigurerAdapter { 
   @Autowired 
   private GoogleProperties properties; 
} 
  1. Now add the following method to configure the ConnectionFactory for the Google provider (this method must be declared inside GoogleConfigurerAdapter):
@Override 
public void addConnectionFactories(ConnectionFactoryConfigurer configurer, 
   Environment environment) { 
   GoogleConnectionFactory factory =  new GoogleConnectionFactory( 
      this.properties.getAppId(), this.properties.getAppSecret()); 
   configurer.addConnectionFactory(factory); 
} 
  1. 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 from org.springframework.context.annotation package and the Connection class, you must import from org.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; 
} 
  1. As per the documentation of Spring Social, we need to configure the ConnectionRepository bean using a Session scope, or in other words, a ConnectionRepository must be created on a per user basis. To do so, add the following two method declarations inside GoogleConfigurerAdapter:
@Override 
public UsersConnectionRepository getUsersConnectionRepository( 
         ConnectionFactoryLocator connectionFactoryLocator) { 
   return new InMemoryUsersConnectionRepository(connectionFactoryLocator); 
} 
 
@Override 
public UserIdSource getUserIdSource() { 
   return new AuthenticationNameUserIdSource(); 
} 
  1. 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 class SecurityConfiguration within the package com.packt.example.socialgoogle1.security, containing the following initial code:
@EnableWebSecurity 
public class SecurityConfiguration extends WebSecurityConfigurerAdapter { 
} 
  1. 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(); 
}
  1. 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"); 
}
  1. 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"; 
   } 
}
  1. Now let's start creating all the views, primarily defining profile.html inside the templates directory, which resides within the src/main/resources project directory. Add the following content to profile.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>
  1. Create the googleConnect.html and googleConnected.html files inside templates/connect as presented in the following screenshot:
  1. Add the following content to googleConnect.html:
<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> 
  1. 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>
  1. 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.

How it works...

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.

There's more...

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/).

See also

  • Preparing the environment
  • Reading the user's contacts from Facebook on the server side
  • Accessing OAuth 2.0 LinkedIn protected resources
About the Author
  • Adolfo Eloy Nascimento

    Adolfo Eloy Nascimento is a software engineer at Elo7, he has a Bachelors degree in Computer Science, and has been working with software development since 1999. In around 2003, he started working with web development implementing applications using ASP, PHP4/5, JavaScript, and Java (sometimes he still does some maintenance for a Ruby on Rails application). He started using OAuth 2.0 two years ago, when designing applications using microservice architectures, as well as modeling and interacting with public APIs. As a tech enthusiast, Adolfo also likes to read and learn about programming languages and new technologies. He also believes that besides creating new applications, it is also important to share the knowledge he has acquired, which is what he does by writing for his personal blog, writing articles for Java Magazine in Brazil, and also writing tech books.

    Browse publications by this author
Latest Reviews (1 reviews total)
Outdated content. None of the Spring examples work.
OAuth 2.0 Cookbook
Unlock this book and the full library FREE for 7 days
Start now