├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── src ├── main │ ├── resources │ │ ├── keycloak.json.bak │ │ └── application.yml │ └── java │ │ └── com │ │ └── broodcamp │ │ ├── business │ │ └── exception │ │ │ └── CustomerNotFoundException.java │ │ ├── data │ │ ├── repository │ │ │ └── CustomerRepository.java │ │ ├── LoadDatabase.java │ │ └── entity │ │ │ └── Customer.java │ │ ├── KeycloakSpringApplication.java │ │ ├── web │ │ ├── application │ │ │ ├── AdminController.java │ │ │ ├── UserController.java │ │ │ └── CustomerController.java │ │ └── assembler │ │ │ └── CustomerResourceAssembler.java │ │ ├── utils │ │ └── SecurityContextUtils.java │ │ └── SecurityConfig.java └── test │ └── java │ └── com │ └── broodcamp │ └── KeycloakSpringApplicationTests.java ├── .github └── FUNDING.yml ├── README.md ├── pom.xml └── config └── primereact-app-realm.json /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/czetsuya/Spring-Keycloak-with-REST-API/HEAD/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip 2 | -------------------------------------------------------------------------------- /src/main/resources/keycloak.json.bak: -------------------------------------------------------------------------------- 1 | { 2 | "realm": "primereact-app", 3 | "bearer-only": true, 4 | "auth-server-url": "http://192.168.1.6:8080/auth", 5 | "ssl-required": "external", 6 | "resource": "springboot-rest-api", 7 | "verify-token-audience": true, 8 | "credentials": { 9 | "secret": "371a34c9-9c8c-4aa5-a6b1-ee338c5454b2" 10 | }, 11 | "confidential-port": 0, 12 | "policy-enforcer": {} 13 | } -------------------------------------------------------------------------------- /src/main/java/com/broodcamp/business/exception/CustomerNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.broodcamp.business.exception; 2 | 3 | /** 4 | * @author Edward P. Legaspi 5 | */ 6 | public class CustomerNotFoundException extends RuntimeException { 7 | 8 | private static final long serialVersionUID = -3310173336873980506L; 9 | 10 | public CustomerNotFoundException(Long id) { 11 | super("Could not find customer " + id); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/broodcamp/data/repository/CustomerRepository.java: -------------------------------------------------------------------------------- 1 | package com.broodcamp.data.repository; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.stereotype.Repository; 5 | 6 | import com.broodcamp.data.entity.Customer; 7 | 8 | /** 9 | * @author Edward P. Legaspi 10 | */ 11 | @Repository 12 | public interface CustomerRepository extends JpaRepository { 13 | } 14 | -------------------------------------------------------------------------------- /src/test/java/com/broodcamp/KeycloakSpringApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.broodcamp; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class KeycloakSpringApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | 18 | -------------------------------------------------------------------------------- /src/main/java/com/broodcamp/KeycloakSpringApplication.java: -------------------------------------------------------------------------------- 1 | package com.broodcamp; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | /** 7 | * @author Edward P. Legaspi 8 | */ 9 | @SpringBootApplication 10 | public class KeycloakSpringApplication { 11 | 12 | public static void main(String[] args) { 13 | SpringApplication.run(KeycloakSpringApplication.class, args); 14 | } 15 | 16 | } 17 | 18 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: czetsuya 4 | patreon: 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: 13 | -------------------------------------------------------------------------------- /src/main/java/com/broodcamp/web/application/AdminController.java: -------------------------------------------------------------------------------- 1 | package com.broodcamp.web.application; 2 | 3 | import org.springframework.web.bind.annotation.GetMapping; 4 | import org.springframework.web.bind.annotation.RequestMapping; 5 | import org.springframework.web.bind.annotation.RestController; 6 | 7 | /** 8 | * @author Edward P. Legaspi 9 | */ 10 | @RestController 11 | @RequestMapping(path = "/v1/admin") 12 | //@PreAuthorize("hasRole('ADMIN')") 13 | public class AdminController { 14 | 15 | @GetMapping(path = "") 16 | public String index() { 17 | return "Admin"; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8080 3 | keycloak: 4 | enabled: true 5 | realm: primereact-app 6 | auth-server-url: http://192.168.1.101:8080/auth 7 | ssl-required: none 8 | resource: springboot-rest-api 9 | bearer-only: true 10 | confidential-port: 0 11 | use-resource-role-mappings: false 12 | principal-attribute: preferred_username 13 | cors: true 14 | security-constraints: 15 | - auth-roles: 16 | - User 17 | security-collections: 18 | - name: unsecured 19 | patterns: 20 | - /v1/users 21 | - auth-roles: 22 | - Admin 23 | security-collections: 24 | - name: secured 25 | patterns: 26 | - /v1/admin 27 | management: 28 | endpoints: 29 | web: 30 | exposure: 31 | include: 32 | - '*' 33 | springdoc: 34 | api-docs: 35 | path: /api-docs 36 | swagger-ui: 37 | path: /swagger-docs 38 | 39 | -------------------------------------------------------------------------------- /src/main/java/com/broodcamp/web/assembler/CustomerResourceAssembler.java: -------------------------------------------------------------------------------- 1 | package com.broodcamp.web.assembler; 2 | 3 | import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo; 4 | import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn; 5 | 6 | import org.springframework.hateoas.EntityModel; 7 | import org.springframework.hateoas.server.RepresentationModelAssembler; 8 | import org.springframework.stereotype.Component; 9 | 10 | import com.broodcamp.data.entity.Customer; 11 | import com.broodcamp.web.application.CustomerController; 12 | 13 | /** 14 | * @author Edward P. Legaspi 15 | */ 16 | @Component 17 | public class CustomerResourceAssembler implements RepresentationModelAssembler> { 18 | 19 | @Override 20 | public EntityModel toModel(Customer customer) { 21 | 22 | return new EntityModel<>(customer, linkTo(methodOn(CustomerController.class).one(customer.getId())).withSelfRel(), 23 | linkTo(methodOn(CustomerController.class).all()).withRel("/v1/customers")); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/broodcamp/data/LoadDatabase.java: -------------------------------------------------------------------------------- 1 | package com.broodcamp.data; 2 | 3 | import org.springframework.boot.CommandLineRunner; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | import com.broodcamp.data.entity.Customer; 8 | import com.broodcamp.data.repository.CustomerRepository; 9 | 10 | import lombok.extern.slf4j.Slf4j; 11 | 12 | /** 13 | * @author Edward P. Legaspi 14 | */ 15 | @Configuration 16 | @Slf4j 17 | public class LoadDatabase { 18 | 19 | @Bean 20 | CommandLineRunner initDatabase(final CustomerRepository customerRepository) { 21 | return args -> { 22 | log.debug("Preloading 6 customers"); 23 | customerRepository.save(new Customer("Kira Yamato", 21, "kira@gundam.com")); 24 | customerRepository.save(new Customer("Aerith Gainsborough", 21, "aerith@ffvii.com")); 25 | customerRepository.save(new Customer("Tifa Lockheart", 21, "tifa@ffvii.com")); 26 | customerRepository.save(new Customer("Garnet Til Alexandros", 21, "tifa@ffix.com")); 27 | customerRepository.save(new Customer("Terra Branford", 21, "terra@ffvi.com")); 28 | }; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/broodcamp/web/application/UserController.java: -------------------------------------------------------------------------------- 1 | package com.broodcamp.web.application; 2 | 3 | import java.util.Set; 4 | 5 | import org.springframework.http.ResponseEntity; 6 | import org.springframework.security.access.prepost.PreAuthorize; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | import com.broodcamp.utils.SecurityContextUtils; 12 | 13 | /** 14 | * @author Edward P. Legaspi 15 | */ 16 | @RestController 17 | @RequestMapping(path = "/v1/users") 18 | //@PreAuthorize("hasRole('ADMIN') or hasRole('USER')") 19 | public class UserController { 20 | 21 | @GetMapping(path = "") 22 | public String index() { 23 | return "Users"; 24 | } 25 | 26 | @GetMapping(path = "/user") 27 | @PreAuthorize("hasAnyAuthority('USER')") 28 | public ResponseEntity getAuthorizedUserName() { 29 | return ResponseEntity.ok(SecurityContextUtils.getUserName()); 30 | } 31 | 32 | @GetMapping(path = "/roles") 33 | @PreAuthorize("hasAnyAuthority('USER')") 34 | public ResponseEntity> getAuthorizedUserRoles() { 35 | return ResponseEntity.ok(SecurityContextUtils.getUserRoles()); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/broodcamp/data/entity/Customer.java: -------------------------------------------------------------------------------- 1 | package com.broodcamp.data.entity; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.GeneratedValue; 5 | import javax.persistence.GenerationType; 6 | import javax.persistence.Id; 7 | import javax.persistence.Transient; 8 | import javax.validation.constraints.Min; 9 | import javax.validation.constraints.NotBlank; 10 | import javax.validation.constraints.Size; 11 | 12 | import lombok.AllArgsConstructor; 13 | import lombok.Data; 14 | import lombok.NoArgsConstructor; 15 | 16 | /** 17 | * @author Edward P. Legaspi 18 | */ 19 | @Entity 20 | @AllArgsConstructor 21 | @NoArgsConstructor 22 | @Data 23 | public class Customer { 24 | 25 | @Id 26 | @GeneratedValue(strategy = GenerationType.AUTO) 27 | private Long id; 28 | 29 | @NotBlank 30 | @Size(min = 5, max = 255) 31 | private String name; 32 | 33 | @NotBlank 34 | @Size(min = 5, max = 255) 35 | private String email; 36 | 37 | @Min(value = 18) 38 | private int age; 39 | 40 | /** 41 | * Because id is being hidden by Spring REST Data JPA 42 | */ 43 | @Transient 44 | private Long entityId; 45 | 46 | public Customer(String name, int age, String email) { 47 | this.name = name; 48 | this.age = age; 49 | this.email = email; 50 | } 51 | 52 | public Long getEntityId() { 53 | return id; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) 2 | 3 | # Spring Project Secured with Keycloak 4 | 5 | A demo project created to demonstrate how a Spring project can be secured using a Keycloak server via bearer token. 6 | 7 | SpringDoc URLs: 8 | 9 | - springdoc.api-docs=/api-docs 10 | - springdoc.swagger-ui.path=/swagger-ui.html 11 | 12 | ## Requirements: 13 | 14 | - Keycloak server 10.0.1 15 | - https://github.com/czetsuya/keycloak-react 16 | 17 | *In case you will be using Keycloak version greater than 7.0.0, the Keycloak team has introduced the notion of feature and uploading the json file has become one of them. Sadly, it is disabled by default. To enable realm, upload run Keycloak with the given parameter: 18 | 19 | ``` 20 | standalone.bat -Djboss.socket.binding.port-offset=1 -Dkeycloak.profile.feature.upload_scripts=enabled 21 | ``` 22 | 23 | Here's the documentation: https://www.keycloak.org/docs/latest/server_installation/, look at the profiles section. 24 | 25 | ## Running Keycloak in Docker 26 | 27 | To run Keycloak as a docker container and enable the realm configuration upload, the keycloak.profile.feature.upload_scripts must be set. 28 | 29 | ```sh 30 | docker run --name=keycloak10 -d -p 8080:8080 -e JAVA_OPTS="-Dkeycloak.profile.feature.scripts=enabled -Dkeycloak.profile.feature.upload_scripts=enabled" -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=kerri jboss/keycloak 31 | ``` 32 | 33 | ## Running Keycloak as Standalone 34 | 35 | Import the realm file inside the config folder. If you're not familiar with the installation process, visit the blog I have written in the reference section below. 36 | 37 | When you import the realm it will also create the users below. 38 | 39 | ### Users 40 | 41 | Role=User, kerri / kerri 42 | Role=Admin, admin / admin 43 | 44 | 45 | ## Note: 46 | 47 | - Make sure that you have the same Keycloak client credentials value for the 2 project ands Keycloak server. 48 | 49 | If keycloak.json file is to be used instead of application.yml, set the following system variable and make sure that you have the file keycloak.json in src/main/resources. 50 | 51 | keycloak.configurationFile = classpath:keycloak.json 52 | 53 | ## References 54 | 55 | - https://czetsuya-tech.blogspot.com/2020/02/download-and-configure-keycloak-sso-server.html 56 | - https://github.com/springdoc/springdoc-openapi-maven-plugin 57 | 58 | ## Authors 59 | 60 | * **Edward P. Legaspi** - *Java Architect* - [czetsuya](https://github.com/czetsuya) 61 | -------------------------------------------------------------------------------- /src/main/java/com/broodcamp/utils/SecurityContextUtils.java: -------------------------------------------------------------------------------- 1 | package com.broodcamp.utils; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | 6 | import org.keycloak.KeycloakPrincipal; 7 | import org.keycloak.KeycloakSecurityContext; 8 | import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken; 9 | import org.springframework.security.core.Authentication; 10 | import org.springframework.security.core.context.SecurityContext; 11 | import org.springframework.security.core.context.SecurityContextHolder; 12 | import org.springframework.security.core.userdetails.UserDetails; 13 | import org.springframework.stereotype.Component; 14 | 15 | import lombok.extern.slf4j.Slf4j; 16 | 17 | /** 18 | * @author Edward P. Legaspi 19 | */ 20 | @Component 21 | @Slf4j 22 | public class SecurityContextUtils { 23 | 24 | private static final String ANONYMOUS = "anonymous"; 25 | 26 | private SecurityContextUtils() { 27 | } 28 | 29 | public static String getUserName() { 30 | 31 | SecurityContext securityContext = SecurityContextHolder.getContext(); 32 | Authentication authentication = securityContext.getAuthentication(); 33 | String username = ANONYMOUS; 34 | 35 | if (null != authentication) { 36 | if (authentication.getPrincipal() instanceof UserDetails) { 37 | UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal(); 38 | username = springSecurityUser.getUsername(); 39 | 40 | } else if (authentication.getPrincipal() instanceof String) { 41 | username = (String) authentication.getPrincipal(); 42 | 43 | } else { 44 | log.debug("User details not found in Security Context"); 45 | } 46 | 47 | } else { 48 | log.debug("Request not authenticated, hence no user name available"); 49 | } 50 | 51 | return username; 52 | } 53 | 54 | public static Set getUserRoles() { 55 | 56 | SecurityContext securityContext = SecurityContextHolder.getContext(); 57 | Authentication authentication = securityContext.getAuthentication(); 58 | Set roles = new HashSet<>(); 59 | 60 | if (null != authentication) { 61 | authentication.getAuthorities().forEach(e -> roles.add(e.getAuthority())); 62 | } 63 | 64 | return roles; 65 | } 66 | 67 | @SuppressWarnings("unchecked") 68 | public static String getJWTToken() { 69 | 70 | KeycloakAuthenticationToken authentication = (KeycloakAuthenticationToken) SecurityContextHolder.getContext().getAuthentication(); 71 | 72 | KeycloakPrincipal keycloakPrincipal = (KeycloakPrincipal) authentication.getPrincipal(); 73 | 74 | return keycloakPrincipal.getKeycloakSecurityContext().getTokenString(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/com/broodcamp/web/application/CustomerController.java: -------------------------------------------------------------------------------- 1 | package com.broodcamp.web.application; 2 | 3 | import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo; 4 | import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn; 5 | 6 | import java.net.URISyntaxException; 7 | import java.util.List; 8 | import java.util.stream.Collectors; 9 | 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.hateoas.CollectionModel; 12 | import org.springframework.hateoas.EntityModel; 13 | import org.springframework.http.ResponseEntity; 14 | import org.springframework.web.bind.annotation.DeleteMapping; 15 | import org.springframework.web.bind.annotation.GetMapping; 16 | import org.springframework.web.bind.annotation.PathVariable; 17 | import org.springframework.web.bind.annotation.PostMapping; 18 | import org.springframework.web.bind.annotation.PutMapping; 19 | import org.springframework.web.bind.annotation.RequestBody; 20 | import org.springframework.web.bind.annotation.RequestMapping; 21 | import org.springframework.web.bind.annotation.RestController; 22 | 23 | import com.broodcamp.business.exception.CustomerNotFoundException; 24 | import com.broodcamp.data.entity.Customer; 25 | import com.broodcamp.data.repository.CustomerRepository; 26 | import com.broodcamp.web.assembler.CustomerResourceAssembler; 27 | 28 | @RestController 29 | @RequestMapping("/v1/customers") 30 | public class CustomerController { 31 | 32 | @Autowired 33 | private CustomerRepository repository; 34 | 35 | @Autowired 36 | private CustomerResourceAssembler assembler; 37 | 38 | /** 39 | * Get all the customer available in the underlying system 40 | * 41 | * @return list of customers 42 | */ 43 | @GetMapping(path = "") 44 | public CollectionModel> all() { 45 | List> entities = repository.findAll().stream().map(assembler::toModel).collect(Collectors.toList()); 46 | 47 | return new CollectionModel<>(entities, linkTo(methodOn(CustomerController.class).all()).withSelfRel()); 48 | } 49 | 50 | /** 51 | * Create a customer with the system.This end point accepts customer information 52 | * in the json format.It will create and send back the data to the REST 53 | * customer. 54 | * 55 | * @param newCustomer 56 | * @return newly created customer 57 | * @throws URISyntaxException 58 | */ 59 | @PostMapping(path = "") 60 | public ResponseEntity> newCustomer(@RequestBody Customer newCustomer) throws URISyntaxException { 61 | 62 | EntityModel resource = assembler.toModel(repository.save(newCustomer)); 63 | return ResponseEntity.created(linkTo(CustomerController.class).slash(resource.getContent().getId()).withSelfRel().toUri()).body(resource); 64 | } 65 | 66 | @PutMapping(path = "/{id}") 67 | public ResponseEntity replaceCustomer(@RequestBody Customer newCustomer, @PathVariable Long id) throws URISyntaxException { 68 | 69 | Customer updatedEmployee = repository.findById(id).map(employee -> { 70 | employee.setName(newCustomer.getName()); 71 | employee.setAge(newCustomer.getAge()); 72 | employee.setEmail(newCustomer.getEmail()); 73 | return repository.save(employee); 74 | }).orElseGet(() -> { 75 | newCustomer.setId(id); 76 | return repository.save(newCustomer); 77 | }); 78 | 79 | EntityModel resource = assembler.toModel(updatedEmployee); 80 | 81 | try { 82 | Thread.sleep(5000); 83 | 84 | } catch (InterruptedException e) { 85 | Thread.currentThread().interrupt(); 86 | } 87 | 88 | return ResponseEntity.ok().body(resource); 89 | } 90 | 91 | /** 92 | * Deleted the customer from the system.client will pass the ID for the customer 93 | * and this end point will remove customer from the system if found. 94 | * 95 | * @param id 96 | * @return 97 | */ 98 | @DeleteMapping(path = "/{id}") 99 | public ResponseEntity deleteCustomer(@PathVariable Long id) { 100 | repository.deleteById(id); 101 | return ResponseEntity.noContent().build(); 102 | } 103 | 104 | /** 105 | * Get the customer detail based on the id passed by the client API. 106 | * 107 | * @param id 108 | * @return customer detail 109 | */ 110 | @GetMapping(path = "/{id}") 111 | public EntityModel one(@PathVariable Long id) { 112 | 113 | Customer entity = repository.findById(id).orElseThrow(() -> new CustomerNotFoundException(id)); 114 | return assembler.toModel(entity); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.springframework.boot 8 | spring-boot-starter-parent 9 | 2.3.0.RELEASE 10 | 11 | 12 | com.broodcamp 13 | keycloak-spring 14 | 0.0.1-SNAPSHOT 15 | keycloak-spring 16 | Keycloak Spring 17 | 18 | 19 | 11 20 | 6.0.1 21 | 22 | 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-actuator 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-security 35 | 36 | 37 | org.keycloak 38 | keycloak-spring-boot-starter 39 | 40 | 41 | org.springframework.data 42 | spring-data-rest-hal-browser 43 | 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-starter-data-jpa 48 | 49 | 50 | org.springframework.boot 51 | spring-boot-starter-data-rest 52 | 53 | 54 | org.springframework.boot 55 | spring-boot-starter-hateoas 56 | 57 | 58 | org.springdoc 59 | springdoc-openapi-ui 60 | 1.2.6 61 | 62 | 63 | org.projectlombok 64 | lombok 65 | provided 66 | 67 | 68 | javax.xml.bind 69 | jaxb-api 70 | 71 | 72 | com.sun.xml.bind 73 | jaxb-impl 74 | 2.3.1 75 | 76 | 77 | com.sun.xml.bind 78 | jaxb-core 79 | 2.3.0.1 80 | 81 | 82 | 83 | com.h2database 84 | h2 85 | runtime 86 | 87 | 88 | org.springframework.boot 89 | spring-boot-devtools 90 | runtime 91 | 92 | 93 | org.springframework.boot 94 | spring-boot-starter-test 95 | test 96 | 97 | 98 | 99 | 100 | 101 | 102 | org.keycloak.bom 103 | keycloak-adapter-bom 104 | ${keycloak.version} 105 | pom 106 | import 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | org.springframework.boot 115 | spring-boot-maven-plugin 116 | 117 | 118 | pre-integration-test 119 | 120 | start 121 | 122 | 123 | 124 | post-integration-test 125 | 126 | stop 127 | 128 | 129 | 130 | 131 | 132 | org.springdoc 133 | springdoc-openapi-maven-plugin 134 | 0.2 135 | 136 | http://localhost:8080/api-docs 137 | openapi.json 138 | ${project.build.directory} 139 | 140 | 141 | 142 | integration-test 143 | 144 | generate 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | Edward P. Legaspi <czetsuya@gmail.com> 154 | 155 | https://github.com/czetsuya 156 | 157 | -------------------------------------------------------------------------------- /src/main/java/com/broodcamp/SecurityConfig.java: -------------------------------------------------------------------------------- 1 | package com.broodcamp; 2 | 3 | import org.keycloak.adapters.KeycloakConfigResolver; 4 | import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver; 5 | import org.keycloak.adapters.springsecurity.KeycloakConfiguration; 6 | import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider; 7 | import org.keycloak.adapters.springsecurity.client.KeycloakClientRequestFactory; 8 | import org.keycloak.adapters.springsecurity.client.KeycloakRestTemplate; 9 | import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter; 10 | import org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticatedActionsFilter; 11 | import org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter; 12 | import org.keycloak.adapters.springsecurity.filter.KeycloakPreAuthActionsFilter; 13 | import org.keycloak.adapters.springsecurity.filter.KeycloakSecurityContextRequestFilter; 14 | import org.keycloak.adapters.springsecurity.management.HttpSessionManager; 15 | import org.springframework.beans.factory.annotation.Autowired; 16 | import org.springframework.beans.factory.config.ConfigurableBeanFactory; 17 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 18 | import org.springframework.boot.web.servlet.FilterRegistrationBean; 19 | import org.springframework.context.annotation.Bean; 20 | import org.springframework.context.annotation.Scope; 21 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 22 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; 23 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 24 | import org.springframework.security.config.http.SessionCreationPolicy; 25 | import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper; 26 | import org.springframework.security.core.context.SecurityContextHolder; 27 | import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy; 28 | import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; 29 | 30 | /** 31 | * @author Edward P. Legaspi 32 | */ 33 | @KeycloakConfiguration 34 | @EnableGlobalMethodSecurity(prePostEnabled = true) 35 | public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter { 36 | 37 | private final KeycloakClientRequestFactory keycloakClientRequestFactory; 38 | 39 | public SecurityConfig(KeycloakClientRequestFactory keycloakClientRequestFactory) { 40 | this.keycloakClientRequestFactory = keycloakClientRequestFactory; 41 | 42 | // to use principal and authentication together with @async 43 | SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL); 44 | } 45 | 46 | @Bean 47 | @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) 48 | public KeycloakRestTemplate keycloakRestTemplate() { 49 | return new KeycloakRestTemplate(keycloakClientRequestFactory); 50 | } 51 | 52 | public SimpleAuthorityMapper grantedAuthority() { 53 | SimpleAuthorityMapper mapper = new SimpleAuthorityMapper(); 54 | mapper.setConvertToUpperCase(true); 55 | return mapper; 56 | } 57 | 58 | @Autowired 59 | public void configureGlobal(AuthenticationManagerBuilder auth) { 60 | KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider(); 61 | keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(grantedAuthority()); 62 | auth.authenticationProvider(keycloakAuthenticationProvider); 63 | } 64 | 65 | @Bean 66 | public KeycloakConfigResolver keycloakConfigResolver() { 67 | return new KeycloakSpringBootConfigResolver(); 68 | } 69 | 70 | /** 71 | * Use NullAuthenticatedSessionStrategy for bearer-only tokens. Otherwise, use 72 | * RegisterSessionAuthenticationStrategy. 73 | */ 74 | @Bean 75 | @Override 76 | protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { 77 | return new NullAuthenticatedSessionStrategy(); 78 | } 79 | 80 | /** 81 | * Secure appropriate endpoints 82 | */ 83 | @Override 84 | protected void configure(HttpSecurity http) throws Exception { 85 | super.configure(http); 86 | http.cors() // 87 | .and() // 88 | .csrf().disable() // 89 | // .anonymous().disable() // 90 | .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 91 | .and() // 92 | .authorizeRequests() // 93 | // .antMatchers("/users*").hasRole("USER") // 94 | // .antMatchers("/admin*").hasRole("ADMIN") // 95 | .anyRequest().permitAll(); // 96 | } 97 | 98 | @SuppressWarnings({ "rawtypes", "unchecked" }) 99 | @Bean 100 | public FilterRegistrationBean keycloakAuthenticationProcessingFilterRegistrationBean(KeycloakAuthenticationProcessingFilter filter) { 101 | 102 | FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter); 103 | registrationBean.setEnabled(false); 104 | return registrationBean; 105 | } 106 | 107 | @SuppressWarnings({ "rawtypes", "unchecked" }) 108 | @Bean 109 | public FilterRegistrationBean keycloakPreAuthActionsFilterRegistrationBean(KeycloakPreAuthActionsFilter filter) { 110 | 111 | FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter); 112 | registrationBean.setEnabled(false); 113 | return registrationBean; 114 | } 115 | 116 | @SuppressWarnings({ "rawtypes", "unchecked" }) 117 | @Bean 118 | public FilterRegistrationBean keycloakAuthenticatedActionsFilterBean(KeycloakAuthenticatedActionsFilter filter) { 119 | 120 | FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter); 121 | registrationBean.setEnabled(false); 122 | return registrationBean; 123 | } 124 | 125 | @SuppressWarnings({ "rawtypes", "unchecked" }) 126 | @Bean 127 | public FilterRegistrationBean keycloakSecurityContextRequestFilterBean(KeycloakSecurityContextRequestFilter filter) { 128 | 129 | FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter); 130 | registrationBean.setEnabled(false); 131 | return registrationBean; 132 | } 133 | 134 | @Bean 135 | @Override 136 | @ConditionalOnMissingBean(HttpSessionManager.class) 137 | protected HttpSessionManager httpSessionManager() { 138 | return new HttpSessionManager(); 139 | } 140 | 141 | } 142 | -------------------------------------------------------------------------------- /config/primereact-app-realm.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "primereact-app", 3 | "realm": "primereact-app", 4 | "notBefore": 1554853296, 5 | "revokeRefreshToken": false, 6 | "refreshTokenMaxReuse": 0, 7 | "accessTokenLifespan": 300, 8 | "accessTokenLifespanForImplicitFlow": 900, 9 | "ssoSessionIdleTimeout": 1800, 10 | "ssoSessionMaxLifespan": 36000, 11 | "ssoSessionIdleTimeoutRememberMe": 0, 12 | "ssoSessionMaxLifespanRememberMe": 0, 13 | "offlineSessionIdleTimeout": 2592000, 14 | "offlineSessionMaxLifespanEnabled": false, 15 | "offlineSessionMaxLifespan": 5184000, 16 | "clientSessionIdleTimeout": 0, 17 | "clientSessionMaxLifespan": 0, 18 | "accessCodeLifespan": 60, 19 | "accessCodeLifespanUserAction": 300, 20 | "accessCodeLifespanLogin": 1800, 21 | "actionTokenGeneratedByAdminLifespan": 43200, 22 | "actionTokenGeneratedByUserLifespan": 300, 23 | "enabled": true, 24 | "sslRequired": "external", 25 | "registrationAllowed": false, 26 | "registrationEmailAsUsername": false, 27 | "rememberMe": false, 28 | "verifyEmail": false, 29 | "loginWithEmailAllowed": true, 30 | "duplicateEmailsAllowed": false, 31 | "resetPasswordAllowed": false, 32 | "editUsernameAllowed": false, 33 | "bruteForceProtected": false, 34 | "permanentLockout": false, 35 | "maxFailureWaitSeconds": 900, 36 | "minimumQuickLoginWaitSeconds": 60, 37 | "waitIncrementSeconds": 60, 38 | "quickLoginCheckMilliSeconds": 1000, 39 | "maxDeltaTimeSeconds": 43200, 40 | "failureFactor": 30, 41 | "roles": { 42 | "realm": [ 43 | { 44 | "id": "9cee8b95-4ba9-4d38-afbe-fa45a8c84e6d", 45 | "name": "User", 46 | "composite": false, 47 | "clientRole": false, 48 | "containerId": "primereact-app", 49 | "attributes": { 50 | 51 | } 52 | }, 53 | { 54 | "id": "c8e085de-460b-420f-afac-3d4932b5c04f", 55 | "name": "uma_authorization", 56 | "description": "${role_uma_authorization}", 57 | "composite": false, 58 | "clientRole": false, 59 | "containerId": "primereact-app", 60 | "attributes": { 61 | 62 | } 63 | }, 64 | { 65 | "id": "959277db-8d32-4f5c-874b-04d83f6003f9", 66 | "name": "offline_access", 67 | "description": "${role_offline-access}", 68 | "composite": false, 69 | "clientRole": false, 70 | "containerId": "primereact-app", 71 | "attributes": { 72 | 73 | } 74 | }, 75 | { 76 | "id": "17b22962-4200-4423-ab66-7026837e7da4", 77 | "name": "Admin", 78 | "composite": false, 79 | "clientRole": false, 80 | "containerId": "primereact-app", 81 | "attributes": { 82 | 83 | } 84 | } 85 | ], 86 | "client": { 87 | "primereact-client": [], 88 | "realm-management": [ 89 | { 90 | "id": "340b375d-1990-4ca7-bdf5-b8d1ac5de197", 91 | "name": "view-users", 92 | "description": "${role_view-users}", 93 | "composite": true, 94 | "composites": { 95 | "client": { 96 | "realm-management": [ 97 | "query-groups", 98 | "query-users" 99 | ] 100 | } 101 | }, 102 | "clientRole": true, 103 | "containerId": "2aac5aa3-b45d-4b32-b733-f16a90bc73d1", 104 | "attributes": { 105 | 106 | } 107 | }, 108 | { 109 | "id": "bd863883-b958-4b66-89a9-bb0dc68c0a3e", 110 | "name": "view-realm", 111 | "description": "${role_view-realm}", 112 | "composite": false, 113 | "clientRole": true, 114 | "containerId": "2aac5aa3-b45d-4b32-b733-f16a90bc73d1", 115 | "attributes": { 116 | 117 | } 118 | }, 119 | { 120 | "id": "ff8458f2-2c9e-4948-9c63-303707c8e181", 121 | "name": "manage-identity-providers", 122 | "description": "${role_manage-identity-providers}", 123 | "composite": false, 124 | "clientRole": true, 125 | "containerId": "2aac5aa3-b45d-4b32-b733-f16a90bc73d1", 126 | "attributes": { 127 | 128 | } 129 | }, 130 | { 131 | "id": "8b4edf2e-cc5c-41fc-add0-14fec0d356db", 132 | "name": "view-events", 133 | "description": "${role_view-events}", 134 | "composite": false, 135 | "clientRole": true, 136 | "containerId": "2aac5aa3-b45d-4b32-b733-f16a90bc73d1", 137 | "attributes": { 138 | 139 | } 140 | }, 141 | { 142 | "id": "9ed5f790-5775-42f9-88cb-bb26219aadd0", 143 | "name": "impersonation", 144 | "description": "${role_impersonation}", 145 | "composite": false, 146 | "clientRole": true, 147 | "containerId": "2aac5aa3-b45d-4b32-b733-f16a90bc73d1", 148 | "attributes": { 149 | 150 | } 151 | }, 152 | { 153 | "id": "a9fcec4d-3631-407f-9825-311d5e3bb2d2", 154 | "name": "manage-users", 155 | "description": "${role_manage-users}", 156 | "composite": false, 157 | "clientRole": true, 158 | "containerId": "2aac5aa3-b45d-4b32-b733-f16a90bc73d1", 159 | "attributes": { 160 | 161 | } 162 | }, 163 | { 164 | "id": "ccbeb298-66e3-4e9a-aba9-8a6497bff650", 165 | "name": "view-clients", 166 | "description": "${role_view-clients}", 167 | "composite": true, 168 | "composites": { 169 | "client": { 170 | "realm-management": [ 171 | "query-clients" 172 | ] 173 | } 174 | }, 175 | "clientRole": true, 176 | "containerId": "2aac5aa3-b45d-4b32-b733-f16a90bc73d1", 177 | "attributes": { 178 | 179 | } 180 | }, 181 | { 182 | "id": "5d6fb9cf-626a-4f9b-8ef3-d4312d92450b", 183 | "name": "query-clients", 184 | "description": "${role_query-clients}", 185 | "composite": false, 186 | "clientRole": true, 187 | "containerId": "2aac5aa3-b45d-4b32-b733-f16a90bc73d1", 188 | "attributes": { 189 | 190 | } 191 | }, 192 | { 193 | "id": "aa198b55-a804-4a52-9a4c-7fd801f2cc40", 194 | "name": "query-realms", 195 | "description": "${role_query-realms}", 196 | "composite": false, 197 | "clientRole": true, 198 | "containerId": "2aac5aa3-b45d-4b32-b733-f16a90bc73d1", 199 | "attributes": { 200 | 201 | } 202 | }, 203 | { 204 | "id": "d608be39-b898-4ff1-bc50-7f76a758d15b", 205 | "name": "query-groups", 206 | "description": "${role_query-groups}", 207 | "composite": false, 208 | "clientRole": true, 209 | "containerId": "2aac5aa3-b45d-4b32-b733-f16a90bc73d1", 210 | "attributes": { 211 | 212 | } 213 | }, 214 | { 215 | "id": "367dff6d-67c0-4e4c-a52f-830519b7f552", 216 | "name": "create-client", 217 | "description": "${role_create-client}", 218 | "composite": false, 219 | "clientRole": true, 220 | "containerId": "2aac5aa3-b45d-4b32-b733-f16a90bc73d1", 221 | "attributes": { 222 | 223 | } 224 | }, 225 | { 226 | "id": "270c50b8-00b7-447a-b2fd-083f71b646dc", 227 | "name": "manage-clients", 228 | "description": "${role_manage-clients}", 229 | "composite": false, 230 | "clientRole": true, 231 | "containerId": "2aac5aa3-b45d-4b32-b733-f16a90bc73d1", 232 | "attributes": { 233 | 234 | } 235 | }, 236 | { 237 | "id": "e46719a7-380d-44ce-96f6-0fa940ffc8e0", 238 | "name": "manage-authorization", 239 | "description": "${role_manage-authorization}", 240 | "composite": false, 241 | "clientRole": true, 242 | "containerId": "2aac5aa3-b45d-4b32-b733-f16a90bc73d1", 243 | "attributes": { 244 | 245 | } 246 | }, 247 | { 248 | "id": "dd5211ad-e74b-4f3a-bc17-9b866f7e8289", 249 | "name": "realm-admin", 250 | "description": "${role_realm-admin}", 251 | "composite": true, 252 | "composites": { 253 | "client": { 254 | "realm-management": [ 255 | "view-users", 256 | "view-realm", 257 | "manage-identity-providers", 258 | "view-events", 259 | "impersonation", 260 | "manage-users", 261 | "query-clients", 262 | "view-clients", 263 | "query-realms", 264 | "query-groups", 265 | "create-client", 266 | "manage-clients", 267 | "manage-authorization", 268 | "view-authorization", 269 | "manage-events", 270 | "view-identity-providers", 271 | "query-users", 272 | "manage-realm" 273 | ] 274 | } 275 | }, 276 | "clientRole": true, 277 | "containerId": "2aac5aa3-b45d-4b32-b733-f16a90bc73d1", 278 | "attributes": { 279 | 280 | } 281 | }, 282 | { 283 | "id": "43a14f49-9cd2-4027-8621-e5af0acc6344", 284 | "name": "view-authorization", 285 | "description": "${role_view-authorization}", 286 | "composite": false, 287 | "clientRole": true, 288 | "containerId": "2aac5aa3-b45d-4b32-b733-f16a90bc73d1", 289 | "attributes": { 290 | 291 | } 292 | }, 293 | { 294 | "id": "5446a70b-b5dd-41f2-abfe-1d300e791c7e", 295 | "name": "manage-events", 296 | "description": "${role_manage-events}", 297 | "composite": false, 298 | "clientRole": true, 299 | "containerId": "2aac5aa3-b45d-4b32-b733-f16a90bc73d1", 300 | "attributes": { 301 | 302 | } 303 | }, 304 | { 305 | "id": "bcd3436f-5af9-45bf-bf94-9781afee7baa", 306 | "name": "view-identity-providers", 307 | "description": "${role_view-identity-providers}", 308 | "composite": false, 309 | "clientRole": true, 310 | "containerId": "2aac5aa3-b45d-4b32-b733-f16a90bc73d1", 311 | "attributes": { 312 | 313 | } 314 | }, 315 | { 316 | "id": "697a860f-1f8a-4666-be6a-2731664955b5", 317 | "name": "query-users", 318 | "description": "${role_query-users}", 319 | "composite": false, 320 | "clientRole": true, 321 | "containerId": "2aac5aa3-b45d-4b32-b733-f16a90bc73d1", 322 | "attributes": { 323 | 324 | } 325 | }, 326 | { 327 | "id": "96557a0b-166f-454c-870c-5cb7b65ec68a", 328 | "name": "manage-realm", 329 | "description": "${role_manage-realm}", 330 | "composite": false, 331 | "clientRole": true, 332 | "containerId": "2aac5aa3-b45d-4b32-b733-f16a90bc73d1", 333 | "attributes": { 334 | 335 | } 336 | } 337 | ], 338 | "security-admin-console": [], 339 | "admin-cli": [], 340 | "account-console": [], 341 | "broker": [ 342 | { 343 | "id": "c9a5be0f-d49d-47c2-91d0-3980ceee9e35", 344 | "name": "read-token", 345 | "description": "${role_read-token}", 346 | "composite": false, 347 | "clientRole": true, 348 | "containerId": "41f6ca15-ee09-4721-93be-a7ef26841099", 349 | "attributes": { 350 | 351 | } 352 | } 353 | ], 354 | "springboot-rest-api": [ 355 | { 356 | "id": "673e3c31-d6b2-4c66-85c2-cdff9f658a90", 357 | "name": "uma_protection", 358 | "composite": false, 359 | "clientRole": true, 360 | "containerId": "7af9f233-680b-4f84-9858-10b523f2d433", 361 | "attributes": { 362 | 363 | } 364 | } 365 | ], 366 | "account": [ 367 | { 368 | "id": "ef82b5ee-80b6-4c52-a0dc-91ad886573d6", 369 | "name": "view-profile", 370 | "description": "${role_view-profile}", 371 | "composite": false, 372 | "clientRole": true, 373 | "containerId": "8149bbc2-128c-4ce6-abee-1e886edf91a9", 374 | "attributes": { 375 | 376 | } 377 | }, 378 | { 379 | "id": "37b2b947-6b52-4c17-9a69-db59398c20fd", 380 | "name": "manage-account", 381 | "description": "${role_manage-account}", 382 | "composite": true, 383 | "composites": { 384 | "client": { 385 | "account": [ 386 | "manage-account-links" 387 | ] 388 | } 389 | }, 390 | "clientRole": true, 391 | "containerId": "8149bbc2-128c-4ce6-abee-1e886edf91a9", 392 | "attributes": { 393 | 394 | } 395 | }, 396 | { 397 | "id": "4c2ad8b3-ae13-4766-83b0-b5d8a009e639", 398 | "name": "manage-account-links", 399 | "description": "${role_manage-account-links}", 400 | "composite": false, 401 | "clientRole": true, 402 | "containerId": "8149bbc2-128c-4ce6-abee-1e886edf91a9", 403 | "attributes": { 404 | 405 | } 406 | }, 407 | { 408 | "id": "12cedc54-fa6c-4a3e-a5b6-db0a0ffa7305", 409 | "name": "manage-consent", 410 | "description": "${role_manage-consent}", 411 | "composite": true, 412 | "composites": { 413 | "client": { 414 | "account": [ 415 | "view-consent" 416 | ] 417 | } 418 | }, 419 | "clientRole": true, 420 | "containerId": "8149bbc2-128c-4ce6-abee-1e886edf91a9", 421 | "attributes": { 422 | 423 | } 424 | }, 425 | { 426 | "id": "98629fdf-72bc-4069-b588-39e7d81a7f70", 427 | "name": "view-consent", 428 | "description": "${role_view-consent}", 429 | "composite": false, 430 | "clientRole": true, 431 | "containerId": "8149bbc2-128c-4ce6-abee-1e886edf91a9", 432 | "attributes": { 433 | 434 | } 435 | }, 436 | { 437 | "id": "4467db62-4d1a-4cc4-af48-55d984a058b7", 438 | "name": "view-applications", 439 | "description": "${role_view-applications}", 440 | "composite": false, 441 | "clientRole": true, 442 | "containerId": "8149bbc2-128c-4ce6-abee-1e886edf91a9", 443 | "attributes": { 444 | 445 | } 446 | } 447 | ] 448 | } 449 | }, 450 | "groups": [], 451 | "defaultRoles": [ 452 | "offline_access", 453 | "uma_authorization" 454 | ], 455 | "requiredCredentials": [ 456 | "password" 457 | ], 458 | "otpPolicyType": "totp", 459 | "otpPolicyAlgorithm": "HmacSHA1", 460 | "otpPolicyInitialCounter": 0, 461 | "otpPolicyDigits": 6, 462 | "otpPolicyLookAheadWindow": 1, 463 | "otpPolicyPeriod": 30, 464 | "otpSupportedApplications": [ 465 | "FreeOTP", 466 | "Google Authenticator" 467 | ], 468 | "webAuthnPolicyRpEntityName": "keycloak", 469 | "webAuthnPolicySignatureAlgorithms": [ 470 | "ES256" 471 | ], 472 | "webAuthnPolicyRpId": "", 473 | "webAuthnPolicyAttestationConveyancePreference": "not specified", 474 | "webAuthnPolicyAuthenticatorAttachment": "not specified", 475 | "webAuthnPolicyRequireResidentKey": "not specified", 476 | "webAuthnPolicyUserVerificationRequirement": "not specified", 477 | "webAuthnPolicyCreateTimeout": 0, 478 | "webAuthnPolicyAvoidSameAuthenticatorRegister": false, 479 | "webAuthnPolicyAcceptableAaguids": [], 480 | "webAuthnPolicyPasswordlessRpEntityName": "keycloak", 481 | "webAuthnPolicyPasswordlessSignatureAlgorithms": [ 482 | "ES256" 483 | ], 484 | "webAuthnPolicyPasswordlessRpId": "", 485 | "webAuthnPolicyPasswordlessAttestationConveyancePreference": "not specified", 486 | "webAuthnPolicyPasswordlessAuthenticatorAttachment": "not specified", 487 | "webAuthnPolicyPasswordlessRequireResidentKey": "not specified", 488 | "webAuthnPolicyPasswordlessUserVerificationRequirement": "not specified", 489 | "webAuthnPolicyPasswordlessCreateTimeout": 0, 490 | "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false, 491 | "webAuthnPolicyPasswordlessAcceptableAaguids": [], 492 | "users": [ 493 | { 494 | "id": "d8f09987-4d43-433b-ab79-9e17a54f40d3", 495 | "createdTimestamp": 1592019306802, 496 | "username": "service-account-springboot-rest-api", 497 | "enabled": true, 498 | "totp": false, 499 | "emailVerified": false, 500 | "serviceAccountClientId": "springboot-rest-api", 501 | "disableableCredentialTypes": [], 502 | "requiredActions": [], 503 | "realmRoles": [ 504 | "uma_authorization", 505 | "offline_access" 506 | ], 507 | "clientRoles": { 508 | "springboot-rest-api": [ 509 | "uma_protection" 510 | ], 511 | "account": [ 512 | "manage-account", 513 | "view-profile" 514 | ] 515 | }, 516 | "notBefore": 0, 517 | "groups": [] 518 | } 519 | ], 520 | "scopeMappings": [ 521 | { 522 | "clientScope": "offline_access", 523 | "roles": [ 524 | "offline_access" 525 | ] 526 | } 527 | ], 528 | "clientScopeMappings": { 529 | "account": [ 530 | { 531 | "client": "account-console", 532 | "roles": [ 533 | "manage-account" 534 | ] 535 | } 536 | ] 537 | }, 538 | "clients": [ 539 | { 540 | "id": "8149bbc2-128c-4ce6-abee-1e886edf91a9", 541 | "clientId": "account", 542 | "name": "${client_account}", 543 | "rootUrl": "${authBaseUrl}", 544 | "baseUrl": "/realms/primereact-app/account/", 545 | "surrogateAuthRequired": false, 546 | "enabled": true, 547 | "alwaysDisplayInConsole": false, 548 | "clientAuthenticatorType": "client-secret", 549 | "secret": "**********", 550 | "defaultRoles": [ 551 | "manage-account", 552 | "view-profile" 553 | ], 554 | "redirectUris": [ 555 | "/realms/primereact-app/account/*" 556 | ], 557 | "webOrigins": [], 558 | "notBefore": 0, 559 | "bearerOnly": false, 560 | "consentRequired": false, 561 | "standardFlowEnabled": true, 562 | "implicitFlowEnabled": false, 563 | "directAccessGrantsEnabled": false, 564 | "serviceAccountsEnabled": false, 565 | "publicClient": false, 566 | "frontchannelLogout": false, 567 | "protocol": "openid-connect", 568 | "attributes": { 569 | 570 | }, 571 | "authenticationFlowBindingOverrides": { 572 | 573 | }, 574 | "fullScopeAllowed": false, 575 | "nodeReRegistrationTimeout": 0, 576 | "defaultClientScopes": [ 577 | "web-origins", 578 | "role_list", 579 | "profile", 580 | "roles", 581 | "email" 582 | ], 583 | "optionalClientScopes": [ 584 | "address", 585 | "phone", 586 | "offline_access" 587 | ] 588 | }, 589 | { 590 | "id": "63efff0a-671e-4777-aa71-657752fe4b71", 591 | "clientId": "account-console", 592 | "name": "${client_account-console}", 593 | "rootUrl": "${authBaseUrl}", 594 | "baseUrl": "/realms/primereact-app/account/", 595 | "surrogateAuthRequired": false, 596 | "enabled": true, 597 | "alwaysDisplayInConsole": false, 598 | "clientAuthenticatorType": "client-secret", 599 | "secret": "**********", 600 | "redirectUris": [ 601 | "/realms/primereact-app/account/*" 602 | ], 603 | "webOrigins": [], 604 | "notBefore": 0, 605 | "bearerOnly": false, 606 | "consentRequired": false, 607 | "standardFlowEnabled": true, 608 | "implicitFlowEnabled": false, 609 | "directAccessGrantsEnabled": false, 610 | "serviceAccountsEnabled": false, 611 | "publicClient": true, 612 | "frontchannelLogout": false, 613 | "protocol": "openid-connect", 614 | "attributes": { 615 | "pkce.code.challenge.method": "S256" 616 | }, 617 | "authenticationFlowBindingOverrides": { 618 | 619 | }, 620 | "fullScopeAllowed": false, 621 | "nodeReRegistrationTimeout": 0, 622 | "protocolMappers": [ 623 | { 624 | "id": "80c2c3f3-a1cf-471c-aae6-cd80432b7456", 625 | "name": "audience resolve", 626 | "protocol": "openid-connect", 627 | "protocolMapper": "oidc-audience-resolve-mapper", 628 | "consentRequired": false, 629 | "config": { 630 | 631 | } 632 | } 633 | ], 634 | "defaultClientScopes": [ 635 | "web-origins", 636 | "role_list", 637 | "profile", 638 | "roles", 639 | "email" 640 | ], 641 | "optionalClientScopes": [ 642 | "address", 643 | "phone", 644 | "offline_access" 645 | ] 646 | }, 647 | { 648 | "id": "50b7c226-c60c-402b-8d69-58cee1e5469f", 649 | "clientId": "admin-cli", 650 | "name": "${client_admin-cli}", 651 | "surrogateAuthRequired": false, 652 | "enabled": true, 653 | "alwaysDisplayInConsole": false, 654 | "clientAuthenticatorType": "client-secret", 655 | "secret": "**********", 656 | "redirectUris": [], 657 | "webOrigins": [], 658 | "notBefore": 0, 659 | "bearerOnly": false, 660 | "consentRequired": false, 661 | "standardFlowEnabled": false, 662 | "implicitFlowEnabled": false, 663 | "directAccessGrantsEnabled": true, 664 | "serviceAccountsEnabled": false, 665 | "publicClient": true, 666 | "frontchannelLogout": false, 667 | "protocol": "openid-connect", 668 | "attributes": { 669 | 670 | }, 671 | "authenticationFlowBindingOverrides": { 672 | 673 | }, 674 | "fullScopeAllowed": false, 675 | "nodeReRegistrationTimeout": 0, 676 | "defaultClientScopes": [ 677 | "web-origins", 678 | "role_list", 679 | "profile", 680 | "roles", 681 | "email" 682 | ], 683 | "optionalClientScopes": [ 684 | "address", 685 | "phone", 686 | "offline_access" 687 | ] 688 | }, 689 | { 690 | "id": "41f6ca15-ee09-4721-93be-a7ef26841099", 691 | "clientId": "broker", 692 | "name": "${client_broker}", 693 | "surrogateAuthRequired": false, 694 | "enabled": true, 695 | "alwaysDisplayInConsole": false, 696 | "clientAuthenticatorType": "client-secret", 697 | "secret": "**********", 698 | "redirectUris": [], 699 | "webOrigins": [], 700 | "notBefore": 0, 701 | "bearerOnly": false, 702 | "consentRequired": false, 703 | "standardFlowEnabled": true, 704 | "implicitFlowEnabled": false, 705 | "directAccessGrantsEnabled": false, 706 | "serviceAccountsEnabled": false, 707 | "publicClient": true, 708 | "frontchannelLogout": false, 709 | "protocol": "openid-connect", 710 | "attributes": { 711 | 712 | }, 713 | "authenticationFlowBindingOverrides": { 714 | 715 | }, 716 | "fullScopeAllowed": false, 717 | "nodeReRegistrationTimeout": 0, 718 | "defaultClientScopes": [ 719 | "web-origins", 720 | "role_list", 721 | "profile", 722 | "roles", 723 | "email" 724 | ], 725 | "optionalClientScopes": [ 726 | "address", 727 | "phone", 728 | "offline_access" 729 | ] 730 | }, 731 | { 732 | "id": "8a77a288-edf8-437f-877d-46dae9b515e2", 733 | "clientId": "primereact-client", 734 | "rootUrl": "http://localhost:3000/", 735 | "adminUrl": "http://localhost:3000/", 736 | "surrogateAuthRequired": false, 737 | "enabled": true, 738 | "alwaysDisplayInConsole": false, 739 | "clientAuthenticatorType": "client-secret", 740 | "secret": "**********", 741 | "redirectUris": [ 742 | "*" 743 | ], 744 | "webOrigins": [ 745 | "*" 746 | ], 747 | "notBefore": 0, 748 | "bearerOnly": false, 749 | "consentRequired": false, 750 | "standardFlowEnabled": true, 751 | "implicitFlowEnabled": false, 752 | "directAccessGrantsEnabled": true, 753 | "serviceAccountsEnabled": false, 754 | "publicClient": true, 755 | "frontchannelLogout": false, 756 | "protocol": "openid-connect", 757 | "attributes": { 758 | "saml.assertion.signature": "false", 759 | "saml.force.post.binding": "false", 760 | "saml.multivalued.roles": "false", 761 | "saml.encrypt": "false", 762 | "saml.server.signature": "false", 763 | "saml.server.signature.keyinfo.ext": "false", 764 | "exclude.session.state.from.auth.response": "false", 765 | "saml_force_name_id_format": "false", 766 | "saml.client.signature": "false", 767 | "tls.client.certificate.bound.access.tokens": "false", 768 | "saml.authnstatement": "false", 769 | "display.on.consent.screen": "false", 770 | "saml.onetimeuse.condition": "false" 771 | }, 772 | "authenticationFlowBindingOverrides": { 773 | 774 | }, 775 | "fullScopeAllowed": true, 776 | "nodeReRegistrationTimeout": -1, 777 | "defaultClientScopes": [ 778 | "web-origins", 779 | "role_list", 780 | "profile", 781 | "roles", 782 | "email" 783 | ], 784 | "optionalClientScopes": [ 785 | "address", 786 | "phone", 787 | "offline_access" 788 | ] 789 | }, 790 | { 791 | "id": "2aac5aa3-b45d-4b32-b733-f16a90bc73d1", 792 | "clientId": "realm-management", 793 | "name": "${client_realm-management}", 794 | "surrogateAuthRequired": false, 795 | "enabled": true, 796 | "alwaysDisplayInConsole": false, 797 | "clientAuthenticatorType": "client-secret", 798 | "secret": "**********", 799 | "redirectUris": [], 800 | "webOrigins": [], 801 | "notBefore": 0, 802 | "bearerOnly": true, 803 | "consentRequired": false, 804 | "standardFlowEnabled": true, 805 | "implicitFlowEnabled": false, 806 | "directAccessGrantsEnabled": false, 807 | "serviceAccountsEnabled": false, 808 | "publicClient": false, 809 | "frontchannelLogout": false, 810 | "protocol": "openid-connect", 811 | "attributes": { 812 | 813 | }, 814 | "authenticationFlowBindingOverrides": { 815 | 816 | }, 817 | "fullScopeAllowed": false, 818 | "nodeReRegistrationTimeout": 0, 819 | "defaultClientScopes": [ 820 | "web-origins", 821 | "role_list", 822 | "profile", 823 | "roles", 824 | "email" 825 | ], 826 | "optionalClientScopes": [ 827 | "address", 828 | "phone", 829 | "offline_access" 830 | ] 831 | }, 832 | { 833 | "id": "43f38851-f3f1-42d8-a405-eb29a3745408", 834 | "clientId": "security-admin-console", 835 | "name": "${client_security-admin-console}", 836 | "rootUrl": "${authAdminUrl}", 837 | "baseUrl": "/admin/primereact-app/console/", 838 | "surrogateAuthRequired": false, 839 | "enabled": true, 840 | "alwaysDisplayInConsole": false, 841 | "clientAuthenticatorType": "client-secret", 842 | "secret": "**********", 843 | "redirectUris": [ 844 | "/admin/primereact-app/console/*" 845 | ], 846 | "webOrigins": [ 847 | "+" 848 | ], 849 | "notBefore": 0, 850 | "bearerOnly": false, 851 | "consentRequired": false, 852 | "standardFlowEnabled": true, 853 | "implicitFlowEnabled": false, 854 | "directAccessGrantsEnabled": false, 855 | "serviceAccountsEnabled": false, 856 | "publicClient": true, 857 | "frontchannelLogout": false, 858 | "protocol": "openid-connect", 859 | "attributes": { 860 | "pkce.code.challenge.method": "S256" 861 | }, 862 | "authenticationFlowBindingOverrides": { 863 | 864 | }, 865 | "fullScopeAllowed": false, 866 | "nodeReRegistrationTimeout": 0, 867 | "protocolMappers": [ 868 | { 869 | "id": "d85bda67-1deb-4fc1-9636-62cd83ac7ceb", 870 | "name": "locale", 871 | "protocol": "openid-connect", 872 | "protocolMapper": "oidc-usermodel-attribute-mapper", 873 | "consentRequired": false, 874 | "config": { 875 | "userinfo.token.claim": "true", 876 | "user.attribute": "locale", 877 | "id.token.claim": "true", 878 | "access.token.claim": "true", 879 | "claim.name": "locale", 880 | "jsonType.label": "String" 881 | } 882 | } 883 | ], 884 | "defaultClientScopes": [ 885 | "web-origins", 886 | "role_list", 887 | "profile", 888 | "roles", 889 | "email" 890 | ], 891 | "optionalClientScopes": [ 892 | "address", 893 | "phone", 894 | "offline_access" 895 | ] 896 | }, 897 | { 898 | "id": "7af9f233-680b-4f84-9858-10b523f2d433", 899 | "clientId": "springboot-rest-api", 900 | "rootUrl": "http://localhost:8080", 901 | "surrogateAuthRequired": false, 902 | "enabled": true, 903 | "alwaysDisplayInConsole": false, 904 | "clientAuthenticatorType": "client-secret", 905 | "secret": "**********", 906 | "redirectUris": [ 907 | "http://localhost:8080" 908 | ], 909 | "webOrigins": [], 910 | "notBefore": 0, 911 | "bearerOnly": false, 912 | "consentRequired": false, 913 | "standardFlowEnabled": true, 914 | "implicitFlowEnabled": false, 915 | "directAccessGrantsEnabled": true, 916 | "serviceAccountsEnabled": true, 917 | "authorizationServicesEnabled": true, 918 | "publicClient": false, 919 | "frontchannelLogout": false, 920 | "protocol": "openid-connect", 921 | "attributes": { 922 | "saml.assertion.signature": "false", 923 | "saml.force.post.binding": "false", 924 | "saml.multivalued.roles": "false", 925 | "saml.encrypt": "false", 926 | "saml.server.signature": "false", 927 | "saml.server.signature.keyinfo.ext": "false", 928 | "exclude.session.state.from.auth.response": "false", 929 | "saml_force_name_id_format": "false", 930 | "saml.client.signature": "false", 931 | "tls.client.certificate.bound.access.tokens": "false", 932 | "saml.authnstatement": "false", 933 | "display.on.consent.screen": "false", 934 | "saml.onetimeuse.condition": "false" 935 | }, 936 | "authenticationFlowBindingOverrides": { 937 | 938 | }, 939 | "fullScopeAllowed": true, 940 | "nodeReRegistrationTimeout": -1, 941 | "protocolMappers": [ 942 | { 943 | "id": "1f23d5bb-c5c2-4e81-b30a-8bd258628b0b", 944 | "name": "Client ID", 945 | "protocol": "openid-connect", 946 | "protocolMapper": "oidc-usersessionmodel-note-mapper", 947 | "consentRequired": false, 948 | "config": { 949 | "user.session.note": "clientId", 950 | "userinfo.token.claim": "true", 951 | "id.token.claim": "true", 952 | "access.token.claim": "true", 953 | "claim.name": "clientId", 954 | "jsonType.label": "String" 955 | } 956 | }, 957 | { 958 | "id": "357f8113-37d4-4bbc-8a24-1f5b395eadfb", 959 | "name": "Client IP Address", 960 | "protocol": "openid-connect", 961 | "protocolMapper": "oidc-usersessionmodel-note-mapper", 962 | "consentRequired": false, 963 | "config": { 964 | "user.session.note": "clientAddress", 965 | "userinfo.token.claim": "true", 966 | "id.token.claim": "true", 967 | "access.token.claim": "true", 968 | "claim.name": "clientAddress", 969 | "jsonType.label": "String" 970 | } 971 | }, 972 | { 973 | "id": "ed8868b3-4ea8-49b5-8199-91f3d3d7ddc0", 974 | "name": "admin", 975 | "protocol": "openid-connect", 976 | "protocolMapper": "oidc-usermodel-property-mapper", 977 | "consentRequired": false, 978 | "config": { 979 | "userinfo.token.claim": "true", 980 | "user.attribute": "username", 981 | "id.token.claim": "true", 982 | "access.token.claim": "true", 983 | "claim.name": "user_name", 984 | "jsonType.label": "String" 985 | } 986 | }, 987 | { 988 | "id": "9e4db923-ea0f-461b-ade6-61acac08cc6e", 989 | "name": "Client Host", 990 | "protocol": "openid-connect", 991 | "protocolMapper": "oidc-usersessionmodel-note-mapper", 992 | "consentRequired": false, 993 | "config": { 994 | "user.session.note": "clientHost", 995 | "userinfo.token.claim": "true", 996 | "id.token.claim": "true", 997 | "access.token.claim": "true", 998 | "claim.name": "clientHost", 999 | "jsonType.label": "String" 1000 | } 1001 | } 1002 | ], 1003 | "defaultClientScopes": [ 1004 | "web-origins", 1005 | "role_list", 1006 | "profile", 1007 | "roles", 1008 | "email" 1009 | ], 1010 | "optionalClientScopes": [ 1011 | "address", 1012 | "phone", 1013 | "offline_access" 1014 | ], 1015 | "authorizationSettings": { 1016 | "allowRemoteResourceManagement": true, 1017 | "policyEnforcementMode": "ENFORCING", 1018 | "resources": [ 1019 | { 1020 | "name": "Default Resource", 1021 | "type": "urn:springboot-rest-api:resources:default", 1022 | "ownerManagedAccess": false, 1023 | "attributes": { 1024 | 1025 | }, 1026 | "_id": "7a2233c6-0a53-4392-b616-51910de1c773", 1027 | "uris": [ 1028 | "/*" 1029 | ] 1030 | } 1031 | ], 1032 | "policies": [ 1033 | { 1034 | "id": "48db2d92-4843-4752-9ab1-0b01b73710f1", 1035 | "name": "Default Policy", 1036 | "description": "A policy that grants access only for users within this realm", 1037 | "type": "js", 1038 | "logic": "POSITIVE", 1039 | "decisionStrategy": "AFFIRMATIVE", 1040 | "config": { 1041 | "code": "// by default, grants any permission associated with this policy\n$evaluation.grant();\n" 1042 | } 1043 | }, 1044 | { 1045 | "id": "db755360-db7c-4e6e-8648-2ed3d03739bf", 1046 | "name": "Default Permission", 1047 | "description": "A permission that applies to the default resource type", 1048 | "type": "resource", 1049 | "logic": "POSITIVE", 1050 | "decisionStrategy": "UNANIMOUS", 1051 | "config": { 1052 | "defaultResourceType": "urn:springboot-rest-api:resources:default", 1053 | "applyPolicies": "[\"Default Policy\"]" 1054 | } 1055 | } 1056 | ], 1057 | "scopes": [], 1058 | "decisionStrategy": "UNANIMOUS" 1059 | } 1060 | } 1061 | ], 1062 | "clientScopes": [ 1063 | { 1064 | "id": "d5873d21-32f9-498b-a521-43cbead37d60", 1065 | "name": "address", 1066 | "description": "OpenID Connect built-in scope: address", 1067 | "protocol": "openid-connect", 1068 | "attributes": { 1069 | "include.in.token.scope": "true", 1070 | "display.on.consent.screen": "true", 1071 | "consent.screen.text": "${addressScopeConsentText}" 1072 | }, 1073 | "protocolMappers": [ 1074 | { 1075 | "id": "2a6f57cc-1698-49e0-b76b-05e83d03cf5d", 1076 | "name": "address", 1077 | "protocol": "openid-connect", 1078 | "protocolMapper": "oidc-address-mapper", 1079 | "consentRequired": false, 1080 | "config": { 1081 | "user.attribute.formatted": "formatted", 1082 | "user.attribute.country": "country", 1083 | "user.attribute.postal_code": "postal_code", 1084 | "userinfo.token.claim": "true", 1085 | "user.attribute.street": "street", 1086 | "id.token.claim": "true", 1087 | "user.attribute.region": "region", 1088 | "access.token.claim": "true", 1089 | "user.attribute.locality": "locality" 1090 | } 1091 | } 1092 | ] 1093 | }, 1094 | { 1095 | "id": "4b6bb892-bdb8-490d-b7ce-d51db16e64e8", 1096 | "name": "email", 1097 | "description": "OpenID Connect built-in scope: email", 1098 | "protocol": "openid-connect", 1099 | "attributes": { 1100 | "include.in.token.scope": "true", 1101 | "display.on.consent.screen": "true", 1102 | "consent.screen.text": "${emailScopeConsentText}" 1103 | }, 1104 | "protocolMappers": [ 1105 | { 1106 | "id": "9555af1c-58cb-4ea5-8a94-2a544c451eb1", 1107 | "name": "email", 1108 | "protocol": "openid-connect", 1109 | "protocolMapper": "oidc-usermodel-property-mapper", 1110 | "consentRequired": false, 1111 | "config": { 1112 | "userinfo.token.claim": "true", 1113 | "user.attribute": "email", 1114 | "id.token.claim": "true", 1115 | "access.token.claim": "true", 1116 | "claim.name": "email", 1117 | "jsonType.label": "String" 1118 | } 1119 | }, 1120 | { 1121 | "id": "9cd97cfa-566d-49bb-826e-e26c0ecd5cfa", 1122 | "name": "email verified", 1123 | "protocol": "openid-connect", 1124 | "protocolMapper": "oidc-usermodel-property-mapper", 1125 | "consentRequired": false, 1126 | "config": { 1127 | "userinfo.token.claim": "true", 1128 | "user.attribute": "emailVerified", 1129 | "id.token.claim": "true", 1130 | "access.token.claim": "true", 1131 | "claim.name": "email_verified", 1132 | "jsonType.label": "boolean" 1133 | } 1134 | } 1135 | ] 1136 | }, 1137 | { 1138 | "id": "2c7cfe21-3b89-4f1e-a635-d88aee3cce7f", 1139 | "name": "offline_access", 1140 | "description": "OpenID Connect built-in scope: offline_access", 1141 | "protocol": "openid-connect", 1142 | "attributes": { 1143 | "consent.screen.text": "${offlineAccessScopeConsentText}", 1144 | "display.on.consent.screen": "true" 1145 | } 1146 | }, 1147 | { 1148 | "id": "033c5f01-c969-4d88-ab06-094b7ce47507", 1149 | "name": "phone", 1150 | "description": "OpenID Connect built-in scope: phone", 1151 | "protocol": "openid-connect", 1152 | "attributes": { 1153 | "include.in.token.scope": "true", 1154 | "display.on.consent.screen": "true", 1155 | "consent.screen.text": "${phoneScopeConsentText}" 1156 | }, 1157 | "protocolMappers": [ 1158 | { 1159 | "id": "de881f81-5314-4f3a-bfb6-137a70e21c9a", 1160 | "name": "phone number", 1161 | "protocol": "openid-connect", 1162 | "protocolMapper": "oidc-usermodel-attribute-mapper", 1163 | "consentRequired": false, 1164 | "config": { 1165 | "userinfo.token.claim": "true", 1166 | "user.attribute": "phoneNumber", 1167 | "id.token.claim": "true", 1168 | "access.token.claim": "true", 1169 | "claim.name": "phone_number", 1170 | "jsonType.label": "String" 1171 | } 1172 | }, 1173 | { 1174 | "id": "beecd765-7c6b-443f-918a-7fd64cc323f6", 1175 | "name": "phone number verified", 1176 | "protocol": "openid-connect", 1177 | "protocolMapper": "oidc-usermodel-attribute-mapper", 1178 | "consentRequired": false, 1179 | "config": { 1180 | "userinfo.token.claim": "true", 1181 | "user.attribute": "phoneNumberVerified", 1182 | "id.token.claim": "true", 1183 | "access.token.claim": "true", 1184 | "claim.name": "phone_number_verified", 1185 | "jsonType.label": "boolean" 1186 | } 1187 | } 1188 | ] 1189 | }, 1190 | { 1191 | "id": "56889d84-c256-4c7e-a37d-0236db15d13c", 1192 | "name": "profile", 1193 | "description": "OpenID Connect built-in scope: profile", 1194 | "protocol": "openid-connect", 1195 | "attributes": { 1196 | "include.in.token.scope": "true", 1197 | "display.on.consent.screen": "true", 1198 | "consent.screen.text": "${profileScopeConsentText}" 1199 | }, 1200 | "protocolMappers": [ 1201 | { 1202 | "id": "d22f0899-3ff6-4df8-89f0-2c5dcaa35a87", 1203 | "name": "zoneinfo", 1204 | "protocol": "openid-connect", 1205 | "protocolMapper": "oidc-usermodel-attribute-mapper", 1206 | "consentRequired": false, 1207 | "config": { 1208 | "userinfo.token.claim": "true", 1209 | "user.attribute": "zoneinfo", 1210 | "id.token.claim": "true", 1211 | "access.token.claim": "true", 1212 | "claim.name": "zoneinfo", 1213 | "jsonType.label": "String" 1214 | } 1215 | }, 1216 | { 1217 | "id": "a31733a0-8a53-42d9-8cf3-8432f0e56268", 1218 | "name": "nickname", 1219 | "protocol": "openid-connect", 1220 | "protocolMapper": "oidc-usermodel-attribute-mapper", 1221 | "consentRequired": false, 1222 | "config": { 1223 | "userinfo.token.claim": "true", 1224 | "user.attribute": "nickname", 1225 | "id.token.claim": "true", 1226 | "access.token.claim": "true", 1227 | "claim.name": "nickname", 1228 | "jsonType.label": "String" 1229 | } 1230 | }, 1231 | { 1232 | "id": "3712b11a-39d1-4141-91ab-7e8c4c77bcb4", 1233 | "name": "gender", 1234 | "protocol": "openid-connect", 1235 | "protocolMapper": "oidc-usermodel-attribute-mapper", 1236 | "consentRequired": false, 1237 | "config": { 1238 | "userinfo.token.claim": "true", 1239 | "user.attribute": "gender", 1240 | "id.token.claim": "true", 1241 | "access.token.claim": "true", 1242 | "claim.name": "gender", 1243 | "jsonType.label": "String" 1244 | } 1245 | }, 1246 | { 1247 | "id": "ebf94e01-6b73-40bb-bf1d-a564bda2516d", 1248 | "name": "picture", 1249 | "protocol": "openid-connect", 1250 | "protocolMapper": "oidc-usermodel-attribute-mapper", 1251 | "consentRequired": false, 1252 | "config": { 1253 | "userinfo.token.claim": "true", 1254 | "user.attribute": "picture", 1255 | "id.token.claim": "true", 1256 | "access.token.claim": "true", 1257 | "claim.name": "picture", 1258 | "jsonType.label": "String" 1259 | } 1260 | }, 1261 | { 1262 | "id": "0ac2d624-2862-4d9e-8146-ebec908b6caa", 1263 | "name": "middle name", 1264 | "protocol": "openid-connect", 1265 | "protocolMapper": "oidc-usermodel-attribute-mapper", 1266 | "consentRequired": false, 1267 | "config": { 1268 | "userinfo.token.claim": "true", 1269 | "user.attribute": "middleName", 1270 | "id.token.claim": "true", 1271 | "access.token.claim": "true", 1272 | "claim.name": "middle_name", 1273 | "jsonType.label": "String" 1274 | } 1275 | }, 1276 | { 1277 | "id": "3e309881-6d29-409e-af02-f83d67241e9a", 1278 | "name": "updated at", 1279 | "protocol": "openid-connect", 1280 | "protocolMapper": "oidc-usermodel-attribute-mapper", 1281 | "consentRequired": false, 1282 | "config": { 1283 | "userinfo.token.claim": "true", 1284 | "user.attribute": "updatedAt", 1285 | "id.token.claim": "true", 1286 | "access.token.claim": "true", 1287 | "claim.name": "updated_at", 1288 | "jsonType.label": "String" 1289 | } 1290 | }, 1291 | { 1292 | "id": "02e04115-196c-4476-9978-9709fb3937bc", 1293 | "name": "birthdate", 1294 | "protocol": "openid-connect", 1295 | "protocolMapper": "oidc-usermodel-attribute-mapper", 1296 | "consentRequired": false, 1297 | "config": { 1298 | "userinfo.token.claim": "true", 1299 | "user.attribute": "birthdate", 1300 | "id.token.claim": "true", 1301 | "access.token.claim": "true", 1302 | "claim.name": "birthdate", 1303 | "jsonType.label": "String" 1304 | } 1305 | }, 1306 | { 1307 | "id": "6d248d44-2afc-4cd4-8e10-af0909a827a2", 1308 | "name": "given name", 1309 | "protocol": "openid-connect", 1310 | "protocolMapper": "oidc-usermodel-property-mapper", 1311 | "consentRequired": false, 1312 | "config": { 1313 | "userinfo.token.claim": "true", 1314 | "user.attribute": "firstName", 1315 | "id.token.claim": "true", 1316 | "access.token.claim": "true", 1317 | "claim.name": "given_name", 1318 | "jsonType.label": "String" 1319 | } 1320 | }, 1321 | { 1322 | "id": "7f0a7e41-5dd9-4a83-95d7-aac0cdb3adb1", 1323 | "name": "username", 1324 | "protocol": "openid-connect", 1325 | "protocolMapper": "oidc-usermodel-property-mapper", 1326 | "consentRequired": false, 1327 | "config": { 1328 | "userinfo.token.claim": "true", 1329 | "user.attribute": "username", 1330 | "id.token.claim": "true", 1331 | "access.token.claim": "true", 1332 | "claim.name": "preferred_username", 1333 | "jsonType.label": "String" 1334 | } 1335 | }, 1336 | { 1337 | "id": "3e74528d-df3f-4bf0-8ed4-f2042e5862e1", 1338 | "name": "website", 1339 | "protocol": "openid-connect", 1340 | "protocolMapper": "oidc-usermodel-attribute-mapper", 1341 | "consentRequired": false, 1342 | "config": { 1343 | "userinfo.token.claim": "true", 1344 | "user.attribute": "website", 1345 | "id.token.claim": "true", 1346 | "access.token.claim": "true", 1347 | "claim.name": "website", 1348 | "jsonType.label": "String" 1349 | } 1350 | }, 1351 | { 1352 | "id": "490c700a-8477-4755-9cf0-b41572b91df2", 1353 | "name": "profile", 1354 | "protocol": "openid-connect", 1355 | "protocolMapper": "oidc-usermodel-attribute-mapper", 1356 | "consentRequired": false, 1357 | "config": { 1358 | "userinfo.token.claim": "true", 1359 | "user.attribute": "profile", 1360 | "id.token.claim": "true", 1361 | "access.token.claim": "true", 1362 | "claim.name": "profile", 1363 | "jsonType.label": "String" 1364 | } 1365 | }, 1366 | { 1367 | "id": "f7afff67-10bd-4618-b5cd-5eef41ff95eb", 1368 | "name": "locale", 1369 | "protocol": "openid-connect", 1370 | "protocolMapper": "oidc-usermodel-attribute-mapper", 1371 | "consentRequired": false, 1372 | "config": { 1373 | "userinfo.token.claim": "true", 1374 | "user.attribute": "locale", 1375 | "id.token.claim": "true", 1376 | "access.token.claim": "true", 1377 | "claim.name": "locale", 1378 | "jsonType.label": "String" 1379 | } 1380 | }, 1381 | { 1382 | "id": "1b111402-2aa1-4469-b4d6-c5c8fb3b19f6", 1383 | "name": "full name", 1384 | "protocol": "openid-connect", 1385 | "protocolMapper": "oidc-full-name-mapper", 1386 | "consentRequired": false, 1387 | "config": { 1388 | "id.token.claim": "true", 1389 | "access.token.claim": "true", 1390 | "userinfo.token.claim": "true" 1391 | } 1392 | }, 1393 | { 1394 | "id": "062cd05c-9011-4d71-8d51-eb3fa4fa8969", 1395 | "name": "family name", 1396 | "protocol": "openid-connect", 1397 | "protocolMapper": "oidc-usermodel-property-mapper", 1398 | "consentRequired": false, 1399 | "config": { 1400 | "userinfo.token.claim": "true", 1401 | "user.attribute": "lastName", 1402 | "id.token.claim": "true", 1403 | "access.token.claim": "true", 1404 | "claim.name": "family_name", 1405 | "jsonType.label": "String" 1406 | } 1407 | } 1408 | ] 1409 | }, 1410 | { 1411 | "id": "d82eeccf-09a6-4760-8fdb-4f07cd9a8997", 1412 | "name": "role_list", 1413 | "description": "SAML role list", 1414 | "protocol": "saml", 1415 | "attributes": { 1416 | "consent.screen.text": "${samlRoleListScopeConsentText}", 1417 | "display.on.consent.screen": "true" 1418 | }, 1419 | "protocolMappers": [ 1420 | { 1421 | "id": "ae05e454-ce7b-428e-88a8-3ba4c66cafc6", 1422 | "name": "role list", 1423 | "protocol": "saml", 1424 | "protocolMapper": "saml-role-list-mapper", 1425 | "consentRequired": false, 1426 | "config": { 1427 | "single": "false", 1428 | "attribute.nameformat": "Basic", 1429 | "attribute.name": "Role" 1430 | } 1431 | } 1432 | ] 1433 | }, 1434 | { 1435 | "id": "f6b10a66-30d6-476b-a331-c5bc9c3b03fa", 1436 | "name": "roles", 1437 | "description": "OpenID Connect scope for add user roles to the access token", 1438 | "protocol": "openid-connect", 1439 | "attributes": { 1440 | "include.in.token.scope": "false", 1441 | "display.on.consent.screen": "true", 1442 | "consent.screen.text": "${rolesScopeConsentText}" 1443 | }, 1444 | "protocolMappers": [ 1445 | { 1446 | "id": "6d821201-a3a8-472c-a5b4-cac07a73ebe7", 1447 | "name": "client roles", 1448 | "protocol": "openid-connect", 1449 | "protocolMapper": "oidc-usermodel-client-role-mapper", 1450 | "consentRequired": false, 1451 | "config": { 1452 | "user.attribute": "foo", 1453 | "access.token.claim": "true", 1454 | "claim.name": "resource_access.${client_id}.roles", 1455 | "jsonType.label": "String", 1456 | "multivalued": "true" 1457 | } 1458 | }, 1459 | { 1460 | "id": "7754cb19-0c59-429d-ae4f-ce9e09aa8262", 1461 | "name": "audience resolve", 1462 | "protocol": "openid-connect", 1463 | "protocolMapper": "oidc-audience-resolve-mapper", 1464 | "consentRequired": false, 1465 | "config": { 1466 | 1467 | } 1468 | }, 1469 | { 1470 | "id": "783ec068-7754-49fa-8c4f-c42aa3a00f99", 1471 | "name": "realm roles", 1472 | "protocol": "openid-connect", 1473 | "protocolMapper": "oidc-usermodel-realm-role-mapper", 1474 | "consentRequired": false, 1475 | "config": { 1476 | "user.attribute": "foo", 1477 | "access.token.claim": "true", 1478 | "claim.name": "realm_access.roles", 1479 | "jsonType.label": "String", 1480 | "multivalued": "true" 1481 | } 1482 | } 1483 | ] 1484 | }, 1485 | { 1486 | "id": "184aed62-4b87-49e7-8fa2-a4b5f78c8dab", 1487 | "name": "web-origins", 1488 | "description": "OpenID Connect scope for add allowed web origins to the access token", 1489 | "protocol": "openid-connect", 1490 | "attributes": { 1491 | "include.in.token.scope": "false", 1492 | "display.on.consent.screen": "false", 1493 | "consent.screen.text": "" 1494 | }, 1495 | "protocolMappers": [ 1496 | { 1497 | "id": "a3ca7d7b-fe1b-4ed1-8d3e-8c9f1c6563d4", 1498 | "name": "allowed web origins", 1499 | "protocol": "openid-connect", 1500 | "protocolMapper": "oidc-allowed-origins-mapper", 1501 | "consentRequired": false, 1502 | "config": { 1503 | 1504 | } 1505 | } 1506 | ] 1507 | } 1508 | ], 1509 | "defaultDefaultClientScopes": [ 1510 | "web-origins", 1511 | "email", 1512 | "profile", 1513 | "role_list", 1514 | "roles" 1515 | ], 1516 | "defaultOptionalClientScopes": [ 1517 | "phone", 1518 | "offline_access", 1519 | "address" 1520 | ], 1521 | "browserSecurityHeaders": { 1522 | "contentSecurityPolicyReportOnly": "", 1523 | "xContentTypeOptions": "nosniff", 1524 | "xRobotsTag": "none", 1525 | "xFrameOptions": "SAMEORIGIN", 1526 | "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", 1527 | "xXSSProtection": "1; mode=block", 1528 | "strictTransportSecurity": "max-age=31536000; includeSubDomains" 1529 | }, 1530 | "smtpServer": { 1531 | 1532 | }, 1533 | "eventsEnabled": false, 1534 | "eventsListeners": [ 1535 | "jboss-logging" 1536 | ], 1537 | "enabledEventTypes": [], 1538 | "adminEventsEnabled": false, 1539 | "adminEventsDetailsEnabled": false, 1540 | "components": { 1541 | "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [ 1542 | { 1543 | "id": "04f7110d-6fcb-47ac-9c4d-b00520257a51", 1544 | "name": "Trusted Hosts", 1545 | "providerId": "trusted-hosts", 1546 | "subType": "anonymous", 1547 | "subComponents": { 1548 | 1549 | }, 1550 | "config": { 1551 | "host-sending-registration-request-must-match": [ 1552 | "true" 1553 | ], 1554 | "client-uris-must-match": [ 1555 | "true" 1556 | ] 1557 | } 1558 | }, 1559 | { 1560 | "id": "80bedeb0-441d-4706-aeae-55a40cc186eb", 1561 | "name": "Max Clients Limit", 1562 | "providerId": "max-clients", 1563 | "subType": "anonymous", 1564 | "subComponents": { 1565 | 1566 | }, 1567 | "config": { 1568 | "max-clients": [ 1569 | "200" 1570 | ] 1571 | } 1572 | }, 1573 | { 1574 | "id": "444e11e9-8ebc-4486-9b41-5279a87d9ba8", 1575 | "name": "Consent Required", 1576 | "providerId": "consent-required", 1577 | "subType": "anonymous", 1578 | "subComponents": { 1579 | 1580 | }, 1581 | "config": { 1582 | 1583 | } 1584 | }, 1585 | { 1586 | "id": "2c631272-daf0-47f5-9769-50311da97e8c", 1587 | "name": "Allowed Protocol Mapper Types", 1588 | "providerId": "allowed-protocol-mappers", 1589 | "subType": "authenticated", 1590 | "subComponents": { 1591 | 1592 | }, 1593 | "config": { 1594 | "allowed-protocol-mapper-types": [ 1595 | "saml-role-list-mapper", 1596 | "oidc-usermodel-property-mapper", 1597 | "oidc-usermodel-attribute-mapper", 1598 | "saml-user-property-mapper", 1599 | "saml-user-attribute-mapper", 1600 | "oidc-address-mapper", 1601 | "oidc-full-name-mapper", 1602 | "oidc-sha256-pairwise-sub-mapper" 1603 | ] 1604 | } 1605 | }, 1606 | { 1607 | "id": "ec0b1ae8-b587-44bc-a4f1-b8a239f45ca0", 1608 | "name": "Allowed Protocol Mapper Types", 1609 | "providerId": "allowed-protocol-mappers", 1610 | "subType": "anonymous", 1611 | "subComponents": { 1612 | 1613 | }, 1614 | "config": { 1615 | "allowed-protocol-mapper-types": [ 1616 | "oidc-usermodel-attribute-mapper", 1617 | "oidc-address-mapper", 1618 | "saml-user-attribute-mapper", 1619 | "saml-user-property-mapper", 1620 | "oidc-sha256-pairwise-sub-mapper", 1621 | "oidc-usermodel-property-mapper", 1622 | "saml-role-list-mapper", 1623 | "oidc-full-name-mapper" 1624 | ] 1625 | } 1626 | }, 1627 | { 1628 | "id": "950bf122-55cc-4f4b-bd57-59c334c9551a", 1629 | "name": "Allowed Client Scopes", 1630 | "providerId": "allowed-client-templates", 1631 | "subType": "anonymous", 1632 | "subComponents": { 1633 | 1634 | }, 1635 | "config": { 1636 | "allow-default-scopes": [ 1637 | "true" 1638 | ] 1639 | } 1640 | }, 1641 | { 1642 | "id": "81aa8925-0d09-4f65-b735-ad81fabeeb8c", 1643 | "name": "Allowed Client Scopes", 1644 | "providerId": "allowed-client-templates", 1645 | "subType": "authenticated", 1646 | "subComponents": { 1647 | 1648 | }, 1649 | "config": { 1650 | "allow-default-scopes": [ 1651 | "true" 1652 | ] 1653 | } 1654 | }, 1655 | { 1656 | "id": "9a3ff53f-7187-4eb4-8d65-fe88e1f95cfe", 1657 | "name": "Full Scope Disabled", 1658 | "providerId": "scope", 1659 | "subType": "anonymous", 1660 | "subComponents": { 1661 | 1662 | }, 1663 | "config": { 1664 | 1665 | } 1666 | } 1667 | ], 1668 | "org.keycloak.keys.KeyProvider": [ 1669 | { 1670 | "id": "427f2345-e1e4-4142-8bba-a0ed1bc76bf5", 1671 | "name": "aes-generated", 1672 | "providerId": "aes-generated", 1673 | "subComponents": { 1674 | 1675 | }, 1676 | "config": { 1677 | "priority": [ 1678 | "100" 1679 | ] 1680 | } 1681 | }, 1682 | { 1683 | "id": "61f5d2e6-c9d5-4f85-b84d-39583854f20d", 1684 | "name": "rsa-generated", 1685 | "providerId": "rsa-generated", 1686 | "subComponents": { 1687 | 1688 | }, 1689 | "config": { 1690 | "priority": [ 1691 | "100" 1692 | ] 1693 | } 1694 | }, 1695 | { 1696 | "id": "398af3e1-4263-49c1-a017-1515afc28659", 1697 | "name": "hmac-generated", 1698 | "providerId": "hmac-generated", 1699 | "subComponents": { 1700 | 1701 | }, 1702 | "config": { 1703 | "priority": [ 1704 | "100" 1705 | ], 1706 | "algorithm": [ 1707 | "HS256" 1708 | ] 1709 | } 1710 | } 1711 | ] 1712 | }, 1713 | "internationalizationEnabled": false, 1714 | "supportedLocales": [], 1715 | "authenticationFlows": [ 1716 | { 1717 | "id": "c7a08183-14e3-4cfd-b3c1-e3c8f8cf70b4", 1718 | "alias": "Handle Existing Account", 1719 | "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider", 1720 | "providerId": "basic-flow", 1721 | "topLevel": false, 1722 | "builtIn": true, 1723 | "authenticationExecutions": [ 1724 | { 1725 | "authenticator": "idp-confirm-link", 1726 | "requirement": "REQUIRED", 1727 | "priority": 10, 1728 | "userSetupAllowed": false, 1729 | "autheticatorFlow": false 1730 | }, 1731 | { 1732 | "requirement": "REQUIRED", 1733 | "priority": 20, 1734 | "flowAlias": "Handle Existing Account - Alternatives - 0", 1735 | "userSetupAllowed": false, 1736 | "autheticatorFlow": true 1737 | } 1738 | ] 1739 | }, 1740 | { 1741 | "id": "00479e52-3185-4aed-b27d-b06b1fb7dc8c", 1742 | "alias": "Handle Existing Account - Alternatives - 0", 1743 | "description": "Subflow of Handle Existing Account with alternative executions", 1744 | "providerId": "basic-flow", 1745 | "topLevel": false, 1746 | "builtIn": true, 1747 | "authenticationExecutions": [ 1748 | { 1749 | "authenticator": "idp-email-verification", 1750 | "requirement": "ALTERNATIVE", 1751 | "priority": 10, 1752 | "userSetupAllowed": false, 1753 | "autheticatorFlow": false 1754 | }, 1755 | { 1756 | "requirement": "ALTERNATIVE", 1757 | "priority": 20, 1758 | "flowAlias": "Verify Existing Account by Re-authentication", 1759 | "userSetupAllowed": false, 1760 | "autheticatorFlow": true 1761 | } 1762 | ] 1763 | }, 1764 | { 1765 | "id": "5ab570c8-5f5c-4639-b128-3dbc042cf31a", 1766 | "alias": "Verify Existing Account by Re-authentication", 1767 | "description": "Reauthentication of existing account", 1768 | "providerId": "basic-flow", 1769 | "topLevel": false, 1770 | "builtIn": true, 1771 | "authenticationExecutions": [ 1772 | { 1773 | "authenticator": "idp-username-password-form", 1774 | "requirement": "REQUIRED", 1775 | "priority": 10, 1776 | "userSetupAllowed": false, 1777 | "autheticatorFlow": false 1778 | }, 1779 | { 1780 | "requirement": "CONDITIONAL", 1781 | "priority": 20, 1782 | "flowAlias": "Verify Existing Account by Re-authentication - auth-otp-form - Conditional", 1783 | "userSetupAllowed": false, 1784 | "autheticatorFlow": true 1785 | } 1786 | ] 1787 | }, 1788 | { 1789 | "id": "68418910-67dc-4aa0-8a80-98a34b1c7ef6", 1790 | "alias": "Verify Existing Account by Re-authentication - auth-otp-form - Conditional", 1791 | "description": "Flow to determine if the auth-otp-form authenticator should be used or not.", 1792 | "providerId": "basic-flow", 1793 | "topLevel": false, 1794 | "builtIn": true, 1795 | "authenticationExecutions": [ 1796 | { 1797 | "authenticator": "conditional-user-configured", 1798 | "requirement": "REQUIRED", 1799 | "priority": 10, 1800 | "userSetupAllowed": false, 1801 | "autheticatorFlow": false 1802 | }, 1803 | { 1804 | "authenticator": "auth-otp-form", 1805 | "requirement": "REQUIRED", 1806 | "priority": 20, 1807 | "userSetupAllowed": false, 1808 | "autheticatorFlow": false 1809 | } 1810 | ] 1811 | }, 1812 | { 1813 | "id": "adad97de-8c82-46fb-9937-1c5571213862", 1814 | "alias": "browser", 1815 | "description": "browser based authentication", 1816 | "providerId": "basic-flow", 1817 | "topLevel": true, 1818 | "builtIn": true, 1819 | "authenticationExecutions": [ 1820 | { 1821 | "authenticator": "auth-cookie", 1822 | "requirement": "ALTERNATIVE", 1823 | "priority": 10, 1824 | "userSetupAllowed": false, 1825 | "autheticatorFlow": false 1826 | }, 1827 | { 1828 | "authenticator": "auth-spnego", 1829 | "requirement": "DISABLED", 1830 | "priority": 20, 1831 | "userSetupAllowed": false, 1832 | "autheticatorFlow": false 1833 | }, 1834 | { 1835 | "authenticator": "identity-provider-redirector", 1836 | "requirement": "ALTERNATIVE", 1837 | "priority": 25, 1838 | "userSetupAllowed": false, 1839 | "autheticatorFlow": false 1840 | }, 1841 | { 1842 | "requirement": "ALTERNATIVE", 1843 | "priority": 30, 1844 | "flowAlias": "forms", 1845 | "userSetupAllowed": false, 1846 | "autheticatorFlow": true 1847 | } 1848 | ] 1849 | }, 1850 | { 1851 | "id": "c74d7719-85bd-4ca7-8820-efe645075c08", 1852 | "alias": "clients", 1853 | "description": "Base authentication for clients", 1854 | "providerId": "client-flow", 1855 | "topLevel": true, 1856 | "builtIn": true, 1857 | "authenticationExecutions": [ 1858 | { 1859 | "authenticator": "client-secret", 1860 | "requirement": "ALTERNATIVE", 1861 | "priority": 10, 1862 | "userSetupAllowed": false, 1863 | "autheticatorFlow": false 1864 | }, 1865 | { 1866 | "authenticator": "client-jwt", 1867 | "requirement": "ALTERNATIVE", 1868 | "priority": 20, 1869 | "userSetupAllowed": false, 1870 | "autheticatorFlow": false 1871 | }, 1872 | { 1873 | "authenticator": "client-secret-jwt", 1874 | "requirement": "ALTERNATIVE", 1875 | "priority": 30, 1876 | "userSetupAllowed": false, 1877 | "autheticatorFlow": false 1878 | }, 1879 | { 1880 | "authenticator": "client-x509", 1881 | "requirement": "ALTERNATIVE", 1882 | "priority": 40, 1883 | "userSetupAllowed": false, 1884 | "autheticatorFlow": false 1885 | } 1886 | ] 1887 | }, 1888 | { 1889 | "id": "61434aa1-dd71-42cf-b0b3-3686f95b9512", 1890 | "alias": "direct grant", 1891 | "description": "OpenID Connect Resource Owner Grant", 1892 | "providerId": "basic-flow", 1893 | "topLevel": true, 1894 | "builtIn": true, 1895 | "authenticationExecutions": [ 1896 | { 1897 | "authenticator": "direct-grant-validate-username", 1898 | "requirement": "REQUIRED", 1899 | "priority": 10, 1900 | "userSetupAllowed": false, 1901 | "autheticatorFlow": false 1902 | }, 1903 | { 1904 | "authenticator": "direct-grant-validate-password", 1905 | "requirement": "REQUIRED", 1906 | "priority": 20, 1907 | "userSetupAllowed": false, 1908 | "autheticatorFlow": false 1909 | }, 1910 | { 1911 | "requirement": "CONDITIONAL", 1912 | "priority": 30, 1913 | "flowAlias": "direct grant - direct-grant-validate-otp - Conditional", 1914 | "userSetupAllowed": false, 1915 | "autheticatorFlow": true 1916 | } 1917 | ] 1918 | }, 1919 | { 1920 | "id": "2aafb64a-b90c-4dfe-b14f-b68b0930f839", 1921 | "alias": "direct grant - direct-grant-validate-otp - Conditional", 1922 | "description": "Flow to determine if the direct-grant-validate-otp authenticator should be used or not.", 1923 | "providerId": "basic-flow", 1924 | "topLevel": false, 1925 | "builtIn": true, 1926 | "authenticationExecutions": [ 1927 | { 1928 | "authenticator": "conditional-user-configured", 1929 | "requirement": "REQUIRED", 1930 | "priority": 10, 1931 | "userSetupAllowed": false, 1932 | "autheticatorFlow": false 1933 | }, 1934 | { 1935 | "authenticator": "direct-grant-validate-otp", 1936 | "requirement": "REQUIRED", 1937 | "priority": 20, 1938 | "userSetupAllowed": false, 1939 | "autheticatorFlow": false 1940 | } 1941 | ] 1942 | }, 1943 | { 1944 | "id": "600c938a-90a4-4066-9edf-eb223893f964", 1945 | "alias": "docker auth", 1946 | "description": "Used by Docker clients to authenticate against the IDP", 1947 | "providerId": "basic-flow", 1948 | "topLevel": true, 1949 | "builtIn": true, 1950 | "authenticationExecutions": [ 1951 | { 1952 | "authenticator": "docker-http-basic-authenticator", 1953 | "requirement": "REQUIRED", 1954 | "priority": 10, 1955 | "userSetupAllowed": false, 1956 | "autheticatorFlow": false 1957 | } 1958 | ] 1959 | }, 1960 | { 1961 | "id": "052709bc-272b-4ae3-9972-80d2bcb20d01", 1962 | "alias": "first broker login", 1963 | "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", 1964 | "providerId": "basic-flow", 1965 | "topLevel": true, 1966 | "builtIn": true, 1967 | "authenticationExecutions": [ 1968 | { 1969 | "authenticatorConfig": "review profile config", 1970 | "authenticator": "idp-review-profile", 1971 | "requirement": "REQUIRED", 1972 | "priority": 10, 1973 | "userSetupAllowed": false, 1974 | "autheticatorFlow": false 1975 | }, 1976 | { 1977 | "requirement": "REQUIRED", 1978 | "priority": 20, 1979 | "flowAlias": "first broker login - Alternatives - 0", 1980 | "userSetupAllowed": false, 1981 | "autheticatorFlow": true 1982 | } 1983 | ] 1984 | }, 1985 | { 1986 | "id": "00e36017-d36c-4f71-b27d-280506a8c8b9", 1987 | "alias": "first broker login - Alternatives - 0", 1988 | "description": "Subflow of first broker login with alternative executions", 1989 | "providerId": "basic-flow", 1990 | "topLevel": false, 1991 | "builtIn": true, 1992 | "authenticationExecutions": [ 1993 | { 1994 | "authenticatorConfig": "create unique user config", 1995 | "authenticator": "idp-create-user-if-unique", 1996 | "requirement": "ALTERNATIVE", 1997 | "priority": 10, 1998 | "userSetupAllowed": false, 1999 | "autheticatorFlow": false 2000 | }, 2001 | { 2002 | "requirement": "ALTERNATIVE", 2003 | "priority": 20, 2004 | "flowAlias": "Handle Existing Account", 2005 | "userSetupAllowed": false, 2006 | "autheticatorFlow": true 2007 | } 2008 | ] 2009 | }, 2010 | { 2011 | "id": "0aaca3e3-1c3d-424c-8d0c-409c7a98a85a", 2012 | "alias": "forms", 2013 | "description": "Username, password, otp and other auth forms.", 2014 | "providerId": "basic-flow", 2015 | "topLevel": false, 2016 | "builtIn": true, 2017 | "authenticationExecutions": [ 2018 | { 2019 | "authenticator": "auth-username-password-form", 2020 | "requirement": "REQUIRED", 2021 | "priority": 10, 2022 | "userSetupAllowed": false, 2023 | "autheticatorFlow": false 2024 | }, 2025 | { 2026 | "requirement": "CONDITIONAL", 2027 | "priority": 20, 2028 | "flowAlias": "forms - auth-otp-form - Conditional", 2029 | "userSetupAllowed": false, 2030 | "autheticatorFlow": true 2031 | } 2032 | ] 2033 | }, 2034 | { 2035 | "id": "94a1f6bd-d314-47f8-ab65-4baf60be8206", 2036 | "alias": "forms - auth-otp-form - Conditional", 2037 | "description": "Flow to determine if the auth-otp-form authenticator should be used or not.", 2038 | "providerId": "basic-flow", 2039 | "topLevel": false, 2040 | "builtIn": true, 2041 | "authenticationExecutions": [ 2042 | { 2043 | "authenticator": "conditional-user-configured", 2044 | "requirement": "REQUIRED", 2045 | "priority": 10, 2046 | "userSetupAllowed": false, 2047 | "autheticatorFlow": false 2048 | }, 2049 | { 2050 | "authenticator": "auth-otp-form", 2051 | "requirement": "REQUIRED", 2052 | "priority": 20, 2053 | "userSetupAllowed": false, 2054 | "autheticatorFlow": false 2055 | } 2056 | ] 2057 | }, 2058 | { 2059 | "id": "3bc923a5-3477-4c3b-a3ac-e3dc57be2687", 2060 | "alias": "http challenge", 2061 | "description": "An authentication flow based on challenge-response HTTP Authentication Schemes", 2062 | "providerId": "basic-flow", 2063 | "topLevel": true, 2064 | "builtIn": true, 2065 | "authenticationExecutions": [ 2066 | { 2067 | "authenticator": "no-cookie-redirect", 2068 | "requirement": "REQUIRED", 2069 | "priority": 10, 2070 | "userSetupAllowed": false, 2071 | "autheticatorFlow": false 2072 | }, 2073 | { 2074 | "authenticator": "basic-auth", 2075 | "requirement": "REQUIRED", 2076 | "priority": 20, 2077 | "userSetupAllowed": false, 2078 | "autheticatorFlow": false 2079 | }, 2080 | { 2081 | "authenticator": "basic-auth-otp", 2082 | "requirement": "DISABLED", 2083 | "priority": 30, 2084 | "userSetupAllowed": false, 2085 | "autheticatorFlow": false 2086 | }, 2087 | { 2088 | "authenticator": "auth-spnego", 2089 | "requirement": "DISABLED", 2090 | "priority": 40, 2091 | "userSetupAllowed": false, 2092 | "autheticatorFlow": false 2093 | } 2094 | ] 2095 | }, 2096 | { 2097 | "id": "88f13bf8-5bf7-46b9-b836-731d896ed970", 2098 | "alias": "registration", 2099 | "description": "registration flow", 2100 | "providerId": "basic-flow", 2101 | "topLevel": true, 2102 | "builtIn": true, 2103 | "authenticationExecutions": [ 2104 | { 2105 | "authenticator": "registration-page-form", 2106 | "requirement": "REQUIRED", 2107 | "priority": 10, 2108 | "flowAlias": "registration form", 2109 | "userSetupAllowed": false, 2110 | "autheticatorFlow": true 2111 | } 2112 | ] 2113 | }, 2114 | { 2115 | "id": "dc6e2244-1173-492f-ada1-fbcb0467c02b", 2116 | "alias": "registration form", 2117 | "description": "registration form", 2118 | "providerId": "form-flow", 2119 | "topLevel": false, 2120 | "builtIn": true, 2121 | "authenticationExecutions": [ 2122 | { 2123 | "authenticator": "registration-user-creation", 2124 | "requirement": "REQUIRED", 2125 | "priority": 20, 2126 | "userSetupAllowed": false, 2127 | "autheticatorFlow": false 2128 | }, 2129 | { 2130 | "authenticator": "registration-profile-action", 2131 | "requirement": "REQUIRED", 2132 | "priority": 40, 2133 | "userSetupAllowed": false, 2134 | "autheticatorFlow": false 2135 | }, 2136 | { 2137 | "authenticator": "registration-password-action", 2138 | "requirement": "REQUIRED", 2139 | "priority": 50, 2140 | "userSetupAllowed": false, 2141 | "autheticatorFlow": false 2142 | }, 2143 | { 2144 | "authenticator": "registration-recaptcha-action", 2145 | "requirement": "DISABLED", 2146 | "priority": 60, 2147 | "userSetupAllowed": false, 2148 | "autheticatorFlow": false 2149 | } 2150 | ] 2151 | }, 2152 | { 2153 | "id": "fb5cf5e8-248c-4894-9364-047e9f845697", 2154 | "alias": "reset credentials", 2155 | "description": "Reset credentials for a user if they forgot their password or something", 2156 | "providerId": "basic-flow", 2157 | "topLevel": true, 2158 | "builtIn": true, 2159 | "authenticationExecutions": [ 2160 | { 2161 | "authenticator": "reset-credentials-choose-user", 2162 | "requirement": "REQUIRED", 2163 | "priority": 10, 2164 | "userSetupAllowed": false, 2165 | "autheticatorFlow": false 2166 | }, 2167 | { 2168 | "authenticator": "reset-credential-email", 2169 | "requirement": "REQUIRED", 2170 | "priority": 20, 2171 | "userSetupAllowed": false, 2172 | "autheticatorFlow": false 2173 | }, 2174 | { 2175 | "authenticator": "reset-password", 2176 | "requirement": "REQUIRED", 2177 | "priority": 30, 2178 | "userSetupAllowed": false, 2179 | "autheticatorFlow": false 2180 | }, 2181 | { 2182 | "requirement": "CONDITIONAL", 2183 | "priority": 40, 2184 | "flowAlias": "reset credentials - reset-otp - Conditional", 2185 | "userSetupAllowed": false, 2186 | "autheticatorFlow": true 2187 | } 2188 | ] 2189 | }, 2190 | { 2191 | "id": "c2061e01-fe50-4bb5-aaf3-88c89aef9983", 2192 | "alias": "reset credentials - reset-otp - Conditional", 2193 | "description": "Flow to determine if the reset-otp authenticator should be used or not.", 2194 | "providerId": "basic-flow", 2195 | "topLevel": false, 2196 | "builtIn": true, 2197 | "authenticationExecutions": [ 2198 | { 2199 | "authenticator": "conditional-user-configured", 2200 | "requirement": "REQUIRED", 2201 | "priority": 10, 2202 | "userSetupAllowed": false, 2203 | "autheticatorFlow": false 2204 | }, 2205 | { 2206 | "authenticator": "reset-otp", 2207 | "requirement": "REQUIRED", 2208 | "priority": 20, 2209 | "userSetupAllowed": false, 2210 | "autheticatorFlow": false 2211 | } 2212 | ] 2213 | }, 2214 | { 2215 | "id": "47a01b7e-65d7-40ac-a759-c0e087595655", 2216 | "alias": "saml ecp", 2217 | "description": "SAML ECP Profile Authentication Flow", 2218 | "providerId": "basic-flow", 2219 | "topLevel": true, 2220 | "builtIn": true, 2221 | "authenticationExecutions": [ 2222 | { 2223 | "authenticator": "http-basic-authenticator", 2224 | "requirement": "REQUIRED", 2225 | "priority": 10, 2226 | "userSetupAllowed": false, 2227 | "autheticatorFlow": false 2228 | } 2229 | ] 2230 | } 2231 | ], 2232 | "authenticatorConfig": [ 2233 | { 2234 | "id": "78d4b5c0-50c8-4d8c-934c-4a7fe3f0ed90", 2235 | "alias": "create unique user config", 2236 | "config": { 2237 | "require.password.update.after.registration": "false" 2238 | } 2239 | }, 2240 | { 2241 | "id": "34578d2c-67c7-4144-9992-554df722600b", 2242 | "alias": "review profile config", 2243 | "config": { 2244 | "update.profile.on.first.login": "missing" 2245 | } 2246 | } 2247 | ], 2248 | "requiredActions": [ 2249 | { 2250 | "alias": "CONFIGURE_TOTP", 2251 | "name": "Configure OTP", 2252 | "providerId": "CONFIGURE_TOTP", 2253 | "enabled": true, 2254 | "defaultAction": false, 2255 | "priority": 10, 2256 | "config": { 2257 | 2258 | } 2259 | }, 2260 | { 2261 | "alias": "terms_and_conditions", 2262 | "name": "Terms and Conditions", 2263 | "providerId": "terms_and_conditions", 2264 | "enabled": false, 2265 | "defaultAction": false, 2266 | "priority": 20, 2267 | "config": { 2268 | 2269 | } 2270 | }, 2271 | { 2272 | "alias": "UPDATE_PASSWORD", 2273 | "name": "Update Password", 2274 | "providerId": "UPDATE_PASSWORD", 2275 | "enabled": true, 2276 | "defaultAction": false, 2277 | "priority": 30, 2278 | "config": { 2279 | 2280 | } 2281 | }, 2282 | { 2283 | "alias": "UPDATE_PROFILE", 2284 | "name": "Update Profile", 2285 | "providerId": "UPDATE_PROFILE", 2286 | "enabled": true, 2287 | "defaultAction": false, 2288 | "priority": 40, 2289 | "config": { 2290 | 2291 | } 2292 | }, 2293 | { 2294 | "alias": "VERIFY_EMAIL", 2295 | "name": "Verify Email", 2296 | "providerId": "VERIFY_EMAIL", 2297 | "enabled": true, 2298 | "defaultAction": false, 2299 | "priority": 50, 2300 | "config": { 2301 | 2302 | } 2303 | }, 2304 | { 2305 | "alias": "update_user_locale", 2306 | "name": "Update User Locale", 2307 | "providerId": "update_user_locale", 2308 | "enabled": true, 2309 | "defaultAction": false, 2310 | "priority": 1000, 2311 | "config": { 2312 | 2313 | } 2314 | } 2315 | ], 2316 | "browserFlow": "browser", 2317 | "registrationFlow": "registration", 2318 | "directGrantFlow": "direct grant", 2319 | "resetCredentialsFlow": "reset credentials", 2320 | "clientAuthenticationFlow": "clients", 2321 | "dockerAuthenticationFlow": "docker auth", 2322 | "attributes": { 2323 | 2324 | }, 2325 | "keycloakVersion": "10.0.2", 2326 | "userManagedAccessAllowed": false, 2327 | "users": [ 2328 | { 2329 | "id": "14ea5a94-296b-4890-bfe7-32490bde43cb", 2330 | "createdTimestamp": 1545877743226, 2331 | "username": "admin", 2332 | "enabled": true, 2333 | "totp": false, 2334 | "emailVerified": true, 2335 | "credentials": [ 2336 | { 2337 | "type": "password", 2338 | "hashedSaltedValue": "TkvYI+asEUt+tytcAnIStiDq1wZsQGq0Rfgap9kFRm4tm1Cnbte0tZlWC8tSKqWWU9YiIv/eY0zUhZTjHzqKow==", 2339 | "salt": "wB6x/52DN64UsR/0A4c0GQ==", 2340 | "hashIterations": 27500, 2341 | "counter": 0, 2342 | "algorithm": "pbkdf2-sha256", 2343 | "digits": 0, 2344 | "period": 0, 2345 | "createdDate": 1545877751179, 2346 | "config": { 2347 | 2348 | } 2349 | } 2350 | ], 2351 | "disableableCredentialTypes": [ 2352 | "password" 2353 | ], 2354 | "requiredActions": [], 2355 | "realmRoles": [ 2356 | "User", 2357 | "uma_authorization", 2358 | "Admin", 2359 | "offline_access" 2360 | ], 2361 | "clientRoles": { 2362 | "account": [ 2363 | "view-profile", 2364 | "manage-account" 2365 | ] 2366 | }, 2367 | "notBefore": 0, 2368 | "groups": [] 2369 | }, 2370 | { 2371 | "id": "91c5c1e5-5124-4c6f-9287-b579094b817d", 2372 | "createdTimestamp": 1545877804738, 2373 | "username": "kerri", 2374 | "enabled": true, 2375 | "totp": false, 2376 | "emailVerified": true, 2377 | "credentials": [ 2378 | { 2379 | "type": "password", 2380 | "hashedSaltedValue": "SqoEcdaGz1HEGAXr+EfTXL9RjI7H4GiodQLHA9TUUDeC4sk+ChXxpe3PlBrMxYc9ppOIhgD8i5rQf2iavNxRfQ==", 2381 | "salt": "9bpVr0yHdudO4vcWp+Jalg==", 2382 | "hashIterations": 27500, 2383 | "counter": 0, 2384 | "algorithm": "pbkdf2-sha256", 2385 | "digits": 0, 2386 | "period": 0, 2387 | "createdDate": 1545877819641, 2388 | "config": { 2389 | 2390 | } 2391 | } 2392 | ], 2393 | "disableableCredentialTypes": [ 2394 | "password" 2395 | ], 2396 | "requiredActions": [], 2397 | "realmRoles": [ 2398 | "User", 2399 | "uma_authorization", 2400 | "offline_access" 2401 | ], 2402 | "clientRoles": { 2403 | "account": [ 2404 | "view-profile", 2405 | "manage-account" 2406 | ] 2407 | }, 2408 | "notBefore": 0, 2409 | "groups": [] 2410 | }, 2411 | { 2412 | "id": "db8467be-98df-4576-8df3-8844a75fc9b4", 2413 | "createdTimestamp": 1545885405326, 2414 | "username": "norole", 2415 | "enabled": true, 2416 | "totp": false, 2417 | "emailVerified": true, 2418 | "credentials": [ 2419 | { 2420 | "type": "password", 2421 | "hashedSaltedValue": "lQCRB/RfqYHn68Vu1Q4aNF+WXwZndy7FGhH4mJzIzAG/SM0lOdMlpv0Am04AV4sFarGvG+bxNH4qhJ+Qok876w==", 2422 | "salt": "3thsCjqySzqjxBNgauGJ6g==", 2423 | "hashIterations": 27500, 2424 | "counter": 0, 2425 | "algorithm": "pbkdf2-sha256", 2426 | "digits": 0, 2427 | "period": 0, 2428 | "createdDate": 1545885410604, 2429 | "config": { 2430 | 2431 | } 2432 | } 2433 | ], 2434 | "disableableCredentialTypes": [ 2435 | "password" 2436 | ], 2437 | "requiredActions": [], 2438 | "realmRoles": [ 2439 | "uma_authorization", 2440 | "offline_access" 2441 | ], 2442 | "clientRoles": { 2443 | "account": [ 2444 | "view-profile", 2445 | "manage-account" 2446 | ] 2447 | }, 2448 | "notBefore": 0, 2449 | "groups": [] 2450 | } 2451 | ] 2452 | } --------------------------------------------------------------------------------