Book Image

OAuth 2.0 Cookbook

By : Adolfo Eloy Nascimento
Book Image

OAuth 2.0 Cookbook

By: Adolfo Eloy Nascimento

Overview of 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.
Table of Contents (16 chapters)
Title Page
Credits
About the Author
About the Reviewer
www.PacktPub.com
Customer Feedback
Preface
Index

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