Loading OAuth/Dockerfile 0 → 100644 +10 −0 Original line number Diff line number Diff line FROM maven:3.8.3-openjdk-17 AS build WORKDIR /app COPY ./.. /app RUN mvn clean install WORKDIR ./core EXPOSE 8082 CMD ["mvn", "spring-boot:run"] No newline at end of file OAuth/oauthopenapi.yaml 0 → 100644 +1 −0 Original line number Diff line number Diff line openapi: "3.0.3" No newline at end of file OAuth/pom.xml 0 → 100644 +174 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <artifactId>smart-energy-management-system</artifactId> <groupId>cz.muni.fi.pa165</groupId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>OAuth</artifactId> <name>OAuth</name> <description>OIDC Relying Party / OAuth 2 Client implemented in Spring Security</description> <build> <defaultGoal>spring-boot:run</defaultGoal> <!-- name of executable JAR file --> <finalName>confidential_client</finalName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <!-- https://docs.spring.io/spring-boot/docs/current/reference/html/deployment.html#deployment.installing --> <executable>true</executable> </configuration> </plugin> <!-- run integration tests in "mvn verify" phase --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> </plugin> <plugin> <groupId>org.openapitools</groupId> <artifactId>openapi-generator-maven-plugin</artifactId> <executions> <execution> <goals> <goal>generate</goal> </goals> <configuration> <!-- see https://github.com/OpenAPITools/openapi-generator/blob/master/modules/openapi-generator-maven-plugin/README.md --> <inputSpec>${project.basedir}/../openapi.yaml</inputSpec> <generatorName>java</generatorName> <verbose>false</verbose> <generateApiTests>false</generateApiTests> <generateModelTests>false</generateModelTests> <generateApiDocumentation>false</generateApiDocumentation> <generateModelDocumentation>false</generateModelDocumentation> <configOptions> <annotationLibrary>none</annotationLibrary> <!-- see https://openapi-generator.tech/docs/generators/java/ --> <library>native</library> <hideGenerationTimestamp>true</hideGenerationTimestamp> </configOptions> </configuration> </execution> </executions> </plugin> </plugins> </build> <dependencies> <!-- Spring MVC --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Thymeleaf for HTML pages templates --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!-- Thymeleaf layout --> <dependency> <groupId>nz.net.ultraq.thymeleaf</groupId> <artifactId>thymeleaf-layout-dialect</artifactId> </dependency> <!-- validation for forms --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> <!-- for Spring application.yml properties handling --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <!-- OAuth2/OIDC client --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-client</artifactId> </dependency> <!-- web jars for Bootstrap and jQuery --> <dependency> <groupId>org.webjars</groupId> <artifactId>webjars-locator-core</artifactId> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>bootstrap</artifactId> <version>5.2.3</version> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>jquery</artifactId> <version>3.6.4</version> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>js-cookie</artifactId> <version>3.0.1</version> </dependency> <dependency> <groupId>cz.muni.fi.pa165</groupId> <artifactId>models</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency><!-- OpenAPI client --> <dependency> <groupId>io.swagger</groupId> <artifactId>swagger-annotations</artifactId> <version>1.6.10</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> </dependency> <dependency> <groupId>com.google.code.findbugs</groupId> <artifactId>jsr305</artifactId> <version>3.0.2</version> </dependency> <dependency> <groupId>jakarta.annotation</groupId> <artifactId>jakarta.annotation-api</artifactId> <scope>provided</scope> <version>2.1.1</version> </dependency> <dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.2</version> </dependency> <!-- for testing --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency> </dependencies> </project> OAuth/src/main/java/cz/muni/pa165/oauth2/client/MainController.java 0 → 100644 +162 −0 Original line number Diff line number Diff line package cz.muni.pa165.oauth2.client; import com.nimbusds.jose.JWSObject; import com.nimbusds.jose.Payload; import cz.muni.fi.pa165.model.dto.user.UserCreateDto; import org.apache.http.HttpHeaders; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.oauth2.core.oidc.user.OidcUser; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.util.Assert; import org.springframework.web.bind.annotation.GetMapping; import org.apache.http.entity.StringEntity; import java.io.IOException; import java.net.http.HttpClient; import java.text.ParseException; import java.util.Map; import static org.hibernate.validator.internal.util.Contracts.assertTrue; /** * Spring MVC Controller. * Handles HTTP requests by preparing data in model and passing it to Thymeleaf HTML templates. */ @Controller public class MainController { private static final Logger log = LoggerFactory.getLogger(MainController.class); /** * Home page accessible even to non-authenticated users. Displays user personal data. */ @GetMapping("/") public String index(Model model, @AuthenticationPrincipal OidcUser user) { log.debug("********************************************************"); log.debug("* index() called *"); log.debug("********************************************************"); log.debug("user {}", user == null ? "is anonymous" : user.getSubject()); log.debug("token {}", user == null ? "is anonymous" : user.getIdToken().getTokenValue()); // put obtained user data into a model attribute named "user" model.addAttribute("user", user); // put issuer name into a model attribute named "issuerName" if (user != null) { model.addAttribute("issuerName", "https://oidc.muni.cz/oidc/".equals(user.getIssuer().toString()) ? "MUNI" : "Google"); model.addAttribute("token", user.getIdToken().getTokenValue()); } // return the name of a Thymeleaf HTML template that // will be searched in src/main/resources/templates with .html suffix return "index"; } /** * Home page accessible even to non-authenticated users. Displays user personal data. */ @GetMapping("/register") public String register(Model model, @AuthenticationPrincipal OidcUser user) { log.debug("********************************************************"); log.debug("* register() called *"); log.debug("********************************************************"); log.debug("user {}", user == null ? "is anonymous" : user.getSubject()); log.debug("token {}", user == null ? "is anonymous" : user.getIdToken().getTokenValue()); // put obtained user data into a model attribute named "user" model.addAttribute("user", user); // put issuer name into a model attribute named "issuerName" if (user != null) { if (!Registrate(user.getIdToken().getTokenValue())){ return "error"; }; model.addAttribute("issuerName", "https://oidc.muni.cz/oidc/".equals(user.getIssuer().toString()) ? "MUNI" : "Google"); model.addAttribute("token", user.getIdToken().getTokenValue()); } // return the name of a Thymeleaf HTML template that // will be searched in src/main/resources/templates with .html suffix return "index"; } private Boolean Registrate(String token) { String payload = CreatePayload(token); StringEntity entity = new StringEntity(payload, ContentType.APPLICATION_JSON); CloseableHttpClient httpClient = HttpClientBuilder.create().build(); HttpPost request = new HttpPost("http://localhost:8080/api/user"); request.setEntity(entity); request.setHeader("Content-Type", "application/json"); request.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + token); HttpResponse response = null; try{ response = httpClient.execute(request); // assert 201 } catch (Exception e){ return false; } return true; } private String CreatePayload(String token){ UserCreateDto userCreateDto = GetUserCreateDto(token); String payload = String.format(""" { "username": "%s", "email": "%s", "firstName": "%s", "lastName": "%s" } """, userCreateDto.getUsername(), userCreateDto.getEmail(), userCreateDto.getFirstName(), userCreateDto.getLastName()); return payload; } private UserCreateDto GetUserCreateDto(String token){ JWSObject jwsObject = null; try { jwsObject = JWSObject.parse(token); } catch (ParseException e) { throw new RuntimeException(e); } Payload payload = jwsObject.getPayload(); Map<String, Object> jsonObject = payload.toJSONObject(); String email = (String) jsonObject.get("email"); String userName = (String) jsonObject.get("name"); // or preferred_username String firstName = (String) jsonObject.get("given_name"); String lastName = (String) jsonObject.get("family_name"); UserCreateDto userCreateDto = new UserCreateDto(); userCreateDto.setEmail(email); userCreateDto.setUsername(userName); userCreateDto.setFirstName(firstName); userCreateDto.setLastName(lastName); return userCreateDto; } } No newline at end of file OAuth/src/main/java/cz/muni/pa165/oauth2/client/MyWebApp.java 0 → 100644 +136 −0 Original line number Diff line number Diff line package cz.muni.pa165.oauth2.client; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent; import org.springframework.context.annotation.Bean; import org.springframework.context.event.EventListener; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.security.oauth2.client.oidc.web.logout.OidcClientInitiatedLogoutSuccessHandler; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.core.oidc.user.OidcUser; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; import org.springframework.security.web.csrf.CookieCsrfTokenRepository; import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler; import java.io.IOException; @SpringBootApplication public class MyWebApp { private static final Logger log = LoggerFactory.getLogger(MyWebApp.class); public static void main(String[] args) { SpringApplication.run(MyWebApp.class, args); } /** * Configuration of Spring Security. Sets up OAuth2/OIDC authentication * for all URLS except a list of public ones. */ @Bean public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception { httpSecurity .authorizeHttpRequests(x -> x // allow anonymous access to listed URLs .requestMatchers("/", "/error", "/robots.txt", "/style.css", "/favicon.ico", "/webjars/**").permitAll() // all other requests must be authenticated .anyRequest().authenticated() ) .oauth2Login(x -> x // our custom handler for successful logins .successHandler(authenticationSuccessHandler()) ) .logout(x -> x // After we log out, redirect to the root page, by default Spring will send you to /login?logout .logoutSuccessUrl("/") // after local logout, do also remote logout at the OIDC Provider too .logoutSuccessHandler(oidcLogoutSuccessHandler()) ) .csrf(c -> c //set CSRF token cookie "XSRF-TOKEN" with httpOnly=false that can be read by JavaScript .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) //replace the default XorCsrfTokenRequestAttributeHandler with one that can use value from the cookie .csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler()) ) ; return httpSecurity.build(); } /** * Handler called when OIDC login successfully completes. * It extends the default SavedRequestAwareAuthenticationSuccessHandler that saves the access token * to the session. * This handler just prints the available info about user to the log and calls its parent implementation. * @see SavedRequestAwareAuthenticationSuccessHandler */ @Bean public AuthenticationSuccessHandler authenticationSuccessHandler() { return new SavedRequestAwareAuthenticationSuccessHandler() { @Override public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse res, Authentication auth) throws ServletException, IOException { if (auth instanceof OAuth2AuthenticationToken token && token.getPrincipal() instanceof OidcUser user) { log.debug("********************************************************"); log.debug("* user successfully logged in *"); log.debug("********************************************************"); log.info("user.issuer: {}", user.getIssuer()); log.info("user.subject: {}", user.getSubject()); log.info("user.fullName: {}", user.getFullName()); log.info("user.givenName: {}", user.getGivenName()); log.info("user.familyName: {}", user.getFamilyName()); log.info("user.gender: {}", user.getGender()); log.info("user.email: {}", user.getEmail()); log.info("user.locale: {}", user.getLocale()); log.info("user.zoneInfo: {}", user.getZoneInfo()); log.info("user.preferredUsername: {}", user.getPreferredUsername()); log.info("user.issuedAt: {}", user.getIssuedAt()); log.info("user.authenticatedAt: {}", user.getAuthenticatedAt()); log.info("user.claimAsListString(\"eduperson_scoped_affiliation\"): {}", user.getClaimAsStringList("eduperson_scoped_affiliation")); log.info("user.attributes.acr: {}", user.<String>getAttribute("acr")); log.info("user.attributes: {}", user.getAttributes()); log.info("user.authorities: {}", user.getAuthorities()); } super.onAuthenticationSuccess(req, res, auth); } }; } /** * Handler called when local logout successfully completes. * It initiates also a complete remote logout at the Authorization Server. * @see OidcClientInitiatedLogoutSuccessHandler */ private OidcClientInitiatedLogoutSuccessHandler oidcLogoutSuccessHandler() { OidcClientInitiatedLogoutSuccessHandler successHandler = new OidcClientInitiatedLogoutSuccessHandler(clientRegistrationRepository); successHandler.setPostLogoutRedirectUri("http://localhost:8080/"); return successHandler; } @Autowired private ClientRegistrationRepository clientRegistrationRepository; /** * Display a hint in the log. */ @EventListener public void onApplicationEvent(final ServletWebServerInitializedEvent event) { log.info("**************************"); log.info("visit http://localhost:{}/", event.getWebServer().getPort()); log.info("**************************"); } } Loading
OAuth/Dockerfile 0 → 100644 +10 −0 Original line number Diff line number Diff line FROM maven:3.8.3-openjdk-17 AS build WORKDIR /app COPY ./.. /app RUN mvn clean install WORKDIR ./core EXPOSE 8082 CMD ["mvn", "spring-boot:run"] No newline at end of file
OAuth/oauthopenapi.yaml 0 → 100644 +1 −0 Original line number Diff line number Diff line openapi: "3.0.3" No newline at end of file
OAuth/pom.xml 0 → 100644 +174 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <artifactId>smart-energy-management-system</artifactId> <groupId>cz.muni.fi.pa165</groupId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>OAuth</artifactId> <name>OAuth</name> <description>OIDC Relying Party / OAuth 2 Client implemented in Spring Security</description> <build> <defaultGoal>spring-boot:run</defaultGoal> <!-- name of executable JAR file --> <finalName>confidential_client</finalName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <!-- https://docs.spring.io/spring-boot/docs/current/reference/html/deployment.html#deployment.installing --> <executable>true</executable> </configuration> </plugin> <!-- run integration tests in "mvn verify" phase --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> </plugin> <plugin> <groupId>org.openapitools</groupId> <artifactId>openapi-generator-maven-plugin</artifactId> <executions> <execution> <goals> <goal>generate</goal> </goals> <configuration> <!-- see https://github.com/OpenAPITools/openapi-generator/blob/master/modules/openapi-generator-maven-plugin/README.md --> <inputSpec>${project.basedir}/../openapi.yaml</inputSpec> <generatorName>java</generatorName> <verbose>false</verbose> <generateApiTests>false</generateApiTests> <generateModelTests>false</generateModelTests> <generateApiDocumentation>false</generateApiDocumentation> <generateModelDocumentation>false</generateModelDocumentation> <configOptions> <annotationLibrary>none</annotationLibrary> <!-- see https://openapi-generator.tech/docs/generators/java/ --> <library>native</library> <hideGenerationTimestamp>true</hideGenerationTimestamp> </configOptions> </configuration> </execution> </executions> </plugin> </plugins> </build> <dependencies> <!-- Spring MVC --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Thymeleaf for HTML pages templates --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!-- Thymeleaf layout --> <dependency> <groupId>nz.net.ultraq.thymeleaf</groupId> <artifactId>thymeleaf-layout-dialect</artifactId> </dependency> <!-- validation for forms --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> <!-- for Spring application.yml properties handling --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <!-- OAuth2/OIDC client --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-client</artifactId> </dependency> <!-- web jars for Bootstrap and jQuery --> <dependency> <groupId>org.webjars</groupId> <artifactId>webjars-locator-core</artifactId> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>bootstrap</artifactId> <version>5.2.3</version> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>jquery</artifactId> <version>3.6.4</version> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>js-cookie</artifactId> <version>3.0.1</version> </dependency> <dependency> <groupId>cz.muni.fi.pa165</groupId> <artifactId>models</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency><!-- OpenAPI client --> <dependency> <groupId>io.swagger</groupId> <artifactId>swagger-annotations</artifactId> <version>1.6.10</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> </dependency> <dependency> <groupId>com.google.code.findbugs</groupId> <artifactId>jsr305</artifactId> <version>3.0.2</version> </dependency> <dependency> <groupId>jakarta.annotation</groupId> <artifactId>jakarta.annotation-api</artifactId> <scope>provided</scope> <version>2.1.1</version> </dependency> <dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.2</version> </dependency> <!-- for testing --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency> </dependencies> </project>
OAuth/src/main/java/cz/muni/pa165/oauth2/client/MainController.java 0 → 100644 +162 −0 Original line number Diff line number Diff line package cz.muni.pa165.oauth2.client; import com.nimbusds.jose.JWSObject; import com.nimbusds.jose.Payload; import cz.muni.fi.pa165.model.dto.user.UserCreateDto; import org.apache.http.HttpHeaders; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.oauth2.core.oidc.user.OidcUser; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.util.Assert; import org.springframework.web.bind.annotation.GetMapping; import org.apache.http.entity.StringEntity; import java.io.IOException; import java.net.http.HttpClient; import java.text.ParseException; import java.util.Map; import static org.hibernate.validator.internal.util.Contracts.assertTrue; /** * Spring MVC Controller. * Handles HTTP requests by preparing data in model and passing it to Thymeleaf HTML templates. */ @Controller public class MainController { private static final Logger log = LoggerFactory.getLogger(MainController.class); /** * Home page accessible even to non-authenticated users. Displays user personal data. */ @GetMapping("/") public String index(Model model, @AuthenticationPrincipal OidcUser user) { log.debug("********************************************************"); log.debug("* index() called *"); log.debug("********************************************************"); log.debug("user {}", user == null ? "is anonymous" : user.getSubject()); log.debug("token {}", user == null ? "is anonymous" : user.getIdToken().getTokenValue()); // put obtained user data into a model attribute named "user" model.addAttribute("user", user); // put issuer name into a model attribute named "issuerName" if (user != null) { model.addAttribute("issuerName", "https://oidc.muni.cz/oidc/".equals(user.getIssuer().toString()) ? "MUNI" : "Google"); model.addAttribute("token", user.getIdToken().getTokenValue()); } // return the name of a Thymeleaf HTML template that // will be searched in src/main/resources/templates with .html suffix return "index"; } /** * Home page accessible even to non-authenticated users. Displays user personal data. */ @GetMapping("/register") public String register(Model model, @AuthenticationPrincipal OidcUser user) { log.debug("********************************************************"); log.debug("* register() called *"); log.debug("********************************************************"); log.debug("user {}", user == null ? "is anonymous" : user.getSubject()); log.debug("token {}", user == null ? "is anonymous" : user.getIdToken().getTokenValue()); // put obtained user data into a model attribute named "user" model.addAttribute("user", user); // put issuer name into a model attribute named "issuerName" if (user != null) { if (!Registrate(user.getIdToken().getTokenValue())){ return "error"; }; model.addAttribute("issuerName", "https://oidc.muni.cz/oidc/".equals(user.getIssuer().toString()) ? "MUNI" : "Google"); model.addAttribute("token", user.getIdToken().getTokenValue()); } // return the name of a Thymeleaf HTML template that // will be searched in src/main/resources/templates with .html suffix return "index"; } private Boolean Registrate(String token) { String payload = CreatePayload(token); StringEntity entity = new StringEntity(payload, ContentType.APPLICATION_JSON); CloseableHttpClient httpClient = HttpClientBuilder.create().build(); HttpPost request = new HttpPost("http://localhost:8080/api/user"); request.setEntity(entity); request.setHeader("Content-Type", "application/json"); request.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + token); HttpResponse response = null; try{ response = httpClient.execute(request); // assert 201 } catch (Exception e){ return false; } return true; } private String CreatePayload(String token){ UserCreateDto userCreateDto = GetUserCreateDto(token); String payload = String.format(""" { "username": "%s", "email": "%s", "firstName": "%s", "lastName": "%s" } """, userCreateDto.getUsername(), userCreateDto.getEmail(), userCreateDto.getFirstName(), userCreateDto.getLastName()); return payload; } private UserCreateDto GetUserCreateDto(String token){ JWSObject jwsObject = null; try { jwsObject = JWSObject.parse(token); } catch (ParseException e) { throw new RuntimeException(e); } Payload payload = jwsObject.getPayload(); Map<String, Object> jsonObject = payload.toJSONObject(); String email = (String) jsonObject.get("email"); String userName = (String) jsonObject.get("name"); // or preferred_username String firstName = (String) jsonObject.get("given_name"); String lastName = (String) jsonObject.get("family_name"); UserCreateDto userCreateDto = new UserCreateDto(); userCreateDto.setEmail(email); userCreateDto.setUsername(userName); userCreateDto.setFirstName(firstName); userCreateDto.setLastName(lastName); return userCreateDto; } } No newline at end of file
OAuth/src/main/java/cz/muni/pa165/oauth2/client/MyWebApp.java 0 → 100644 +136 −0 Original line number Diff line number Diff line package cz.muni.pa165.oauth2.client; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent; import org.springframework.context.annotation.Bean; import org.springframework.context.event.EventListener; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.security.oauth2.client.oidc.web.logout.OidcClientInitiatedLogoutSuccessHandler; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.core.oidc.user.OidcUser; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; import org.springframework.security.web.csrf.CookieCsrfTokenRepository; import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler; import java.io.IOException; @SpringBootApplication public class MyWebApp { private static final Logger log = LoggerFactory.getLogger(MyWebApp.class); public static void main(String[] args) { SpringApplication.run(MyWebApp.class, args); } /** * Configuration of Spring Security. Sets up OAuth2/OIDC authentication * for all URLS except a list of public ones. */ @Bean public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception { httpSecurity .authorizeHttpRequests(x -> x // allow anonymous access to listed URLs .requestMatchers("/", "/error", "/robots.txt", "/style.css", "/favicon.ico", "/webjars/**").permitAll() // all other requests must be authenticated .anyRequest().authenticated() ) .oauth2Login(x -> x // our custom handler for successful logins .successHandler(authenticationSuccessHandler()) ) .logout(x -> x // After we log out, redirect to the root page, by default Spring will send you to /login?logout .logoutSuccessUrl("/") // after local logout, do also remote logout at the OIDC Provider too .logoutSuccessHandler(oidcLogoutSuccessHandler()) ) .csrf(c -> c //set CSRF token cookie "XSRF-TOKEN" with httpOnly=false that can be read by JavaScript .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) //replace the default XorCsrfTokenRequestAttributeHandler with one that can use value from the cookie .csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler()) ) ; return httpSecurity.build(); } /** * Handler called when OIDC login successfully completes. * It extends the default SavedRequestAwareAuthenticationSuccessHandler that saves the access token * to the session. * This handler just prints the available info about user to the log and calls its parent implementation. * @see SavedRequestAwareAuthenticationSuccessHandler */ @Bean public AuthenticationSuccessHandler authenticationSuccessHandler() { return new SavedRequestAwareAuthenticationSuccessHandler() { @Override public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse res, Authentication auth) throws ServletException, IOException { if (auth instanceof OAuth2AuthenticationToken token && token.getPrincipal() instanceof OidcUser user) { log.debug("********************************************************"); log.debug("* user successfully logged in *"); log.debug("********************************************************"); log.info("user.issuer: {}", user.getIssuer()); log.info("user.subject: {}", user.getSubject()); log.info("user.fullName: {}", user.getFullName()); log.info("user.givenName: {}", user.getGivenName()); log.info("user.familyName: {}", user.getFamilyName()); log.info("user.gender: {}", user.getGender()); log.info("user.email: {}", user.getEmail()); log.info("user.locale: {}", user.getLocale()); log.info("user.zoneInfo: {}", user.getZoneInfo()); log.info("user.preferredUsername: {}", user.getPreferredUsername()); log.info("user.issuedAt: {}", user.getIssuedAt()); log.info("user.authenticatedAt: {}", user.getAuthenticatedAt()); log.info("user.claimAsListString(\"eduperson_scoped_affiliation\"): {}", user.getClaimAsStringList("eduperson_scoped_affiliation")); log.info("user.attributes.acr: {}", user.<String>getAttribute("acr")); log.info("user.attributes: {}", user.getAttributes()); log.info("user.authorities: {}", user.getAuthorities()); } super.onAuthenticationSuccess(req, res, auth); } }; } /** * Handler called when local logout successfully completes. * It initiates also a complete remote logout at the Authorization Server. * @see OidcClientInitiatedLogoutSuccessHandler */ private OidcClientInitiatedLogoutSuccessHandler oidcLogoutSuccessHandler() { OidcClientInitiatedLogoutSuccessHandler successHandler = new OidcClientInitiatedLogoutSuccessHandler(clientRegistrationRepository); successHandler.setPostLogoutRedirectUri("http://localhost:8080/"); return successHandler; } @Autowired private ClientRegistrationRepository clientRegistrationRepository; /** * Display a hint in the log. */ @EventListener public void onApplicationEvent(final ServletWebServerInitializedEvent event) { log.info("**************************"); log.info("visit http://localhost:{}/", event.getWebServer().getPort()); log.info("**************************"); } }