From 7b53c3cb55b110470ae9e4528aef3a165fa1a873 Mon Sep 17 00:00:00 2001 From: Anshul Bansal Date: Tue, 26 May 2020 18:58:17 +0300 Subject: [PATCH] BAEL-3996_Spring_Security_with_Auth0 (#9300) * BAEL-3996 - Spring Security with Auth0 * BAEL-3996 - Spring Security with Auth0 code fixes * BAEL-3996 - Spring Security with Auth0 * BAEL-3996 - removed spaces * BAEL-3996 - RequestMapping is updated to GetMapping --- spring-security-modules/pom.xml | 1 + .../spring-security-auth0/pom.xml | 75 ++++++++++++ .../java/com/baeldung/auth0/Application.java | 13 ++ .../java/com/baeldung/auth0/AuthConfig.java | 114 ++++++++++++++++++ .../auth0/controller/AuthController.java | 77 ++++++++++++ .../auth0/controller/HomeController.java | 37 ++++++ .../auth0/controller/LogoutController.java | 35 ++++++ .../auth0/controller/UserController.java | 57 +++++++++ .../baeldung/auth0/service/ApiService.java | 44 +++++++ .../src/main/resources/application.properties | 7 ++ 10 files changed, 460 insertions(+) create mode 100644 spring-security-modules/spring-security-auth0/pom.xml create mode 100644 spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/Application.java create mode 100644 spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/AuthConfig.java create mode 100644 spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/AuthController.java create mode 100644 spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/HomeController.java create mode 100755 spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/LogoutController.java create mode 100644 spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/UserController.java create mode 100644 spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/service/ApiService.java create mode 100644 spring-security-modules/spring-security-auth0/src/main/resources/application.properties diff --git a/spring-security-modules/pom.xml b/spring-security-modules/pom.xml index 7ce33dd3e3..60a662781f 100644 --- a/spring-security-modules/pom.xml +++ b/spring-security-modules/pom.xml @@ -15,6 +15,7 @@ spring-security-acl + spring-security-auth0 spring-security-angular/server spring-security-cache-control spring-security-core diff --git a/spring-security-modules/spring-security-auth0/pom.xml b/spring-security-modules/spring-security-auth0/pom.xml new file mode 100644 index 0000000000..0bd879a40b --- /dev/null +++ b/spring-security-modules/spring-security-auth0/pom.xml @@ -0,0 +1,75 @@ + + + 4.0.0 + spring-security-auth0 + 1.0-SNAPSHOT + spring-security-auth0 + war + + + com.baeldung + parent-boot-2 + 0.0.1-SNAPSHOT + ../../parent-boot-2 + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.security + spring-security-core + + + org.springframework.security + spring-security-oauth2-resource-server + + + com.auth0 + mvc-auth-commons + ${mvc-auth-commons.version} + + + org.json + json + ${json.version} + + + + + spring-security-auth0 + + + src/main/resources + + + + + org.springframework.boot + spring-boot-maven-plugin + + true + + + + + repackage + + + + + + + + + 20190722 + 1.2.0 + + \ No newline at end of file diff --git a/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/Application.java b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/Application.java new file mode 100644 index 0000000000..42f8d946b5 --- /dev/null +++ b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/Application.java @@ -0,0 +1,13 @@ +package com.baeldung.auth0; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} diff --git a/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/AuthConfig.java b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/AuthConfig.java new file mode 100644 index 0000000000..69cf8b3071 --- /dev/null +++ b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/AuthConfig.java @@ -0,0 +1,114 @@ +package com.baeldung.auth0; + +import java.io.UnsupportedEncodingException; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; + +import com.auth0.AuthenticationController; +import com.baeldung.auth0.controller.LogoutController; +import com.auth0.jwk.JwkProvider; +import com.auth0.jwk.JwkProviderBuilder; + +@Configuration +@EnableWebSecurity +public class AuthConfig extends WebSecurityConfigurerAdapter { + + @Value(value = "${com.auth0.domain}") + private String domain; + + @Value(value = "${com.auth0.clientId}") + private String clientId; + + @Value(value = "${com.auth0.clientSecret}") + private String clientSecret; + + @Value(value = "${com.auth0.managementApi.clientId}") + private String managementApiClientId; + + @Value(value = "${com.auth0.managementApi.clientSecret}") + private String managementApiClientSecret; + + @Value(value = "${com.auth0.managementApi.grantType}") + private String grantType; + + @Bean + public LogoutSuccessHandler logoutSuccessHandler() { + return new LogoutController(); + } + + @Bean + public AuthenticationController authenticationController() throws UnsupportedEncodingException { + JwkProvider jwkProvider = new JwkProviderBuilder(domain).build(); + return AuthenticationController.newBuilder(domain, clientId, clientSecret) + .withJwkProvider(jwkProvider) + .build(); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.csrf().disable(); + http + .authorizeRequests() + .antMatchers("/callback", "/login", "/").permitAll() + .anyRequest().authenticated() + .and() + .formLogin() + .loginPage("/login") + .and() + .logout().logoutSuccessHandler(logoutSuccessHandler()).permitAll(); + } + + public String getDomain() { + return domain; + } + + public String getClientId() { + return clientId; + } + + public String getClientSecret() { + return clientSecret; + } + + public String getManagementApiClientId() { + return managementApiClientId; + } + + public String getManagementApiClientSecret() { + return managementApiClientSecret; + } + + public String getGrantType() { + return grantType; + } + + public String getUserInfoUrl() { + return "https://" + getDomain() + "/userinfo"; + } + + public String getUsersUrl() { + return "https://" + getDomain() + "/api/v2/users"; + } + + public String getUsersByEmailUrl() { + return "https://" + getDomain() + "/api/v2/users-by-email?email="; + } + + public String getLogoutUrl() { + return "https://" + getDomain() +"/v2/logout"; + } + + public String getContextPath(HttpServletRequest request) { + String path = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort(); + return path; + } +} diff --git a/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/AuthController.java b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/AuthController.java new file mode 100644 index 0000000000..48d09db155 --- /dev/null +++ b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/AuthController.java @@ -0,0 +1,77 @@ +package com.baeldung.auth0.controller; + +import java.io.IOException; +import java.util.HashMap; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.json.JSONObject; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.security.authentication.TestingAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.client.RestTemplate; + +import com.auth0.AuthenticationController; +import com.auth0.IdentityVerificationException; +import com.auth0.Tokens; +import com.auth0.jwt.JWT; +import com.auth0.jwt.interfaces.DecodedJWT; +import com.baeldung.auth0.AuthConfig; + +@Controller +public class AuthController { + + @Autowired + private AuthenticationController authenticationController; + + @Autowired + private AuthConfig config; + + private static final String AUTH0_TOKEN_URL = "https://dev-example.auth0.com/oauth/token"; + + @GetMapping(value = "/login") + protected void login(HttpServletRequest request, HttpServletResponse response) throws IOException { + String redirectUri = config.getContextPath(request) + "/callback"; + String authorizeUrl = authenticationController.buildAuthorizeUrl(request, response, redirectUri) + .withScope("openid email") + .build(); + response.sendRedirect(authorizeUrl); + } + + @GetMapping(value="/callback") + public void callback(HttpServletRequest request, HttpServletResponse response) throws IOException, IdentityVerificationException { + Tokens tokens = authenticationController.handle(request, response); + + DecodedJWT jwt = JWT.decode(tokens.getIdToken()); + TestingAuthenticationToken authToken2 = new TestingAuthenticationToken(jwt.getSubject(), jwt.getToken()); + authToken2.setAuthenticated(true); + + SecurityContextHolder.getContext().setAuthentication(authToken2); + response.sendRedirect(config.getContextPath(request) + "/"); + } + + public String getManagementApiToken() { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + JSONObject requestBody = new JSONObject(); + requestBody.put("client_id", config.getManagementApiClientId()); + requestBody.put("client_secret", config.getManagementApiClientSecret()); + requestBody.put("audience", "https://dev-example.auth0.com/api/v2/"); + requestBody.put("grant_type", config.getGrantType()); + + HttpEntity request = new HttpEntity(requestBody.toString(), headers); + + RestTemplate restTemplate = new RestTemplate(); + HashMap result = restTemplate.postForObject(AUTH0_TOKEN_URL, request, HashMap.class); + + return result.get("access_token"); + } + +} diff --git a/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/HomeController.java b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/HomeController.java new file mode 100644 index 0000000000..8a4e650846 --- /dev/null +++ b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/HomeController.java @@ -0,0 +1,37 @@ +package com.baeldung.auth0.controller; + +import java.io.IOException; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.security.authentication.TestingAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.interfaces.DecodedJWT; + +@Controller +public class HomeController { + + @GetMapping(value = "/") + @ResponseBody + public String home(HttpServletRequest request, HttpServletResponse response, final Authentication authentication) throws IOException { + + if (authentication!= null && authentication instanceof TestingAuthenticationToken) { + TestingAuthenticationToken token = (TestingAuthenticationToken) authentication; + + DecodedJWT jwt = JWT.decode(token.getCredentials().toString()); + String email = jwt.getClaims().get("email").asString(); + + return "Welcome, " + email + "!"; + } else { + response.sendRedirect("http://localhost:8080/login"); + return null; + } + } + +} diff --git a/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/LogoutController.java b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/LogoutController.java new file mode 100755 index 0000000000..d508fe2c44 --- /dev/null +++ b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/LogoutController.java @@ -0,0 +1,35 @@ +package com.baeldung.auth0.controller; + +import java.io.IOException; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; +import org.springframework.stereotype.Controller; + +import com.baeldung.auth0.AuthConfig; + +@Controller +public class LogoutController implements LogoutSuccessHandler { + + @Autowired + private AuthConfig config; + + @Override + public void onLogoutSuccess(HttpServletRequest req, HttpServletResponse res, Authentication authentication) { + if (req.getSession() != null) { + req.getSession().invalidate(); + } + String returnTo = config.getContextPath(req); + String logoutUrl = config.getLogoutUrl() + "?client_id=" + config.getClientId() + "&returnTo=" +returnTo; + try { + res.sendRedirect(logoutUrl); + } catch(IOException e){ + e.printStackTrace(); + } + } + +} diff --git a/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/UserController.java b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/UserController.java new file mode 100644 index 0000000000..86601a06d3 --- /dev/null +++ b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/UserController.java @@ -0,0 +1,57 @@ +package com.baeldung.auth0.controller; + +import java.io.IOException; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.json.JSONObject; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import com.auth0.IdentityVerificationException; +import com.baeldung.auth0.AuthConfig; +import com.baeldung.auth0.service.ApiService; + +@Controller +public class UserController { + + @Autowired + private ApiService apiService; + + @Autowired + private AuthConfig config; + + @GetMapping(value="/users") + @ResponseBody + public ResponseEntity users(HttpServletRequest request, HttpServletResponse response) throws IOException, IdentityVerificationException { + ResponseEntity result = apiService.getCall(config.getUsersUrl()); + return result; + } + + @GetMapping(value = "/userByEmail") + @ResponseBody + public ResponseEntity userByEmail(HttpServletResponse response, @RequestParam String email) { + ResponseEntity result = apiService.getCall(config.getUsersByEmailUrl()+email); + return result; + } + + @GetMapping(value = "/createUser") + @ResponseBody + public ResponseEntity createUser(HttpServletResponse response) { + JSONObject request = new JSONObject(); + request.put("email", "norman.lewis@email.com"); + request.put("given_name", "Norman"); + request.put("family_name", "Lewis"); + request.put("connection", "Username-Password-Authentication"); + request.put("password", "Pa33w0rd"); + + ResponseEntity result = apiService.postCall(config.getUsersUrl(), request.toString()); + return result; + } + +} diff --git a/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/service/ApiService.java b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/service/ApiService.java new file mode 100644 index 0000000000..0d8263ae19 --- /dev/null +++ b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/service/ApiService.java @@ -0,0 +1,44 @@ +package com.baeldung.auth0.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import com.baeldung.auth0.controller.AuthController; + +@Service +public class ApiService { + + @Autowired + private AuthController controller; + + public ResponseEntity getCall(String url) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.set("Authorization", "Bearer "+controller.getManagementApiToken()); + + HttpEntity entity = new HttpEntity(headers); + RestTemplate restTemplate = new RestTemplate(); + ResponseEntity result = restTemplate.exchange(url, HttpMethod.GET, entity, String.class); + + return result; + } + + public ResponseEntity postCall(String url, String requestBody) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.set("Authorization", "Bearer "+controller.getManagementApiToken()); + + HttpEntity request = new HttpEntity(requestBody, headers); + RestTemplate restTemplate = new RestTemplate(); + ResponseEntity result = restTemplate.postForEntity(url, request, String.class); + + return result; + } + +} diff --git a/spring-security-modules/spring-security-auth0/src/main/resources/application.properties b/spring-security-modules/spring-security-auth0/src/main/resources/application.properties new file mode 100644 index 0000000000..45492c5c00 --- /dev/null +++ b/spring-security-modules/spring-security-auth0/src/main/resources/application.properties @@ -0,0 +1,7 @@ +com.auth0.domain: dev-example.auth0.com +com.auth0.clientId: exampleClientId +com.auth0.clientSecret: exampleClientSecret + +com.auth0.managementApi.clientId: exampleManagementApiClientId +com.auth0.managementApi.clientSecret: exampleManagementApiClientSecret +com.auth0.managementApi.grantType: client_credentials \ No newline at end of file