Spring Boot oauth2 tutorial for accessing Facebook, Google, LinkedIn and Twitter
Maven dependencies
Add the following dependencies in your pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>angularjs</artifactId> <version>1.4.3</version> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>jquery</artifactId> <version>2.1.1</version> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>bootstrap</artifactId> <version>3.2.0</version> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>webjars-locator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> </dependencies> |
Add the right annotations and extend the WebSecurityConfigurerAdapter
1 2 3 4 5 6 | @SpringBootApplication @EnableOAuth2Client @RestController public class SimpleApplication extends WebSecurityConfigurerAdapter { |
Override the configure method to allow
- Filters needed for single sign-on
- The request to be authenticated for selected URLs like /connect/.
- Defining the logout URL
- Defining the CsrfTokenRepository
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | // @formatter:off @Override protected void configure(HttpSecurity http) throws Exception { http.antMatcher("/**") .addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class) .authorizeRequests() .antMatchers("/", "/connect**", "/webjars/**") .permitAll() .anyRequest() .authenticated() .and() .logout() .logoutSuccessUrl("/").permitAll().and().csrf() .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()); } // @formatter:on |
- Add the request mapping to get Principal(The User logged In)
1 2 3 4 5 6 | @RequestMapping("/user") public Principal user(Principal principal) { return principal; } |
- Add the filters needed for each provider
1 2 3 4 5 6 7 8 | CompositeFilter filter = new CompositeFilter(); List filters = new ArrayList<>(); OAuth2ClientAuthenticationProcessingFilter facebookFilter = new OAuth2ClientAuthenticationProcessingFilter( "/connect/facebook"); OAuth2RestTemplate facebookTemplate = new OAuth2RestTemplate(facebook(), oauth2ClientContext);.... |
- Define the Beans needs for each provider
1 2 3 4 5 6 7 | @Bean @ConfigurationProperties("google.client") public AuthorizationCodeResourceDetails google() { return new AuthorizationCodeResourceDetails(); }....... |
Updating the Properties file with ClientId and Secret Key
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | facebook: client: clientId: 384261248599251 clientSecret: fd7fa1c5f5a267f463263a0ce7ff2025 accessTokenUri: https://graph.facebook.com/oauth/access_token userAuthorizationUri: https://www.facebook.com/dialog/oauth tokenName: oauth_token authenticationScheme: query clientAuthenticationScheme: form resource: userInfoUri: https://graph.facebook.com/me google: client: clientId: 12894100090-tqso3lih5o42isneort886la2pesafmp.apps.googleusercontent.com clientSecret: 9xfU16efvxQ-BTMsXT9wOLpw accessTokenUri: https://accounts.google.com/o/oauth2/token userAuthorizationUri: https://accounts.google.com/o/oauth2/auth clientAuthenticationScheme: form scope: profile email resource: userInfoUri: https://www.googleapis.com/oauth2/v3/userinfo linkedIn: client: clientId: 771mrzk94hye1w clientSecret: iIJFgBf9lCb18zYe accessTokenUri: https://www.linkedin.com/oauth/v2/accessToken userAuthorizationUri: https://www.linkedin.com/oauth/v2/authorization resource: userInfoUri: https://api.linkedin.com/v1/people/~?format=json twitter: client: clientId: oXJIDGVdB0PDnMICDwKckyzKm clientSecret: Z4BpN51kNsb2wbEfzDXm40v38W3I2P1u4H6fvfM6HQraVfry5j accessTokenUri: https://api.twitter.com/oauth/access_token userAuthorizationUri: https://api.twitter.com/oauth/authorize clientAuthenticationScheme: form resource: userInfoUri: https://api.twitter.com/1.1/users/show.json server: port: 3000 |
Update the HTML to add Links for LinkedIn, Twitter and Google
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | <!doctype html> <html lang="en"> <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <title>Demo</title> <meta name="description" content="" /> <meta name="viewport" content="width=device-width" /> <base href="/" /> <link rel="stylesheet" type="text/css" href="/webjars/bootstrap/css/bootstrap.min.css" /> <script type="text/javascript" src="/webjars/jquery/jquery.min.js"></script> <script type="text/javascript" src="/webjars/bootstrap/js/bootstrap.min.js"></script> </head> <body ng-app="app" ng-controller="home as home"> <h1>Login</h1> <div class="container" ng-show="!home.authenticated"> <div> With Facebook: <a href="/connect/facebook">click here</a> </div> <div> With Google: <a href="/connect/google">click here</a> </div> <div> With LinkedIn: <a href="/connect/linkedIn">click here</a> </div> <div> With Twitter: <a href="/connect/twitter">click here</a> </div> </div> <div class="container" ng-show="home.authenticated"> Logged in as: <span ng-bind="home.user"></span> </div> <div> <button ng-click="home.logout()" class="btn btn-primary">Logout</button> </div> </body> <script type="text/javascript" src="/webjars/angularjs/angular.min.js"></script> <script type="text/javascript"> angular.module("app", []).controller("home", function($http) { var self = this; $http.get("/user").success(function(data) { self.user = data.userAuthentication.details.name; self.authenticated = true; }).error(function() { self.user = "N/A"; self.authenticated = false; }); self.logout = function() { $http.post('/logout', {}).success(function() { self.authenticated = false; $location.path("/"); }).error(function(data) { console.log("Logout failed") self.authenticated = false; }); }; }); </script> </html> |
The Complete Java class will look like this
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | package com.example; import java.security.Principal; import java.util.ArrayList; import java.util.List; import javax.servlet.Filter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties; import org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoTokenServices; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.oauth2.client.OAuth2ClientContext; import org.springframework.security.oauth2.client.OAuth2RestTemplate; import org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter; import org.springframework.security.oauth2.client.filter.OAuth2ClientContextFilter; import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import org.springframework.security.web.csrf.CookieCsrfTokenRepository; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.filter.CompositeFilter; @SpringBootApplication @EnableOAuth2Client @RestController public class SimpleApplication extends WebSecurityConfigurerAdapter { @Autowired OAuth2ClientContext oauth2ClientContext; public static void main(String[] args) { SpringApplication.run(SimpleApplication.class, args); } @RequestMapping("/user") public Principal user(Principal principal) { return principal; } // @formatter:off @Override protected void configure(HttpSecurity http) throws Exception { http.antMatcher("/**") .addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class) .authorizeRequests() .antMatchers("/", "/connect**", "/webjars/**") .permitAll() .anyRequest() .authenticated() .and() .logout() .logoutSuccessUrl("/").permitAll().and().csrf() .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()); } // @formatter:on private Filter ssoFilter() { CompositeFilter filter = new CompositeFilter(); List filters = new ArrayList<>(); OAuth2ClientAuthenticationProcessingFilter facebookFilter = new OAuth2ClientAuthenticationProcessingFilter( "/connect/facebook"); OAuth2RestTemplate facebookTemplate = new OAuth2RestTemplate(facebook(), oauth2ClientContext); facebookFilter.setRestTemplate(facebookTemplate); UserInfoTokenServices tokenServices = new UserInfoTokenServices(facebookResource().getUserInfoUri(), facebook().getClientId()); tokenServices.setRestTemplate(facebookTemplate); facebookFilter.setTokenServices(tokenServices); OAuth2ClientAuthenticationProcessingFilter googleFilter = new OAuth2ClientAuthenticationProcessingFilter( "/connect/google"); OAuth2RestTemplate googleTemplate = new OAuth2RestTemplate(google(), oauth2ClientContext); googleFilter.setRestTemplate(googleTemplate); tokenServices = new UserInfoTokenServices(googleResource().getUserInfoUri(), google().getClientId()); tokenServices.setRestTemplate(googleTemplate); googleFilter.setTokenServices(tokenServices); OAuth2ClientAuthenticationProcessingFilter linkedInFilter = new OAuth2ClientAuthenticationProcessingFilter( "/connect/linkedIn"); OAuth2RestTemplate linkedInTemplate = new OAuth2RestTemplate(linkedIn(), oauth2ClientContext); linkedInFilter.setRestTemplate(linkedInTemplate); tokenServices = new UserInfoTokenServices(linkedInResource().getUserInfoUri(), linkedIn().getClientId()); tokenServices.setRestTemplate(linkedInTemplate); linkedInFilter.setTokenServices(tokenServices); OAuth2ClientAuthenticationProcessingFilter twitterFilter = new OAuth2ClientAuthenticationProcessingFilter( "/connect/twitter"); OAuth2RestTemplate twitterTemplate = new OAuth2RestTemplate(twitter(), oauth2ClientContext); twitterFilter.setRestTemplate(twitterTemplate); tokenServices = new UserInfoTokenServices(twitterResource().getUserInfoUri(), twitter().getClientId()); tokenServices.setRestTemplate(twitterTemplate); twitterFilter.setTokenServices(tokenServices); filters.add(facebookFilter); filters.add(googleFilter); filters.add(linkedInFilter); filters.add(twitterFilter); filter.setFilters(filters); return filter; } @Bean public FilterRegistrationBean oauth2ClientFilterRegistration(OAuth2ClientContextFilter filter) { FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(filter); registration.setOrder(-100); return registration; } @Bean @ConfigurationProperties("facebook.client") public AuthorizationCodeResourceDetails facebook() { return new AuthorizationCodeResourceDetails(); } @Bean @ConfigurationProperties("facebook.resource") public ResourceServerProperties facebookResource() { return new ResourceServerProperties(); } @Bean @ConfigurationProperties("google.client") public AuthorizationCodeResourceDetails google() { return new AuthorizationCodeResourceDetails(); } @Bean @ConfigurationProperties("google.resource") public ResourceServerProperties googleResource() { return new ResourceServerProperties(); } @Bean @ConfigurationProperties("linkedIn.client") public AuthorizationCodeResourceDetails linkedIn() { return new AuthorizationCodeResourceDetails(); } @Bean @ConfigurationProperties("linkedIn.resource") public ResourceServerProperties linkedInResource() { return new ResourceServerProperties(); } @Bean @ConfigurationProperties("twitter.client") public AuthorizationCodeResourceDetails twitter() { return new AuthorizationCodeResourceDetails(); } @Bean @ConfigurationProperties("twitter.resource") public ResourceServerProperties twitterResource() { return new ResourceServerProperties(); } } |
Run the application on localhost:3000, if you have not changed the server.port properties in the file
Above code can be cloned from here: Github Spring OAUTH2 facebook google authorization
Please note that by the time of writing this blog, the twitter and LinkedIn providers were having some issues after authorization. I will fix them as soon as I get some time
Also, I have written a step by step tutorial for authorising user using Spring-social and Spring-security, see link below.
Hi author, how could we get client_id, client_secret from gg, fb or twitter?
Thank you!
http://developers.facebook.com/apps
After creating an app, the App ID / Client ID is shown.
Twitter is not working, getting an error since request token is missing. Is there a way how to fix this issue, so it will work with @EnableOAuth2Client,
Yeah Twitter is not working with the above code as keys are not properly configured. alternatively If you are not using OAuth2 you can look at http://www.littlebigextra.com/part-1-authorising-user-using-spring-social-google-facebook-and-linkedin-and-spring-security/
Twitter doesn’t work
Yeah I am aware that twitter doesn’t work.
Hi ABHI,
How can I use that implementation to integrate android app with spring REST
Thank you
can you provide a example for maven? not Spring Boot