├── demoapp
├── run.sh
└── index.html
├── etc
├── runDemoApp.sh
├── triggerDockerExtensionDeploy.sh
└── exportRealm.sh
├── requests
├── acme.http
└── http-client.env.json
├── acme-themes
├── src
│ └── main
│ │ └── resources
│ │ └── theme
│ │ └── acme
│ │ └── login
│ │ ├── resources
│ │ ├── js
│ │ │ └── acme-login.js
│ │ └── css
│ │ │ └── acme-login.css
│ │ └── theme.properties
└── pom.xml
├── slides
└── 20210406-JUGSaar-keycloak-extension-development.pdf
├── acme-extensions
├── src
│ ├── test
│ │ ├── resources
│ │ │ └── log4j.properties
│ │ └── java
│ │ │ └── demo
│ │ │ └── acme
│ │ │ └── keycloak
│ │ │ ├── BoostrapTest.java
│ │ │ ├── KeycloakTestSupport.java
│ │ │ └── AcmeKeycloakIntegrationTest.java
│ └── main
│ │ ├── java
│ │ └── demo
│ │ │ └── acme
│ │ │ └── keycloak
│ │ │ ├── api
│ │ │ ├── AcmeResourceProviderFactory.java
│ │ │ ├── AcmeResource.java
│ │ │ └── AcmeResourceProvider.java
│ │ │ ├── audit
│ │ │ ├── AcmeAuditListenerFactory.java
│ │ │ └── AcmeAuditListener.java
│ │ │ └── oidc
│ │ │ └── AgeInfoMapper.java
│ │ └── resources
│ │ └── META-INF
│ │ └── jboss-deployment-structure.xml
└── pom.xml
├── acme-dist
├── src
│ └── main
│ │ └── docker
│ │ └── keycloak
│ │ └── Dockerfile
└── pom.xml
├── docker-compose.yml
├── pom.xml
├── cli
└── onstart-0001-init.cli
├── readme.md
├── notes.md
├── .gitignore
└── imex
└── acme-realm.json
/demoapp/run.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | python3 -m http.server 4000
--------------------------------------------------------------------------------
/etc/runDemoApp.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | (cd demoapp; python3 -m http.server 4000)
--------------------------------------------------------------------------------
/requests/acme.http:
--------------------------------------------------------------------------------
1 | ### Call ping resource as user
2 | GET {{KC_ISSUER_URL}}/acme-resources/ping
3 | Accept: application/json
4 | Authorization: Bearer {{testClientToken}}
--------------------------------------------------------------------------------
/acme-themes/src/main/resources/theme/acme/login/resources/js/acme-login.js:
--------------------------------------------------------------------------------
1 | // acme-login.js
2 |
3 | (function onAcmeLogin() {
4 | console.log("acme login jugsaar");
5 | })();
6 |
7 |
--------------------------------------------------------------------------------
/slides/20210406-JUGSaar-keycloak-extension-development.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomasdarimont/keycloak-extensions-talk/HEAD/slides/20210406-JUGSaar-keycloak-extension-development.pdf
--------------------------------------------------------------------------------
/etc/triggerDockerExtensionDeploy.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -eou pipefail
4 |
5 | docker-compose exec -T keycloak \
6 | touch /opt/jboss/keycloak/standalone/deployments/acme-extensions.jar.dodeploy
--------------------------------------------------------------------------------
/acme-extensions/src/test/resources/log4j.properties:
--------------------------------------------------------------------------------
1 | log4j.rootLogger=INFO,stdout
2 |
3 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender
4 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
5 | log4j.appender.stdout.layout.ConversionPattern=%p\t%d{ISO8601}\t%r\t%c\t[%t]\t%m%n
--------------------------------------------------------------------------------
/acme-extensions/src/test/java/demo/acme/keycloak/BoostrapTest.java:
--------------------------------------------------------------------------------
1 | package demo.acme.keycloak;
2 |
3 | import org.junit.jupiter.api.Assertions;
4 | import org.junit.jupiter.api.Test;
5 |
6 | public class BoostrapTest {
7 |
8 | @Test
9 | public void shouldRunAsUnitTest() {
10 | Assertions.assertTrue(true);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/acme-dist/src/main/docker/keycloak/Dockerfile:
--------------------------------------------------------------------------------
1 | ARG KEYCLOAK_VERSION=12.0.4
2 | FROM quay.io/keycloak/keycloak:$KEYCLOAK_VERSION
3 |
4 | COPY --chown=jboss:jboss maven/cli/ /opt/jboss/startup-scripts
5 | COPY --chown=jboss:jboss maven/acme-extensions/ /opt/jboss/keycloak/standalone/deployments
6 | COPY --chown=jboss:jboss maven/acme-theme /opt/jboss/keycloak/themes/acme
7 |
8 |
--------------------------------------------------------------------------------
/etc/exportRealm.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -eou pipefail
4 |
5 | docker-compose exec keycloak \
6 | /opt/jboss/keycloak/bin/standalone.sh -c standalone.xml \
7 | -Djboss.socket.binding.port-offset=10000 \
8 | -Dkeycloak.migration.action=export \
9 | -Dkeycloak.migration.file=/opt/jboss/imex/acme-realm.json \
10 | -Dkeycloak.migration.provider=singleFile \
11 | -Dkeycloak.migration.realmName=acme
12 |
--------------------------------------------------------------------------------
/acme-themes/src/main/resources/theme/acme/login/theme.properties:
--------------------------------------------------------------------------------
1 | parent=keycloak
2 | import=common/keycloak
3 | # Custom Styles
4 | styles=css/login.css css/acme-login.css
5 | stylesCommon=web_modules/@patternfly/react-core/dist/styles/base.css web_modules/@patternfly/react-core/dist/styles/app.css node_modules/patternfly/dist/css/patternfly.min.css node_modules/patternfly/dist/css/patternfly-additions.min.css lib/pficon/pficon.css
6 | # Custom JavaScript
7 | scripts=js/acme-login.js
8 | # Custom Page Metadata
9 | meta=viewport==width=device-width,initial-scale=1
10 |
--------------------------------------------------------------------------------
/acme-themes/src/main/resources/theme/acme/login/resources/css/acme-login.css:
--------------------------------------------------------------------------------
1 | /* white-login.css */
2 | /* see: https://leaverou.github.io/css3patterns/ */
3 | .login-pf body {
4 | background: radial-gradient(black 15%, transparent 16%) 0 0,
5 | radial-gradient(black 15%, transparent 16%) 8px 8px,
6 | radial-gradient(rgba(255, 255, 255, 0.1) 15%, transparent 20%) 0 1px,
7 | radial-gradient(rgba(255, 255, 255, 0.1) 15%, transparent 20%) 8px 9px !important;
8 | background-color: #282828 !important;
9 | background-size: 16px 16px !important;
10 | }
11 |
--------------------------------------------------------------------------------
/acme-themes/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | keycloak-extensions-talk
7 | com.github.thomasdarimont.keycloak
8 | 1.0.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | acme-themes
13 |
14 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.7'
2 | services:
3 | keycloak:
4 | container_name: keycloak-extdev
5 | image: quay.io/keycloak/keycloak:12.0.4
6 | user: "1000:1000"
7 | environment:
8 | KEYCLOAK_USER: "admin"
9 | KEYCLOAK_PASSWORD: "admin"
10 | KEYCLOAK_THEME_CACHING: "false"
11 | KEYCLOAK_THEME_TEMPLATE_CACHING: "false"
12 | KEYCLOAK_IMPORT: "/opt/jboss/imex/acme-realm.json"
13 | command: [ "--debug", "*:8787", "--server-config", "standalone.xml" ]
14 | ports:
15 | - "8080:8080"
16 | - "127.0.0.1:8787:8787"
17 | volumes:
18 | - ./acme-extensions/target/classes:/opt/jboss/keycloak/standalone/deployments/acme-extensions.jar:z
19 | - ./acme-themes/target/classes/theme/acme:/opt/jboss/keycloak/themes/acme:z
20 | - ./testrun/data:/opt/jboss/keycloak/standalone/data:z
21 | - ./imex:/opt/jboss/imex:z
22 | - ./cli:/opt/jboss/startup-scripts:z
23 |
--------------------------------------------------------------------------------
/acme-extensions/src/main/java/demo/acme/keycloak/api/AcmeResourceProviderFactory.java:
--------------------------------------------------------------------------------
1 | package demo.acme.keycloak.api;
2 |
3 | import com.google.auto.service.AutoService;
4 | import org.keycloak.Config;
5 | import org.keycloak.models.KeycloakSession;
6 | import org.keycloak.models.KeycloakSessionFactory;
7 | import org.keycloak.services.resource.RealmResourceProvider;
8 | import org.keycloak.services.resource.RealmResourceProviderFactory;
9 |
10 | @AutoService(RealmResourceProviderFactory.class)
11 | public class AcmeResourceProviderFactory implements RealmResourceProviderFactory {
12 |
13 | @Override
14 | public String getId() {
15 | return AcmeResourceProvider.ID;
16 | }
17 |
18 | @Override
19 | public RealmResourceProvider create(KeycloakSession session) {
20 | return new AcmeResourceProvider(session);
21 | }
22 |
23 | @Override
24 | public void init(Config.Scope config) {
25 | // NOOP
26 | }
27 |
28 | @Override
29 | public void postInit(KeycloakSessionFactory factory) {
30 | // NOOP
31 | }
32 |
33 | @Override
34 | public void close() {
35 | // NOOP
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/requests/http-client.env.json:
--------------------------------------------------------------------------------
1 | {
2 | "dev": {
3 | "KC_ISSUER_URL": "http://localhost:8080/auth/realms/acme",
4 | "testClientToken": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI5enFRZTA3cm9MMU1OUUdxdk5wYmw1MVZhb0EtWnBXV2JlaTAwX2s2NDJrIn0.eyJleHAiOjE2MTc2NjY1NDIsImlhdCI6MTYxNzY1NTc0MiwianRpIjoiM2EwZDdlZTQtMjQwZS00YjJkLTk2ZGMtZDhhYjJmMDJhNGNkIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL2FjbWUiLCJzdWIiOiJlYTZjMWE4ZC04ZTI3LTRlZDMtYmY0OC01NzdkMjY0YjdkNTkiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJ0ZXN0LWNsaWVudCIsInNlc3Npb25fc3RhdGUiOiIwNTVkMmQ4Mi00NzdhLTQzYzUtYTRiOS00ZDdiZWQyYmNhZGUiLCJhY3IiOiIxIiwic2NvcGUiOiJvcGVuaWQgZW1haWwgYWNtZS5hZ2VpbmZvIGFjbWUucHJvZmlsZSBhY21lLmFwaSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwibmFtZSI6IkZpcnN0bmFtZSBMYXN0bmFtZSIsInByZWZlcnJlZF91c2VybmFtZSI6InRlc3QtdXNlci1hZ2UyMiIsImdpdmVuX25hbWUiOiJGaXJzdG5hbWUiLCJmYW1pbHlfbmFtZSI6Ikxhc3RuYW1lIn0.EJEalMMP_dVTqlWyMmWm6kpSeD4hOQd9QZj32rgryO3J_1UTEO0ihqfee9cVbsfzjekvRJrnzVYH0q0KuirhUcfqioJ1NedTWt1vQPWtuhxQNYtVqbxJras10zpnpnOxZZM8YnHjKFtpojPCe3sKmxKEL50m666f7AoXVMrMxGE_3pWlo_yw6EdbnWPQn1HobbsdwkXGTiM1cLXCze0UniPyfAown5MPQoSm12WvAGq9juNFgxs4Gve4k77Mn3MReZIb-ARwXfXKfqBV6VrM0iCqRHypcGzaObBm0ytXsfUuhnKdbbLHhuTkOYem9bOuObdbBhj8BQYI2tYzRVMNwg"
5 | }
6 | }
--------------------------------------------------------------------------------
/acme-extensions/src/main/java/demo/acme/keycloak/audit/AcmeAuditListenerFactory.java:
--------------------------------------------------------------------------------
1 | package demo.acme.keycloak.audit;
2 |
3 | import com.google.auto.service.AutoService;
4 | import org.keycloak.Config;
5 | import org.keycloak.events.EventListenerProvider;
6 | import org.keycloak.events.EventListenerProviderFactory;
7 | import org.keycloak.models.KeycloakSession;
8 | import org.keycloak.models.KeycloakSessionFactory;
9 |
10 | @AutoService(EventListenerProviderFactory.class)
11 | public class AcmeAuditListenerFactory implements EventListenerProviderFactory {
12 |
13 | private static final AcmeAuditListener INSTANCE = new AcmeAuditListener();
14 |
15 | @Override
16 | public EventListenerProvider create(KeycloakSession session) {
17 | return INSTANCE;
18 | }
19 |
20 | @Override
21 | public void init(Config.Scope config) {
22 | // NOOP
23 | }
24 |
25 | @Override
26 | public void postInit(KeycloakSessionFactory factory) {
27 | // NOOP
28 | }
29 |
30 | @Override
31 | public void close() {
32 | // NOOP
33 | }
34 |
35 | @Override
36 | public String getId() {
37 | return AcmeAuditListener.ID;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/acme-extensions/src/main/java/demo/acme/keycloak/audit/AcmeAuditListener.java:
--------------------------------------------------------------------------------
1 | package demo.acme.keycloak.audit;
2 |
3 | import lombok.extern.jbosslog.JBossLog;
4 | import org.keycloak.events.Event;
5 | import org.keycloak.events.EventListenerProvider;
6 | import org.keycloak.events.admin.AdminEvent;
7 |
8 | @JBossLog
9 | public class AcmeAuditListener implements EventListenerProvider {
10 |
11 | public static final String ID = "acme-audit-listener";
12 |
13 | @Override
14 | public void onEvent(Event event) {
15 | // called for each User-Event
16 | log.infof("audit userEvent=%s type=%s realm=%suserId=%s",
17 | event, event.getType(), event.getRealmId(), event.getUserId());
18 | }
19 |
20 | @Override
21 | public void onEvent(AdminEvent event, boolean includeRepresentation) {
22 | // called for each AdminEvent
23 | log.infof("audit adminEvent=%s type=%s resourceType=%s resourcePath=%s includeRepresentation=%s",
24 | event, event.getOperationType(), event.getResourceType(), event.getResourcePath(), includeRepresentation);
25 | }
26 |
27 | @Override
28 | public void close() {
29 | // NOOP
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/acme-extensions/src/main/java/demo/acme/keycloak/api/AcmeResource.java:
--------------------------------------------------------------------------------
1 | package demo.acme.keycloak.api;
2 |
3 | import org.keycloak.models.KeycloakSession;
4 | import org.keycloak.representations.AccessToken;
5 |
6 | import javax.ws.rs.GET;
7 | import javax.ws.rs.Path;
8 | import javax.ws.rs.Produces;
9 | import javax.ws.rs.core.MediaType;
10 | import javax.ws.rs.core.Response;
11 | import java.util.HashMap;
12 | import java.util.Map;
13 |
14 | /**
15 | * {@code
16 | * curl -v http://localhost:8080/auth/realms/acme/acme-resources/ping | jq -C .
17 | * }
18 | */
19 | public class AcmeResource {
20 |
21 | private final KeycloakSession session;
22 | private final AccessToken token;
23 |
24 | public AcmeResource(KeycloakSession session, AccessToken accessToken) {
25 | this.session = session;
26 | this.token = accessToken;
27 | }
28 |
29 | @GET
30 | @Path("ping")
31 | @Produces(MediaType.APPLICATION_JSON)
32 | public Response ping() {
33 |
34 | Map payload = new HashMap<>();
35 | payload.put("realm", session.getContext().getRealm().getName());
36 | payload.put("user", token == null ? "anonymous" : token.getPreferredUsername());
37 | payload.put("timestamp", System.currentTimeMillis());
38 |
39 | return Response.ok(payload).build();
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/acme-extensions/src/main/resources/META-INF/jboss-deployment-structure.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.github.thomasdarimont.keycloak
8 | keycloak-extensions-talk
9 | pom
10 | 1.0.0-SNAPSHOT
11 |
12 | acme-extensions
13 | acme-themes
14 | acme-dist
15 |
16 |
17 |
18 |
19 | UTF-8
20 | 11
21 | ${java.version}
22 | ${java.version}
23 | thomasdarimont/acme-keycloak
24 |
25 |
26 | 12.0.4
27 |
28 |
29 | 5.7.1
30 | 3.9.1
31 | 1.6.0
32 |
33 |
34 | 1.0-rc7
35 | 1.18.16
36 | 0.35.0
37 | 2.22.2
38 | 2.22.2
39 |
40 |
--------------------------------------------------------------------------------
/acme-extensions/src/main/java/demo/acme/keycloak/api/AcmeResourceProvider.java:
--------------------------------------------------------------------------------
1 | package demo.acme.keycloak.api;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import org.keycloak.authorization.util.Tokens;
5 | import org.keycloak.models.KeycloakSession;
6 | import org.keycloak.representations.AccessToken;
7 | import org.keycloak.services.resource.RealmResourceProvider;
8 |
9 | import javax.ws.rs.ForbiddenException;
10 | import javax.ws.rs.NotAuthorizedException;
11 | import javax.ws.rs.core.Response;
12 |
13 | import static javax.ws.rs.core.Response.Status.FORBIDDEN;
14 | import static javax.ws.rs.core.Response.Status.UNAUTHORIZED;
15 |
16 | @RequiredArgsConstructor
17 | public class AcmeResourceProvider implements RealmResourceProvider {
18 |
19 | public static final String ID = "acme-resources";
20 |
21 | private final KeycloakSession session;
22 |
23 | @Override
24 | public Object getResource() {
25 |
26 | AccessToken accessToken = Tokens.getAccessToken(session);
27 |
28 | // check access
29 | // if (accessToken == null) {
30 | // throw new NotAuthorizedException("Invalid Token", Response.status(UNAUTHORIZED).build());
31 | // } else if (!hasScope("acme.api", accessToken.getScope())) {
32 | // throw new ForbiddenException("No Access", Response.status(FORBIDDEN).build());
33 | // }
34 |
35 | return new AcmeResource(session, accessToken);
36 | }
37 |
38 | private boolean hasScope(String requiredScope, String scope) {
39 |
40 | if (scope == null || scope.isEmpty()) {
41 | return false;
42 | }
43 |
44 | String[] scopeEntries = scope.split(" ");
45 | for (String entry : scopeEntries) {
46 | if (entry.equals(requiredScope)) {
47 | return true;
48 | }
49 | }
50 |
51 | return false;
52 | }
53 |
54 | @Override
55 | public void close() {
56 | // NOOP
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/cli/onstart-0001-init.cli:
--------------------------------------------------------------------------------
1 | echo using ${env.JBOSS_HOME}/standalone/configuration/standalone.xml
2 |
3 | embed-server --server-config=standalone.xml --std-out=echo
4 |
5 | echo SETUP: Begin Keycloak custom configuration...
6 |
7 | #### EVENT LISTENERS SPI Configuration
8 | echo SETUP: Customize Keycloak Event Listener configuration
9 | # Add dedicated eventsListener config element to allow configuring elements.
10 | if (outcome == failed) of /subsystem=keycloak-server/spi=eventsListener/:read-resource
11 | echo SETUP: Add missing eventsListener SPI
12 | /subsystem=keycloak-server/spi=eventsListener:add()
13 | echo
14 | end-if
15 |
16 | echo SETUP: Configure built-in "jboss-logging" event listener
17 | if (outcome == failed) of /subsystem=keycloak-server/spi=eventsListener/provider=jboss-logging/:read-resource
18 | echo SETUP: Add missing "jboss-logging" event listener
19 | /subsystem=keycloak-server/spi=eventsListener/provider=jboss-logging:add(enabled=true)
20 | echo
21 | end-if
22 |
23 | # Propagate success events to INFO instead of DEBUG
24 | # This allows to track successful logins in log analysis
25 | /subsystem=keycloak-server/spi=eventsListener/provider=jboss-logging:write-attribute(name=properties.success-level,value=info)
26 | /subsystem=keycloak-server/spi=eventsListener/provider=jboss-logging:write-attribute(name=properties.error-level,value=warn)
27 |
28 |
29 | ### Theme Configuration ###
30 |
31 | echo SETUP: Configure theme caching
32 | /subsystem=keycloak-server/theme=defaults:write-attribute(name=cacheThemes,value=${env.KEYCLOAK_THEME_CACHING:true})
33 | /subsystem=keycloak-server/theme=defaults:write-attribute(name=cacheTemplates,value=${env.KEYCLOAK_THEME_TEMPLATE_CACHING:true})
34 | /subsystem=keycloak-server/theme=defaults:write-attribute(name=welcomeTheme,value=${env.KEYCLOAK_WELCOME_THEME:keycloak})
35 | /subsystem=keycloak-server/theme=defaults:write-attribute(name=default,value=${env.KEYCLOAK_DEFAULT_THEME:keycloak})
36 |
37 |
38 | echo SETUP: Finished Keycloak custom configuration.
39 |
40 | stop-embedded-server
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | Easy Keycloak Extension Development
2 | ---
3 |
4 | This repository contains the code & slides of my talk `Keycloak Extension Development - Overview & Best Practices`.
5 |
6 | This example shows how to develop and deploy a set of Keycloak extensions, custom themens and configuration to a Keycloak docker container.
7 | In addition to that, the example also shows how to write integration tests via [Keycloak-Testcontainers](https://github.com/dasniko/testcontainers-keycloak) and how to package all extensions and themes as a
8 | custom docker image.
9 |
10 | The example contains the following Keycloak extensions:
11 | - OIDC ProtocolMapper to compute age-class information based on a `birthdate` user attribute: `AgeInfoMapper`
12 | - Audit Event Listener sketch to forward certain Keycloak user and admin events to an external service: `AcmeAuditListener`
13 | - Custom REST Endpoint the can expose additional custom APIs: `AcmeResource`
14 |
15 | # Some Highlights
16 | - Support for deploying extensions to running Keycloak container
17 | - Support for instant reloading of theme and extension code changes
18 | - Support Keycloak configuration customization via CLI scripts
19 | - Examples for Integration Tests with Keycloak-Testcontainers
20 |
21 | # Build
22 | The example can be build with the following maven command:
23 | ```
24 | mvn clean verify
25 | ```
26 |
27 | ## Build with Integration Tests
28 | The example can be build with integration tests by running the following maven command:
29 | ```
30 | mvn clean verify -Pwith-integration-tests
31 | ```
32 |
33 | ## Build Docker Image
34 | To build a custom Keycloak Docker image that contains the custom extensions and themes, you can run the following command:
35 | ```
36 | mvn clean verify -Pwith-integration-tests io.fabric8:docker-maven-plugin:build
37 | ```
38 |
39 | # Run
40 |
41 | ## Start Keycloak container with docker-compose
42 |
43 | Keycloak will be available on http://localhost:8080/auth.
44 | The default Keycloak admin username is `admin` with password `admin`.
45 |
46 | You can start the Keycloak container via:
47 | ```
48 | docker-compose up
49 | ```
50 |
51 | ## Run custom Docker Image
52 | The custom docker image created during the build can be stared with the following command:
53 | ```
54 | docker run \
55 | --name acme-keycloak \
56 | -e KEYCLOAK_USER=admin \
57 | -e KEYCLOAK_PASSWORD=admin \
58 | -e KEYCLOAK_IMPORT=/opt/jboss/imex/acme-realm.json \
59 | -v $PWD/imex:/opt/jboss/imex:z \
60 | -it \
61 | --rm \
62 | -p 8080:8080 \
63 | thomasdarimont/acme-keycloak:latest
64 | ```
65 |
66 | # Example environment
67 |
68 | The example environment contains a Keycloak realm named `acme`, which contains a simple demo application as well as a test user.
69 | The test user has the username `tester` and password `test`.
70 |
71 | ### Example App
72 |
73 | A simple demo app can be used to show information from the Access-Token, ID-Token and UserInfo endpoint provided by Keycloak.
74 |
75 | The demo app can be started by running `etc/runDemoApp.sh` and will be accessible via http://localhost:4000.
76 |
77 | # Scripts
78 |
79 | ## Manually trigger Extension Deployment
80 | ```
81 | etc/triggerDockerExtensionDeploy.sh
82 | ```
83 |
84 | ## Exporting the 'Acme' Realm
85 | ```
86 | etc/exportRealm.sh
87 | ```
88 |
--------------------------------------------------------------------------------
/acme-dist/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | keycloak-extensions-talk
7 | com.github.thomasdarimont.keycloak
8 | 1.0.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | acme-dist
13 |
14 |
15 |
16 | io.fabric8
17 | docker-maven-plugin
18 | ${docker-maven-plugin.version}
19 |
20 |
21 |
22 | docker-build-100
23 | docker
24 |
25 | build
26 |
27 |
28 |
29 |
30 |
31 | true
32 | true
33 |
34 |
35 |
36 | ${docker-image}
37 |
38 |
39 | ${project.version}
40 |
41 |
42 |
43 | keycloak
44 |
45 |
46 |
47 |
48 |
49 | ../acme-extensions/target
50 |
51 | acme-extensions-${project.version}.jar
52 |
53 | acme-extensions
54 |
55 |
56 |
57 | ../acme-themes/target/classes/theme/acme
58 | acme-theme
59 |
60 |
61 |
62 | ../cli
63 | cli
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/acme-extensions/src/main/java/demo/acme/keycloak/oidc/AgeInfoMapper.java:
--------------------------------------------------------------------------------
1 | package demo.acme.keycloak.oidc;
2 |
3 | import com.google.auto.service.AutoService;
4 | import org.keycloak.models.ClientSessionContext;
5 | import org.keycloak.models.KeycloakSession;
6 | import org.keycloak.models.ProtocolMapperModel;
7 | import org.keycloak.models.UserModel;
8 | import org.keycloak.models.UserSessionModel;
9 | import org.keycloak.protocol.ProtocolMapper;
10 | import org.keycloak.protocol.oidc.mappers.AbstractOIDCProtocolMapper;
11 | import org.keycloak.protocol.oidc.mappers.OIDCAccessTokenMapper;
12 | import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
13 | import org.keycloak.protocol.oidc.mappers.OIDCIDTokenMapper;
14 | import org.keycloak.protocol.oidc.mappers.UserInfoTokenMapper;
15 | import org.keycloak.provider.ProviderConfigProperty;
16 | import org.keycloak.representations.IDToken;
17 |
18 | import java.time.LocalDate;
19 | import java.time.LocalDateTime;
20 | import java.time.format.DateTimeParseException;
21 | import java.time.temporal.ChronoUnit;
22 | import java.util.ArrayList;
23 | import java.util.List;
24 | import java.util.Optional;
25 |
26 | @AutoService(ProtocolMapper.class)
27 | public class AgeInfoMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper {
28 |
29 | static final String PROVIDER_ID = "acme-ageinfo-mapper";
30 |
31 | static final List CONFIG_PROPERTIES;
32 |
33 | public static final String AGE_CLASS_CLAIM = "acme_age_class";
34 |
35 | static {
36 |
37 | List configProperties = new ArrayList<>();
38 | OIDCAttributeMapperHelper.addIncludeInTokensConfig(configProperties, AgeInfoMapper.class);
39 |
40 | CONFIG_PROPERTIES = configProperties;
41 | }
42 |
43 | @Override
44 | public String getId() {
45 | return PROVIDER_ID;
46 | }
47 |
48 | @Override
49 | public String getDisplayType() {
50 | return "Acme: AgeInfo";
51 | }
52 |
53 | @Override
54 | public String getHelpText() {
55 | return "Exposes the user's age-class as claim";
56 | }
57 |
58 | @Override
59 | public String getDisplayCategory() {
60 | return TOKEN_MAPPER_CATEGORY;
61 | }
62 |
63 | @Override
64 | public List getConfigProperties() {
65 | return CONFIG_PROPERTIES;
66 | }
67 |
68 | @Override
69 | protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession, KeycloakSession keycloakSession, ClientSessionContext clientSessionCtx) {
70 |
71 | UserModel user = userSession.getUser();
72 | String ageClass = computeAgeClass(user);
73 |
74 | token.getOtherClaims().put(AGE_CLASS_CLAIM, ageClass);
75 | }
76 |
77 | private String computeAgeClass(UserModel user) {
78 |
79 | return Optional.ofNullable(user.getFirstAttribute("birthdate")).map(birthdateString -> {
80 |
81 | LocalDate birthdate;
82 | try {
83 | birthdate = LocalDate.parse(birthdateString);
84 | } catch (DateTimeParseException ex) {
85 | return "invalid";
86 | }
87 |
88 | long ageInYears = ChronoUnit.YEARS.between(birthdate, LocalDateTime.now());
89 | String ageClass = "under16";
90 |
91 | if (ageInYears >= 16) {
92 | ageClass = "over16";
93 | }
94 | if (ageInYears >= 18) {
95 | ageClass = "over18";
96 | }
97 | if (ageInYears >= 21) {
98 | ageClass = "over21";
99 | }
100 |
101 | return ageClass;
102 | }).orElse("missing");
103 | }
104 | }
105 |
106 |
--------------------------------------------------------------------------------
/acme-extensions/src/test/java/demo/acme/keycloak/KeycloakTestSupport.java:
--------------------------------------------------------------------------------
1 | package demo.acme.keycloak;
2 |
3 | import dasniko.testcontainers.keycloak.KeycloakContainer;
4 | import lombok.AllArgsConstructor;
5 | import lombok.Data;
6 | import org.jboss.resteasy.client.jaxrs.ResteasyClient;
7 | import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
8 | import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget;
9 | import org.keycloak.admin.client.CreatedResponseUtil;
10 | import org.keycloak.admin.client.resource.RealmResource;
11 | import org.keycloak.admin.client.token.TokenService;
12 | import org.keycloak.representations.idm.CredentialRepresentation;
13 | import org.keycloak.representations.idm.UserRepresentation;
14 | import org.testcontainers.containers.output.OutputFrame;
15 | import org.testcontainers.utility.MountableFile;
16 |
17 | import javax.ws.rs.core.Response;
18 | import javax.ws.rs.core.UriBuilder;
19 | import java.nio.file.Path;
20 | import java.util.List;
21 | import java.util.function.Consumer;
22 |
23 | public class KeycloakTestSupport {
24 |
25 | public static final String MASTER_REALM = "master";
26 |
27 | public static final String ADMIN_CLI = "admin-cli";
28 |
29 | public static KeycloakContainer createKeycloakContainer(boolean keycloakLocal, String realmImportFileName) {
30 |
31 | if (keycloakLocal) {
32 | return new LocalKeycloak("http://localhost:8080/auth", "admin", "admin");
33 | }
34 |
35 | return new KeycloakContainer("quay.io/keycloak/keycloak:12.0.4")
36 | // .withRealmImportFile(REALM_IMPORT_FILE) // broken for integration-tests outside of the IDE
37 | .withCopyFileToContainer(MountableFile.forHostPath(Path.of("target/classes/" + realmImportFileName)), "/tmp/" + realmImportFileName)
38 | .withEnv("KEYCLOAK_IMPORT", "/tmp/" + realmImportFileName)
39 | .withCopyFileToContainer(MountableFile.forHostPath(Path.of("../cli/onstart-0001-init.cli")), "/opt/jboss/startup-scripts/onstart-0001-init.cli")
40 | .withExtensionClassesFrom("target/classes");
41 | }
42 |
43 | public static TokenService getTokenService(KeycloakContainer keycloak) {
44 | return getResteasyWebTarget(keycloak).proxy(TokenService.class);
45 | }
46 |
47 | public static ResteasyWebTarget getResteasyWebTarget(KeycloakContainer keycloak) {
48 | ResteasyClient client = new ResteasyClientBuilder().build();
49 | return client.target(UriBuilder.fromPath(keycloak.getAuthServerUrl()));
50 | }
51 |
52 | public static UserRef createOrUpdateTestUser(RealmResource realm, String username, String password, Consumer adjuster) {
53 |
54 | List existingUsers = realm.users().search(username, true);
55 |
56 | String userId;
57 | UserRepresentation userRep;
58 |
59 | if (existingUsers.isEmpty()) {
60 | userRep = new UserRepresentation();
61 | userRep.setUsername(username);
62 | userRep.setEnabled(true);
63 | adjuster.accept(userRep);
64 | try (Response response = realm.users().create(userRep)) {
65 | userId = CreatedResponseUtil.getCreatedId(response);
66 | } catch (Exception ex) {
67 | throw new RuntimeException(ex);
68 | }
69 | } else {
70 | userRep = existingUsers.get(0);
71 | adjuster.accept(userRep);
72 | userId = userRep.getId();
73 | }
74 |
75 | CredentialRepresentation passwordRep = new CredentialRepresentation();
76 | passwordRep.setType(CredentialRepresentation.PASSWORD);
77 | passwordRep.setValue(password);
78 | realm.users().get(userId).resetPassword(passwordRep);
79 |
80 | return new UserRef(userId, username);
81 | }
82 |
83 | @Data
84 | @AllArgsConstructor
85 | public static class UserRef {
86 | String userId;
87 | String username;
88 | }
89 |
90 |
91 | @Data
92 | @AllArgsConstructor
93 | public static class LocalKeycloak extends KeycloakContainer {
94 |
95 | String authServerUrl;
96 | String adminUsername;
97 | String adminPassword;
98 |
99 | public void start() {
100 | // NOOP
101 | }
102 |
103 | @Override
104 | public void followOutput(Consumer consumer) {
105 | // NOOP
106 | }
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/acme-extensions/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | keycloak-extensions-talk
7 | com.github.thomasdarimont.keycloak
8 | 1.0.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | acme-extensions
13 |
14 |
15 |
16 |
17 | org.keycloak
18 | keycloak-server-spi
19 | ${keycloak.version}
20 | provided
21 |
22 |
23 |
24 |
25 | org.keycloak
26 | keycloak-server-spi-private
27 | ${keycloak.version}
28 | provided
29 |
30 |
31 |
32 | org.keycloak
33 | keycloak-services
34 | ${keycloak.version}
35 | provided
36 |
37 |
38 |
39 | org.keycloak
40 | keycloak-admin-client
41 | ${keycloak.version}
42 | test
43 |
44 |
45 |
46 | org.junit.jupiter
47 | junit-jupiter
48 | ${junit-jupiter.version}
49 | test
50 |
51 |
52 |
53 | org.assertj
54 | assertj-core
55 | ${assertj-core.version}
56 | test
57 |
58 |
59 |
60 | com.github.dasniko
61 | testcontainers-keycloak
62 | ${testcontainers-keycloak.version}
63 | test
64 |
65 |
66 |
67 | org.projectlombok
68 | lombok
69 | ${lombok.version}
70 | provided
71 | true
72 |
73 |
74 |
75 | com.google.auto.service
76 | auto-service
77 | ${auto-service.version}
78 | provided
79 | true
80 |
81 |
82 |
83 |
84 | with-integration-tests
85 |
86 |
87 |
88 | org.apache.maven.plugins
89 | maven-failsafe-plugin
90 | ${maven-failsafe-plugin.version}
91 |
92 |
93 | integration-test
94 |
95 | integration-test
96 | verify
97 |
98 |
99 |
100 |
101 |
102 | **/*IntegrationTest.java
103 |
104 | once
105 |
106 | true
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 | org.apache.maven.plugins
119 | maven-surefire-plugin
120 | ${maven-surefire-plugin.version}
121 |
122 |
123 | **/*IntegrationTest.java
124 |
125 |
126 |
127 |
128 |
129 |
--------------------------------------------------------------------------------
/notes.md:
--------------------------------------------------------------------------------
1 | Notes
2 | ---
3 |
4 | ```
5 | docker run \
6 | --name keycloak-extdev \
7 | --rm \
8 | -e KEYCLOAK_USER=admin \
9 | -e KEYCLOAK_PASSWORD=admin \
10 | -e KEYCLOAK_THEME_CACHING=false \
11 | -e KEYCLOAK_THEME_TEMPLATE_CACHING=false \
12 | -v $PWD/acme-extensions/target/classes:/opt/jboss/keycloak/standalone/deployments/acme-extensions.jar:z \
13 | -v $PWD/acme-themes/target/classes/theme/acme:/opt/jboss/keycloak/themes/acme:z \
14 | -v $PWD/testrun/data:/opt/jboss/keycloak/standalone/data:z \
15 | -v $PWD/cli:/opt/jboss/startup-scripts:z \
16 | -v $PWD/imex:/opt/jboss/imex:z \
17 | -p 8080:8080 \
18 | -p 8787:8787 \
19 | quay.io/keycloak/keycloak:12.0.4 --debug '*:8787' --server-config standalone.xml
20 | ```
21 |
22 | ```
23 | mvn clean verify io.fabric8:docker-maven-plugin:build -Pwith-integration-tests
24 |
25 | mvn clean verify -Pwith-integration-tests
26 | ```
27 |
28 |
29 | # Scratch Notes
30 |
31 | ## Prepare Scratch Keycloak
32 |
33 | ```
34 | cd scratch
35 | cp ~/Downloads/keycloak-12.0.4.tar.gz .
36 | tar xzf keycloak-12.0.4.tar.gz
37 | keycloak-12.0.4/bin/add-user-keycloak.sh --user admin --password admin
38 | ```
39 |
40 | ## Start Scratch Keycloak
41 | ```
42 | cd scratch/keycloak-12.0.4
43 | bin/standalone.sh --debug -Dwildfly.statistics-enabled=true -c standalone.xml
44 | ```
45 |
46 | ## Deploy extensions
47 | ```
48 | cp acme-extensions/target/acme-extensions-1.0.0-SNAPSHOT.jar scratch/keycloak-12.0.4/standalone/deployments
49 | ```
50 |
51 | ## Undeploy extensions
52 | ```
53 | rm scratch/keycloak-12.0.4/standalone/deployments/*.jar
54 | ```
55 |
56 |
57 | Scratch
58 | ---
59 |
60 | `AcmeAuditListener.java`
61 | ```java
62 | package demo.acme.keycloak.audit;
63 |
64 | import org.jboss.logging.Logger;
65 | import org.keycloak.events.Event;
66 | import org.keycloak.events.EventListenerProvider;
67 | import org.keycloak.events.admin.AdminEvent;
68 |
69 | public class AcmeAuditListener implements EventListenerProvider {
70 |
71 | public static final String ID = "acme-audit-listener";
72 |
73 | private static final Logger LOG = Logger.getLogger(AcmeAuditListener.class);
74 |
75 | @Override
76 | public void onEvent(Event event) {
77 | // called for each User-Event
78 | LOG.infof("audit userEvent=%s type=%s realm=%suserId=%s",
79 | event, event.getType(), event.getRealmId(), event.getUserId());
80 | }
81 |
82 | @Override
83 | public void onEvent(AdminEvent event, boolean includeRepresentation) {
84 | // called for each AdminEvent
85 | LOG.infof("audit adminEvent=%s type=%s resourceType=%s resourcePath=%s includeRepresentation=%s",
86 | event, event.getOperationType(), event.getResourceType(), event.getResourcePath(), includeRepresentation);
87 | }
88 |
89 | @Override
90 | public void close() {
91 | // NOOP
92 | }
93 | }
94 | ```
95 |
96 | `AcmeAuditListenerFactory.java`
97 | ```java
98 | package demo.acme.keycloak.audit;
99 |
100 | import org.keycloak.Config;
101 | import org.keycloak.events.EventListenerProvider;
102 | import org.keycloak.events.EventListenerProviderFactory;
103 | import org.keycloak.models.KeycloakSession;
104 | import org.keycloak.models.KeycloakSessionFactory;
105 |
106 | // src/main/resources/META-INF/servcies -> org.keycloak.events.EventListenerProviderFactory
107 | public class AcmeAuditListenerFactory implements EventListenerProviderFactory {
108 |
109 | private static final AcmeAuditListener INSTANCE = new AcmeAuditListener();
110 |
111 | @Override
112 | public String getId() {
113 | return AcmeAuditListener.ID;
114 | }
115 |
116 | @Override
117 | public EventListenerProvider create(KeycloakSession session) {
118 | // return singleton instance, or create new AcmeAuditListener(session) if you need KeycloakSession access.
119 | return INSTANCE;
120 | }
121 |
122 | @Override
123 | public void init(Config.Scope config) {
124 | // we could read settings from the provider config in standalone(-ha).xml
125 | }
126 |
127 | @Override
128 | public void postInit(KeycloakSessionFactory factory) {
129 | // we could init our provider with information from other providers
130 | }
131 |
132 | @Override
133 | public void close() {
134 | // close resources if providers
135 | }
136 | }
137 | ```
138 |
139 |
140 | `org.keycloak.events.EventListenerProviderFactory`
141 | ```
142 | demo.acme.keycloak.audit.AcmeAuditListenerFactory
143 | ```
144 |
145 | Test for Audit Listener in `AcmeKeycloakIntegrationTest.java`
146 | ```java
147 | @Test
148 | public void auditListenerShouldPrintLogMessage() throws Exception{
149 |
150 | Assumptions.assumeTrue(!keycloakLocal);
151 |
152 | ToStringConsumer consumer = new ToStringConsumer();
153 | keycloak.followOutput(consumer);
154 |
155 | TokenService tokenService = KeycloakTestSupport.getTokenService(keycloak);
156 |
157 | // trigger user login via ROPC
158 | AccessTokenResponse accessTokenResponse = tokenService.grantToken(ACME_REALM, new Form()
159 | .param("grant_type", "password")
160 | .param("username", "tester")
161 | .param("password", TEST_USER_PASSWORD)
162 | .param("client_id", TEST_CLIENT)
163 | .param("scope", "openid acme.profile acme.ageinfo")
164 | .asMap());
165 |
166 | // Allow the container log to flush
167 | TimeUnit.MILLISECONDS.sleep(750);
168 |
169 | assertThat(consumer.toUtf8String()).contains("audit userEvent");
170 | }
171 |
172 | ```
173 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | !testrun/.gitkeep
2 | testrun/
3 | !scratch/.gitkeep
4 | scratch/
5 | *.jfr
6 |
7 | # Created by https://www.gitignore.io/api/osx,java,maven,gradle,eclipse,intellij+all,visualstudiocode
8 | # Edit at https://www.gitignore.io/?templates=osx,java,maven,gradle,eclipse,intellij+all,visualstudiocode
9 |
10 | ### Eclipse ###
11 |
12 | .metadata
13 | bin/
14 | tmp/
15 | *.tmp
16 | *.bak
17 | *.swp
18 | *~.nib
19 | local.properties
20 | .settings/
21 | .loadpath
22 | .recommenders
23 |
24 | # External tool builders
25 | .externalToolBuilders/
26 |
27 | # Locally stored "Eclipse launch configurations"
28 | *.launch
29 |
30 | # PyDev specific (Python IDE for Eclipse)
31 | *.pydevproject
32 |
33 | # CDT-specific (C/C++ Development Tooling)
34 | .cproject
35 |
36 | # CDT- autotools
37 | .autotools
38 |
39 | # Java annotation processor (APT)
40 | .factorypath
41 |
42 | # PDT-specific (PHP Development Tools)
43 | .buildpath
44 |
45 | # sbteclipse plugin
46 | .target
47 |
48 | # Tern plugin
49 | .tern-project
50 |
51 | # TeXlipse plugin
52 | .texlipse
53 |
54 | # STS (Spring Tool Suite)
55 | .springBeans
56 |
57 | # Code Recommenders
58 | .recommenders/
59 |
60 | # Annotation Processing
61 | .apt_generated/
62 |
63 | # Scala IDE specific (Scala & Java development for Eclipse)
64 | .cache-main
65 | .scala_dependencies
66 | .worksheet
67 |
68 | ### Eclipse Patch ###
69 | # Eclipse Core
70 | .project
71 |
72 | # JDT-specific (Eclipse Java Development Tools)
73 | .classpath
74 |
75 | # Annotation Processing
76 | .apt_generated
77 |
78 | .sts4-cache/
79 |
80 | ### Intellij+all ###
81 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
82 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
83 |
84 | # User-specific stuff
85 | .idea/**/workspace.xml
86 | .idea/**/tasks.xml
87 | .idea/**/usage.statistics.xml
88 | .idea/**/dictionaries
89 | .idea/**/shelf
90 |
91 | # Generated files
92 | .idea/**/contentModel.xml
93 |
94 | # Sensitive or high-churn files
95 | .idea/**/dataSources/
96 | .idea/**/dataSources.ids
97 | .idea/**/dataSources.local.xml
98 | .idea/**/sqlDataSources.xml
99 | .idea/**/dynamic.xml
100 | .idea/**/uiDesigner.xml
101 | .idea/**/dbnavigator.xml
102 |
103 | # Gradle
104 | .idea/**/gradle.xml
105 | .idea/**/libraries
106 |
107 | # Gradle and Maven with auto-import
108 | # When using Gradle or Maven with auto-import, you should exclude module files,
109 | # since they will be recreated, and may cause churn. Uncomment if using
110 | # auto-import.
111 | # .idea/modules.xml
112 | # .idea/*.iml
113 | # .idea/modules
114 |
115 | # CMake
116 | cmake-build-*/
117 |
118 | # Mongo Explorer plugin
119 | .idea/**/mongoSettings.xml
120 |
121 | # File-based project format
122 | *.iws
123 |
124 | # IntelliJ
125 | out/
126 |
127 | # mpeltonen/sbt-idea plugin
128 | .idea_modules/
129 |
130 | # JIRA plugin
131 | atlassian-ide-plugin.xml
132 |
133 | # Cursive Clojure plugin
134 | .idea/replstate.xml
135 |
136 | # Crashlytics plugin (for Android Studio and IntelliJ)
137 | com_crashlytics_export_strings.xml
138 | crashlytics.properties
139 | crashlytics-build.properties
140 | fabric.properties
141 |
142 | # Editor-based Rest Client
143 | .idea/httpRequests
144 |
145 | # Android studio 3.1+ serialized cache file
146 | .idea/caches/build_file_checksums.ser
147 |
148 | ### Intellij+all Patch ###
149 | # Ignores the whole .idea folder and all .iml files
150 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
151 |
152 | .idea/
153 |
154 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
155 |
156 | *.iml
157 | modules.xml
158 | .idea/misc.xml
159 | *.ipr
160 |
161 | # Sonarlint plugin
162 | .idea/sonarlint
163 |
164 | ### Java ###
165 | # Compiled class file
166 | *.class
167 |
168 | # Log file
169 | *.log
170 |
171 | # BlueJ files
172 | *.ctxt
173 |
174 | # Mobile Tools for Java (J2ME)
175 | .mtj.tmp/
176 |
177 | # Package Files #
178 | *.jar
179 | *.war
180 | *.nar
181 | *.ear
182 | *.zip
183 | *.tar.gz
184 | *.rar
185 |
186 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
187 | hs_err_pid*
188 |
189 | ### Maven ###
190 | target/
191 | pom.xml.tag
192 | pom.xml.releaseBackup
193 | pom.xml.versionsBackup
194 | pom.xml.next
195 | release.properties
196 | dependency-reduced-pom.xml
197 | buildNumber.properties
198 | .mvn/timing.properties
199 | .mvn/wrapper/maven-wrapper.jar
200 |
201 | ### OSX ###
202 | # General
203 | .DS_Store
204 | .AppleDouble
205 | .LSOverride
206 |
207 | # Icon must end with two \r
208 | Icon
209 |
210 | # Thumbnails
211 | ._*
212 |
213 | # Files that might appear in the root of a volume
214 | .DocumentRevisions-V100
215 | .fseventsd
216 | .Spotlight-V100
217 | .TemporaryItems
218 | .Trashes
219 | .VolumeIcon.icns
220 | .com.apple.timemachine.donotpresent
221 |
222 | # Directories potentially created on remote AFP share
223 | .AppleDB
224 | .AppleDesktop
225 | Network Trash Folder
226 | Temporary Items
227 | .apdisk
228 |
229 | ### VisualStudioCode ###
230 | .vscode/*
231 | !.vscode/settings.json
232 | !.vscode/tasks.json
233 | !.vscode/launch.json
234 | !.vscode/extensions.json
235 |
236 | ### VisualStudioCode Patch ###
237 | # Ignore all local history of files
238 | .history
239 |
240 | ### Gradle ###
241 | .gradle
242 | build/
243 |
244 | # Ignore Gradle GUI config
245 | gradle-app.setting
246 |
247 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
248 | !gradle-wrapper.jar
249 |
250 | # Cache of project
251 | .gradletasknamecache
252 |
253 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
254 | # gradle/wrapper/gradle-wrapper.properties
255 |
256 | ### Gradle Patch ###
257 | **/build/
258 |
259 | # End of https://www.gitignore.io/api/osx,java,maven,gradle,eclipse,intellij+all,visualstudiocode
260 |
--------------------------------------------------------------------------------
/acme-extensions/src/test/java/demo/acme/keycloak/AcmeKeycloakIntegrationTest.java:
--------------------------------------------------------------------------------
1 | package demo.acme.keycloak;
2 |
3 | import dasniko.testcontainers.keycloak.KeycloakContainer;
4 | import demo.acme.keycloak.KeycloakTestSupport.UserRef;
5 | import demo.acme.keycloak.oidc.AgeInfoMapper;
6 | import lombok.extern.slf4j.Slf4j;
7 | import org.junit.jupiter.api.AfterAll;
8 | import org.junit.jupiter.api.Assumptions;
9 | import org.junit.jupiter.api.BeforeAll;
10 | import org.junit.jupiter.api.Test;
11 | import org.keycloak.TokenVerifier;
12 | import org.keycloak.admin.client.Keycloak;
13 | import org.keycloak.admin.client.resource.RealmResource;
14 | import org.keycloak.admin.client.token.TokenService;
15 | import org.keycloak.representations.AccessTokenResponse;
16 | import org.keycloak.representations.IDToken;
17 | import org.testcontainers.containers.output.Slf4jLogConsumer;
18 | import org.testcontainers.containers.output.ToStringConsumer;
19 | import org.testcontainers.shaded.com.google.common.collect.ImmutableMap;
20 |
21 | import javax.ws.rs.Consumes;
22 | import javax.ws.rs.GET;
23 | import javax.ws.rs.HeaderParam;
24 | import javax.ws.rs.PathParam;
25 | import javax.ws.rs.core.Form;
26 | import javax.ws.rs.core.MediaType;
27 | import java.nio.file.Files;
28 | import java.nio.file.Path;
29 | import java.time.LocalDate;
30 | import java.util.List;
31 | import java.util.Map;
32 | import java.util.concurrent.TimeUnit;
33 |
34 | import static demo.acme.keycloak.KeycloakTestSupport.ADMIN_CLI;
35 | import static demo.acme.keycloak.KeycloakTestSupport.MASTER_REALM;
36 | import static org.assertj.core.api.Assertions.assertThat;
37 |
38 | @Slf4j
39 | public class AcmeKeycloakIntegrationTest {
40 |
41 | public static final String ACME_REALM = "acme";
42 |
43 | public static final String TEST_CLIENT = "test-client";
44 |
45 | public static final String TEST_USER_PASSWORD = "test";
46 |
47 | public static final String REALM_IMPORT_FILE = "acme-realm.json";
48 |
49 | public static KeycloakContainer keycloak;
50 |
51 | static boolean keycloakLocal = false;
52 |
53 | @BeforeAll
54 | public static void beforeAll() throws Exception {
55 |
56 | // use the previously copied realm file
57 | if (!Path.of("target/classes/" + REALM_IMPORT_FILE).toFile().exists()) {
58 | Files.copy(Path.of("../imex/" + REALM_IMPORT_FILE), Path.of("target/classes/" + REALM_IMPORT_FILE));
59 | }
60 |
61 | if (!Path.of("target/classes/cli/onstart-0001-init.cli").toFile().exists()) {
62 | Path targetFile = Path.of("target/classes/cli/onstart-0001-init.cli");
63 | targetFile.getParent().toFile().mkdirs();
64 | Files.copy(Path.of("../cli/onstart-0001-init.cli"), targetFile);
65 | }
66 |
67 | keycloak = KeycloakTestSupport.createKeycloakContainer(keycloakLocal, REALM_IMPORT_FILE);
68 | keycloak.withReuse(true);
69 | keycloak.start();
70 | keycloak.followOutput(new Slf4jLogConsumer(log));
71 | }
72 |
73 | @AfterAll
74 | public static void afterAll() {
75 | if (keycloak != null) {
76 | keycloak.stop();
77 | }
78 | }
79 |
80 | @Test
81 | public void auditListenerShouldPrintLogMessage() throws Exception {
82 |
83 | Assumptions.assumeTrue(!keycloakLocal);
84 |
85 | ToStringConsumer consumer = new ToStringConsumer();
86 | keycloak.followOutput(consumer);
87 |
88 | TokenService tokenService = KeycloakTestSupport.getTokenService(keycloak);
89 |
90 | // trigger user login via ROPC
91 | AccessTokenResponse accessTokenResponse = tokenService.grantToken(ACME_REALM, new Form()
92 | .param("grant_type", "password")
93 | .param("username", "tester")
94 | .param("password", TEST_USER_PASSWORD)
95 | .param("client_id", TEST_CLIENT)
96 | .param("scope", "openid acme.profile acme.ageinfo")
97 | .asMap());
98 |
99 | // Allow the container log to flush
100 | TimeUnit.MILLISECONDS.sleep(750);
101 |
102 | assertThat(consumer.toUtf8String()).contains("audit userEvent");
103 | }
104 |
105 | @Test
106 | public void ageInfoMapperShouldAddAgeClassClaim() throws Exception {
107 |
108 | Keycloak keycloakAdminClient = Keycloak.getInstance(keycloak.getAuthServerUrl(), MASTER_REALM,
109 | keycloak.getAdminUsername(), keycloak.getAdminPassword(), ADMIN_CLI);
110 |
111 | RealmResource acmeRealm = keycloakAdminClient.realm(ACME_REALM);
112 |
113 | UserRef user22Years = KeycloakTestSupport.createOrUpdateTestUser(acmeRealm, "test-user-age22", TEST_USER_PASSWORD, user -> {
114 | user.setFirstName("Firstname");
115 | user.setLastName("Lastname");
116 | user.setAttributes(ImmutableMap.of("birthdate", List.of(LocalDate.now().minusYears(22).toString())));
117 | });
118 |
119 | TokenService tokenService = KeycloakTestSupport.getTokenService(keycloak);
120 |
121 | AccessTokenResponse accessTokenResponse = tokenService.grantToken(ACME_REALM, new Form()
122 | .param("grant_type", "password")
123 | .param("username", user22Years.getUsername())
124 | .param("password", TEST_USER_PASSWORD)
125 | .param("client_id", TEST_CLIENT)
126 | .param("scope", "openid acme.profile acme.ageinfo")
127 | .asMap());
128 |
129 | // System.out.println("Token: " + accessTokenResponse.getToken());
130 |
131 | // parse the received id-token
132 | TokenVerifier verifier = TokenVerifier.create(accessTokenResponse.getIdToken(), IDToken.class);
133 | verifier.parse();
134 |
135 | // check for the custom claim
136 | IDToken accessToken = verifier.getToken();
137 | String ageInfoClaim = (String) accessToken.getOtherClaims().get(AgeInfoMapper.AGE_CLASS_CLAIM);
138 |
139 | assertThat(ageInfoClaim).isNotNull();
140 | assertThat(ageInfoClaim).isEqualTo("over21");
141 | }
142 |
143 | @Test
144 | public void pingResourceShouldBeAccessibleForUser() {
145 |
146 | TokenService tokenService = KeycloakTestSupport.getTokenService(keycloak);
147 |
148 | AccessTokenResponse accessTokenResponse = tokenService.grantToken(ACME_REALM, new Form()
149 | .param("grant_type", "password")
150 | .param("username", "tester")
151 | .param("password", TEST_USER_PASSWORD)
152 | .param("client_id", TEST_CLIENT)
153 | .param("scope", "openid acme.profile acme.api")
154 | .asMap());
155 |
156 | String accessToken = accessTokenResponse.getToken();
157 | System.out.println("Token: " + accessToken);
158 |
159 | AcmeResources acmeResources = KeycloakTestSupport.getResteasyWebTarget(keycloak).proxy(AcmeResources.class);
160 | Map response = acmeResources.ping(ACME_REALM, "Bearer " + accessToken);
161 | System.out.println(response);
162 |
163 | assertThat(response).isNotNull();
164 | assertThat(response.get("user")).isEqualTo("tester");
165 | }
166 |
167 |
168 | interface AcmeResources {
169 |
170 | @GET
171 | @Consumes(MediaType.APPLICATION_JSON)
172 | @javax.ws.rs.Path("/realms/{realm}/acme-resources/ping")
173 | Map ping(@PathParam("realm") String realm, @HeaderParam("Authorization") String token);
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/demoapp/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Mini SPA Demo
8 |
9 |
107 |
108 |
109 |
110 |
111 |
112 |
ClientId:
113 |
114 |
115 |
124 |
125 |
126 |
136 |
137 |
138 |
139 |
140 |
141 | | First name |
142 | |
143 |
144 |
145 | | Last name |
146 | |
147 |
148 |
149 | | Email |
150 | |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
173 |
174 |
190 |
191 |
207 |
208 |
209 |
339 |
340 |
341 |
--------------------------------------------------------------------------------
/imex/acme-realm.json:
--------------------------------------------------------------------------------
1 | {
2 | "id" : "acme",
3 | "realm" : "acme",
4 | "notBefore" : 0,
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 | "clientOfflineSessionIdleTimeout" : 0,
19 | "clientOfflineSessionMaxLifespan" : 0,
20 | "accessCodeLifespan" : 60,
21 | "accessCodeLifespanUserAction" : 300,
22 | "accessCodeLifespanLogin" : 1800,
23 | "actionTokenGeneratedByAdminLifespan" : 43200,
24 | "actionTokenGeneratedByUserLifespan" : 300,
25 | "enabled" : true,
26 | "sslRequired" : "external",
27 | "registrationAllowed" : false,
28 | "registrationEmailAsUsername" : false,
29 | "rememberMe" : false,
30 | "verifyEmail" : false,
31 | "loginWithEmailAllowed" : true,
32 | "duplicateEmailsAllowed" : false,
33 | "resetPasswordAllowed" : false,
34 | "editUsernameAllowed" : false,
35 | "bruteForceProtected" : false,
36 | "permanentLockout" : false,
37 | "maxFailureWaitSeconds" : 900,
38 | "minimumQuickLoginWaitSeconds" : 60,
39 | "waitIncrementSeconds" : 60,
40 | "quickLoginCheckMilliSeconds" : 1000,
41 | "maxDeltaTimeSeconds" : 43200,
42 | "failureFactor" : 30,
43 | "roles" : {
44 | "realm" : [ {
45 | "id" : "bd6a1bbb-ebe6-405d-806c-e64b9fcca028",
46 | "name" : "offline_access",
47 | "description" : "${role_offline-access}",
48 | "composite" : false,
49 | "clientRole" : false,
50 | "containerId" : "acme",
51 | "attributes" : { }
52 | }, {
53 | "id" : "5b815c11-9dcc-46f1-91c8-7a5e53f034dd",
54 | "name" : "uma_authorization",
55 | "composite" : false,
56 | "clientRole" : false,
57 | "containerId" : "acme",
58 | "attributes" : { }
59 | } ],
60 | "client" : {
61 | "realm-management" : [ {
62 | "id" : "15280cdc-23eb-44c7-8de0-8e70ec81abe9",
63 | "name" : "manage-events",
64 | "description" : "${role_manage-events}",
65 | "composite" : false,
66 | "clientRole" : true,
67 | "containerId" : "a28f054a-e493-4bbf-b361-e2ac4746467a",
68 | "attributes" : { }
69 | }, {
70 | "id" : "f917ba91-8366-4a05-a4ab-38103de116bc",
71 | "name" : "view-authorization",
72 | "description" : "${role_view-authorization}",
73 | "composite" : false,
74 | "clientRole" : true,
75 | "containerId" : "a28f054a-e493-4bbf-b361-e2ac4746467a",
76 | "attributes" : { }
77 | }, {
78 | "id" : "f1df51d0-f9b3-4f53-8b15-dea6d69dadf2",
79 | "name" : "view-realm",
80 | "description" : "${role_view-realm}",
81 | "composite" : false,
82 | "clientRole" : true,
83 | "containerId" : "a28f054a-e493-4bbf-b361-e2ac4746467a",
84 | "attributes" : { }
85 | }, {
86 | "id" : "a4ce49bb-de88-4840-8623-30d39308fcd6",
87 | "name" : "view-clients",
88 | "description" : "${role_view-clients}",
89 | "composite" : true,
90 | "composites" : {
91 | "client" : {
92 | "realm-management" : [ "query-clients" ]
93 | }
94 | },
95 | "clientRole" : true,
96 | "containerId" : "a28f054a-e493-4bbf-b361-e2ac4746467a",
97 | "attributes" : { }
98 | }, {
99 | "id" : "b73eec62-8068-4e31-a5b0-074ee4f8b255",
100 | "name" : "manage-identity-providers",
101 | "description" : "${role_manage-identity-providers}",
102 | "composite" : false,
103 | "clientRole" : true,
104 | "containerId" : "a28f054a-e493-4bbf-b361-e2ac4746467a",
105 | "attributes" : { }
106 | }, {
107 | "id" : "18817174-ffba-4154-a06e-d75557640896",
108 | "name" : "view-identity-providers",
109 | "description" : "${role_view-identity-providers}",
110 | "composite" : false,
111 | "clientRole" : true,
112 | "containerId" : "a28f054a-e493-4bbf-b361-e2ac4746467a",
113 | "attributes" : { }
114 | }, {
115 | "id" : "06dc5a0c-c854-4da9-8d7e-a6d12857e7b9",
116 | "name" : "query-groups",
117 | "description" : "${role_query-groups}",
118 | "composite" : false,
119 | "clientRole" : true,
120 | "containerId" : "a28f054a-e493-4bbf-b361-e2ac4746467a",
121 | "attributes" : { }
122 | }, {
123 | "id" : "93b5097d-56cb-4576-b24d-f826fba239ab",
124 | "name" : "query-clients",
125 | "description" : "${role_query-clients}",
126 | "composite" : false,
127 | "clientRole" : true,
128 | "containerId" : "a28f054a-e493-4bbf-b361-e2ac4746467a",
129 | "attributes" : { }
130 | }, {
131 | "id" : "183227f9-6a1b-4afc-b53b-05737e8f0deb",
132 | "name" : "manage-authorization",
133 | "description" : "${role_manage-authorization}",
134 | "composite" : false,
135 | "clientRole" : true,
136 | "containerId" : "a28f054a-e493-4bbf-b361-e2ac4746467a",
137 | "attributes" : { }
138 | }, {
139 | "id" : "1567536d-40c6-4fcd-8b47-bbed91857023",
140 | "name" : "manage-users",
141 | "description" : "${role_manage-users}",
142 | "composite" : false,
143 | "clientRole" : true,
144 | "containerId" : "a28f054a-e493-4bbf-b361-e2ac4746467a",
145 | "attributes" : { }
146 | }, {
147 | "id" : "2e1e7aaf-6b22-442d-bb86-fc04cc4f7eb9",
148 | "name" : "view-users",
149 | "description" : "${role_view-users}",
150 | "composite" : true,
151 | "composites" : {
152 | "client" : {
153 | "realm-management" : [ "query-groups", "query-users" ]
154 | }
155 | },
156 | "clientRole" : true,
157 | "containerId" : "a28f054a-e493-4bbf-b361-e2ac4746467a",
158 | "attributes" : { }
159 | }, {
160 | "id" : "7ead49d9-90a4-49f3-8cde-9b79f49b6584",
161 | "name" : "query-realms",
162 | "description" : "${role_query-realms}",
163 | "composite" : false,
164 | "clientRole" : true,
165 | "containerId" : "a28f054a-e493-4bbf-b361-e2ac4746467a",
166 | "attributes" : { }
167 | }, {
168 | "id" : "cc174725-6139-4c4c-9230-d60e7477feaf",
169 | "name" : "manage-clients",
170 | "description" : "${role_manage-clients}",
171 | "composite" : false,
172 | "clientRole" : true,
173 | "containerId" : "a28f054a-e493-4bbf-b361-e2ac4746467a",
174 | "attributes" : { }
175 | }, {
176 | "id" : "c7a3a11e-5b14-45d1-9d24-b85ae83911b2",
177 | "name" : "impersonation",
178 | "description" : "${role_impersonation}",
179 | "composite" : false,
180 | "clientRole" : true,
181 | "containerId" : "a28f054a-e493-4bbf-b361-e2ac4746467a",
182 | "attributes" : { }
183 | }, {
184 | "id" : "511c0a3f-6f4f-4be1-9adf-3fe3ca7d7007",
185 | "name" : "realm-admin",
186 | "description" : "${role_realm-admin}",
187 | "composite" : true,
188 | "composites" : {
189 | "client" : {
190 | "realm-management" : [ "manage-events", "view-authorization", "view-realm", "view-clients", "manage-identity-providers", "view-identity-providers", "query-groups", "query-clients", "manage-users", "manage-authorization", "view-users", "query-realms", "manage-clients", "view-events", "impersonation", "create-client", "manage-realm", "query-users" ]
191 | }
192 | },
193 | "clientRole" : true,
194 | "containerId" : "a28f054a-e493-4bbf-b361-e2ac4746467a",
195 | "attributes" : { }
196 | }, {
197 | "id" : "933c120c-959a-4509-8ab3-3619cd3fd26b",
198 | "name" : "view-events",
199 | "description" : "${role_view-events}",
200 | "composite" : false,
201 | "clientRole" : true,
202 | "containerId" : "a28f054a-e493-4bbf-b361-e2ac4746467a",
203 | "attributes" : { }
204 | }, {
205 | "id" : "2d356fb9-23c5-4db0-a63c-b211e7c23595",
206 | "name" : "create-client",
207 | "description" : "${role_create-client}",
208 | "composite" : false,
209 | "clientRole" : true,
210 | "containerId" : "a28f054a-e493-4bbf-b361-e2ac4746467a",
211 | "attributes" : { }
212 | }, {
213 | "id" : "5425cf69-ae05-4ab7-8180-bbda860f2f48",
214 | "name" : "manage-realm",
215 | "description" : "${role_manage-realm}",
216 | "composite" : false,
217 | "clientRole" : true,
218 | "containerId" : "a28f054a-e493-4bbf-b361-e2ac4746467a",
219 | "attributes" : { }
220 | }, {
221 | "id" : "79683db1-c3a5-472e-b370-e2e9d1d98d39",
222 | "name" : "query-users",
223 | "description" : "${role_query-users}",
224 | "composite" : false,
225 | "clientRole" : true,
226 | "containerId" : "a28f054a-e493-4bbf-b361-e2ac4746467a",
227 | "attributes" : { }
228 | } ],
229 | "app-minispa" : [ ],
230 | "security-admin-console" : [ ],
231 | "admin-cli" : [ ],
232 | "test-client" : [ ],
233 | "account-console" : [ ],
234 | "broker" : [ ],
235 | "account" : [ {
236 | "id" : "0fa469d0-6348-4b48-b10e-cebc4f22f9b5",
237 | "name" : "manage-account",
238 | "composite" : false,
239 | "clientRole" : true,
240 | "containerId" : "b3fa51ab-d818-4221-b5aa-4a6ea7a31d7d",
241 | "attributes" : { }
242 | }, {
243 | "id" : "51ff6b55-e881-4e54-a4a8-45febd80ad49",
244 | "name" : "view-profile",
245 | "composite" : false,
246 | "clientRole" : true,
247 | "containerId" : "b3fa51ab-d818-4221-b5aa-4a6ea7a31d7d",
248 | "attributes" : { }
249 | }, {
250 | "id" : "c4ab549c-4f9a-4d3e-9596-d8a2e30534b8",
251 | "name" : "delete-account",
252 | "description" : "${role_delete-account}",
253 | "composite" : false,
254 | "clientRole" : true,
255 | "containerId" : "b3fa51ab-d818-4221-b5aa-4a6ea7a31d7d",
256 | "attributes" : { }
257 | } ],
258 | "app-demospa" : [ ]
259 | }
260 | },
261 | "groups" : [ ],
262 | "defaultRoles" : [ "offline_access", "uma_authorization" ],
263 | "requiredCredentials" : [ "password" ],
264 | "passwordPolicy" : "hashIterations(5)",
265 | "otpPolicyType" : "totp",
266 | "otpPolicyAlgorithm" : "HmacSHA1",
267 | "otpPolicyInitialCounter" : 0,
268 | "otpPolicyDigits" : 6,
269 | "otpPolicyLookAheadWindow" : 1,
270 | "otpPolicyPeriod" : 30,
271 | "otpSupportedApplications" : [ "FreeOTP", "Google Authenticator" ],
272 | "webAuthnPolicyRpEntityName" : "keycloak",
273 | "webAuthnPolicySignatureAlgorithms" : [ "ES256" ],
274 | "webAuthnPolicyRpId" : "",
275 | "webAuthnPolicyAttestationConveyancePreference" : "not specified",
276 | "webAuthnPolicyAuthenticatorAttachment" : "not specified",
277 | "webAuthnPolicyRequireResidentKey" : "not specified",
278 | "webAuthnPolicyUserVerificationRequirement" : "not specified",
279 | "webAuthnPolicyCreateTimeout" : 0,
280 | "webAuthnPolicyAvoidSameAuthenticatorRegister" : false,
281 | "webAuthnPolicyAcceptableAaguids" : [ ],
282 | "webAuthnPolicyPasswordlessRpEntityName" : "keycloak",
283 | "webAuthnPolicyPasswordlessSignatureAlgorithms" : [ "ES256" ],
284 | "webAuthnPolicyPasswordlessRpId" : "",
285 | "webAuthnPolicyPasswordlessAttestationConveyancePreference" : "not specified",
286 | "webAuthnPolicyPasswordlessAuthenticatorAttachment" : "not specified",
287 | "webAuthnPolicyPasswordlessRequireResidentKey" : "not specified",
288 | "webAuthnPolicyPasswordlessUserVerificationRequirement" : "not specified",
289 | "webAuthnPolicyPasswordlessCreateTimeout" : 0,
290 | "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister" : false,
291 | "webAuthnPolicyPasswordlessAcceptableAaguids" : [ ],
292 | "users" : [ {
293 | "id" : "ea6c1a8d-8e27-4ed3-bf48-577d264b7d59",
294 | "createdTimestamp" : 1617652894097,
295 | "username" : "test-user-age22",
296 | "enabled" : true,
297 | "totp" : false,
298 | "emailVerified" : false,
299 | "firstName" : "Firstname",
300 | "lastName" : "Lastname",
301 | "attributes" : {
302 | "birthdate" : [ "1999-04-05" ]
303 | },
304 | "credentials" : [ {
305 | "id" : "5faf653a-517c-43e1-8739-f980f5092ccb",
306 | "type" : "password",
307 | "createdDate" : 1617656945687,
308 | "secretData" : "{\"value\":\"fucNCd7FNawqwHNBH6zHBUuItdcdZQZD5Lolo4AA9Dw+ixio0MWd7qEHc9iAZhlVgdPd7yGe9QkF0rbocxIQDQ==\",\"salt\":\"axBIzt79iBL5FiTYqeu3Ag==\",\"additionalParameters\":{}}",
309 | "credentialData" : "{\"hashIterations\":5,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
310 | } ],
311 | "disableableCredentialTypes" : [ ],
312 | "requiredActions" : [ ],
313 | "realmRoles" : [ "offline_access", "uma_authorization" ],
314 | "clientRoles" : {
315 | "account" : [ "manage-account", "view-profile" ]
316 | },
317 | "notBefore" : 0,
318 | "groups" : [ ]
319 | }, {
320 | "id" : "bf77ad09-3d60-4ee2-bbcc-3f74965336dc",
321 | "createdTimestamp" : 1617641449258,
322 | "username" : "tester",
323 | "enabled" : true,
324 | "totp" : false,
325 | "emailVerified" : false,
326 | "firstName" : "Theo",
327 | "lastName" : "Tester",
328 | "email" : "tester@localhost",
329 | "attributes" : {
330 | "birthdate" : [ "1984-12-13" ]
331 | },
332 | "credentials" : [ {
333 | "id" : "33342a2d-8757-4fad-9d7e-aa2fdd91a180",
334 | "type" : "password",
335 | "createdDate" : 1617659515087,
336 | "secretData" : "{\"value\":\"oeXgsFRRtdauTBSmrDMesCZWwABzWnSnsgWYsMohll6wTbiEfJhAnFaaQ6L6E8yx8Hqmkkk/yoWJ+rUrNKvhMQ==\",\"salt\":\"aaunx54jHMlDtiGHLXuLyQ==\",\"additionalParameters\":{}}",
337 | "credentialData" : "{\"hashIterations\":5,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
338 | } ],
339 | "disableableCredentialTypes" : [ ],
340 | "requiredActions" : [ ],
341 | "realmRoles" : [ "offline_access", "uma_authorization" ],
342 | "clientRoles" : {
343 | "account" : [ "manage-account", "view-profile" ]
344 | },
345 | "notBefore" : 0,
346 | "groups" : [ ]
347 | } ],
348 | "scopeMappings" : [ {
349 | "clientScope" : "offline_access",
350 | "roles" : [ "offline_access" ]
351 | } ],
352 | "clientScopeMappings" : {
353 | "account" : [ {
354 | "client" : "account-console",
355 | "roles" : [ "manage-account" ]
356 | } ]
357 | },
358 | "clients" : [ {
359 | "id" : "b3fa51ab-d818-4221-b5aa-4a6ea7a31d7d",
360 | "clientId" : "account",
361 | "name" : "${client_account}",
362 | "rootUrl" : "${authBaseUrl}",
363 | "baseUrl" : "/realms/acme/account/",
364 | "surrogateAuthRequired" : false,
365 | "enabled" : true,
366 | "alwaysDisplayInConsole" : false,
367 | "clientAuthenticatorType" : "client-secret",
368 | "secret" : "**********",
369 | "defaultRoles" : [ "manage-account", "view-profile" ],
370 | "redirectUris" : [ "/realms/acme/account/*" ],
371 | "webOrigins" : [ ],
372 | "notBefore" : 0,
373 | "bearerOnly" : false,
374 | "consentRequired" : false,
375 | "standardFlowEnabled" : true,
376 | "implicitFlowEnabled" : false,
377 | "directAccessGrantsEnabled" : false,
378 | "serviceAccountsEnabled" : false,
379 | "publicClient" : false,
380 | "frontchannelLogout" : false,
381 | "protocol" : "openid-connect",
382 | "attributes" : { },
383 | "authenticationFlowBindingOverrides" : { },
384 | "fullScopeAllowed" : false,
385 | "nodeReRegistrationTimeout" : 0,
386 | "defaultClientScopes" : [ "web-origins", "role_list", "profile", "roles", "email" ],
387 | "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ]
388 | }, {
389 | "id" : "f16c3bb1-f61b-4740-bcc7-bc65a9f7f055",
390 | "clientId" : "account-console",
391 | "name" : "${client_account-console}",
392 | "rootUrl" : "${authBaseUrl}",
393 | "baseUrl" : "/realms/acme/account/",
394 | "surrogateAuthRequired" : false,
395 | "enabled" : true,
396 | "alwaysDisplayInConsole" : false,
397 | "clientAuthenticatorType" : "client-secret",
398 | "secret" : "**********",
399 | "redirectUris" : [ "/realms/acme/account/*" ],
400 | "webOrigins" : [ ],
401 | "notBefore" : 0,
402 | "bearerOnly" : false,
403 | "consentRequired" : false,
404 | "standardFlowEnabled" : true,
405 | "implicitFlowEnabled" : false,
406 | "directAccessGrantsEnabled" : false,
407 | "serviceAccountsEnabled" : false,
408 | "publicClient" : true,
409 | "frontchannelLogout" : false,
410 | "protocol" : "openid-connect",
411 | "attributes" : {
412 | "pkce.code.challenge.method" : "S256"
413 | },
414 | "authenticationFlowBindingOverrides" : { },
415 | "fullScopeAllowed" : false,
416 | "nodeReRegistrationTimeout" : 0,
417 | "protocolMappers" : [ {
418 | "id" : "1721fc2d-249e-4ba5-837b-d7fb5e25c0f4",
419 | "name" : "audience resolve",
420 | "protocol" : "openid-connect",
421 | "protocolMapper" : "oidc-audience-resolve-mapper",
422 | "consentRequired" : false,
423 | "config" : { }
424 | } ],
425 | "defaultClientScopes" : [ "web-origins", "role_list", "profile", "roles", "email" ],
426 | "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ]
427 | }, {
428 | "id" : "6b3fd885-8031-4498-a351-b5ce1d0659b6",
429 | "clientId" : "admin-cli",
430 | "name" : "${client_admin-cli}",
431 | "surrogateAuthRequired" : false,
432 | "enabled" : true,
433 | "alwaysDisplayInConsole" : false,
434 | "clientAuthenticatorType" : "client-secret",
435 | "secret" : "**********",
436 | "redirectUris" : [ ],
437 | "webOrigins" : [ ],
438 | "notBefore" : 0,
439 | "bearerOnly" : false,
440 | "consentRequired" : false,
441 | "standardFlowEnabled" : false,
442 | "implicitFlowEnabled" : false,
443 | "directAccessGrantsEnabled" : true,
444 | "serviceAccountsEnabled" : false,
445 | "publicClient" : true,
446 | "frontchannelLogout" : false,
447 | "protocol" : "openid-connect",
448 | "attributes" : { },
449 | "authenticationFlowBindingOverrides" : { },
450 | "fullScopeAllowed" : false,
451 | "nodeReRegistrationTimeout" : 0,
452 | "defaultClientScopes" : [ "role_list", "roles", "profile", "email" ],
453 | "optionalClientScopes" : [ "address", "phone", "acme.ageinfo", "offline_access", "microprofile-jwt", "acme.profile" ]
454 | }, {
455 | "id" : "7d7a03ee-793f-4669-bd75-6f4741d4b0e5",
456 | "clientId" : "app-demospa",
457 | "name" : "Demo SPA",
458 | "rootUrl" : "http://demo.local:4000",
459 | "adminUrl" : "",
460 | "baseUrl" : "/?client_id=app-demospa",
461 | "surrogateAuthRequired" : false,
462 | "enabled" : true,
463 | "alwaysDisplayInConsole" : false,
464 | "clientAuthenticatorType" : "client-secret",
465 | "secret" : "**********",
466 | "redirectUris" : [ "/*" ],
467 | "webOrigins" : [ "+" ],
468 | "notBefore" : 0,
469 | "bearerOnly" : false,
470 | "consentRequired" : false,
471 | "standardFlowEnabled" : true,
472 | "implicitFlowEnabled" : false,
473 | "directAccessGrantsEnabled" : false,
474 | "serviceAccountsEnabled" : false,
475 | "publicClient" : true,
476 | "frontchannelLogout" : false,
477 | "protocol" : "openid-connect",
478 | "attributes" : {
479 | "saml.assertion.signature" : "false",
480 | "saml.force.post.binding" : "false",
481 | "saml.multivalued.roles" : "false",
482 | "saml.encrypt" : "false",
483 | "backchannel.logout.revoke.offline.tokens" : "false",
484 | "saml.server.signature" : "false",
485 | "saml.server.signature.keyinfo.ext" : "false",
486 | "exclude.session.state.from.auth.response" : "false",
487 | "backchannel.logout.session.required" : "true",
488 | "client_credentials.use_refresh_token" : "false",
489 | "saml_force_name_id_format" : "false",
490 | "saml.client.signature" : "false",
491 | "tls.client.certificate.bound.access.tokens" : "false",
492 | "saml.authnstatement" : "false",
493 | "display.on.consent.screen" : "false",
494 | "saml.onetimeuse.condition" : "false"
495 | },
496 | "authenticationFlowBindingOverrides" : { },
497 | "fullScopeAllowed" : false,
498 | "nodeReRegistrationTimeout" : -1,
499 | "defaultClientScopes" : [ "web-origins", "role_list", "profile", "roles", "email" ],
500 | "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ]
501 | }, {
502 | "id" : "f0bbf46f-2b1b-4dd3-a4d2-23fd4a0d3823",
503 | "clientId" : "app-minispa",
504 | "name" : "Mni SPA",
505 | "rootUrl" : "http://localhost:4000",
506 | "adminUrl" : "",
507 | "baseUrl" : "/",
508 | "surrogateAuthRequired" : false,
509 | "enabled" : true,
510 | "alwaysDisplayInConsole" : false,
511 | "clientAuthenticatorType" : "client-secret",
512 | "secret" : "**********",
513 | "redirectUris" : [ "/*" ],
514 | "webOrigins" : [ "+" ],
515 | "notBefore" : 0,
516 | "bearerOnly" : false,
517 | "consentRequired" : false,
518 | "standardFlowEnabled" : true,
519 | "implicitFlowEnabled" : false,
520 | "directAccessGrantsEnabled" : false,
521 | "serviceAccountsEnabled" : false,
522 | "publicClient" : true,
523 | "frontchannelLogout" : false,
524 | "protocol" : "openid-connect",
525 | "attributes" : {
526 | "saml.assertion.signature" : "false",
527 | "saml.force.post.binding" : "false",
528 | "saml.multivalued.roles" : "false",
529 | "saml.encrypt" : "false",
530 | "backchannel.logout.revoke.offline.tokens" : "false",
531 | "saml.server.signature" : "false",
532 | "saml.server.signature.keyinfo.ext" : "false",
533 | "exclude.session.state.from.auth.response" : "false",
534 | "backchannel.logout.session.required" : "false",
535 | "client_credentials.use_refresh_token" : "false",
536 | "saml_force_name_id_format" : "false",
537 | "saml.client.signature" : "false",
538 | "tls.client.certificate.bound.access.tokens" : "false",
539 | "saml.authnstatement" : "false",
540 | "display.on.consent.screen" : "false",
541 | "saml.onetimeuse.condition" : "false"
542 | },
543 | "authenticationFlowBindingOverrides" : { },
544 | "fullScopeAllowed" : false,
545 | "nodeReRegistrationTimeout" : -1,
546 | "defaultClientScopes" : [ "role_list", "email" ],
547 | "optionalClientScopes" : [ "acme.ageinfo", "phone", "acme.profile" ]
548 | }, {
549 | "id" : "d827cb0d-4d73-400a-a5c3-9f89eedb1c41",
550 | "clientId" : "broker",
551 | "name" : "${client_broker}",
552 | "surrogateAuthRequired" : false,
553 | "enabled" : true,
554 | "alwaysDisplayInConsole" : false,
555 | "clientAuthenticatorType" : "client-secret",
556 | "secret" : "**********",
557 | "redirectUris" : [ ],
558 | "webOrigins" : [ ],
559 | "notBefore" : 0,
560 | "bearerOnly" : false,
561 | "consentRequired" : false,
562 | "standardFlowEnabled" : true,
563 | "implicitFlowEnabled" : false,
564 | "directAccessGrantsEnabled" : false,
565 | "serviceAccountsEnabled" : false,
566 | "publicClient" : false,
567 | "frontchannelLogout" : false,
568 | "protocol" : "openid-connect",
569 | "attributes" : { },
570 | "authenticationFlowBindingOverrides" : { },
571 | "fullScopeAllowed" : false,
572 | "nodeReRegistrationTimeout" : 0,
573 | "defaultClientScopes" : [ "web-origins", "role_list", "profile", "roles", "email" ],
574 | "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ]
575 | }, {
576 | "id" : "a28f054a-e493-4bbf-b361-e2ac4746467a",
577 | "clientId" : "realm-management",
578 | "name" : "${client_realm-management}",
579 | "surrogateAuthRequired" : false,
580 | "enabled" : true,
581 | "alwaysDisplayInConsole" : false,
582 | "clientAuthenticatorType" : "client-secret",
583 | "secret" : "**********",
584 | "redirectUris" : [ ],
585 | "webOrigins" : [ ],
586 | "notBefore" : 0,
587 | "bearerOnly" : true,
588 | "consentRequired" : false,
589 | "standardFlowEnabled" : true,
590 | "implicitFlowEnabled" : false,
591 | "directAccessGrantsEnabled" : false,
592 | "serviceAccountsEnabled" : false,
593 | "publicClient" : false,
594 | "frontchannelLogout" : false,
595 | "protocol" : "openid-connect",
596 | "attributes" : { },
597 | "authenticationFlowBindingOverrides" : { },
598 | "fullScopeAllowed" : false,
599 | "nodeReRegistrationTimeout" : 0,
600 | "defaultClientScopes" : [ "web-origins", "role_list", "profile", "roles", "email" ],
601 | "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ]
602 | }, {
603 | "id" : "4f70f1f5-4776-4c06-9fc5-ca32ce890649",
604 | "clientId" : "security-admin-console",
605 | "name" : "${client_security-admin-console}",
606 | "rootUrl" : "${authAdminUrl}",
607 | "baseUrl" : "/admin/acme/console/",
608 | "surrogateAuthRequired" : false,
609 | "enabled" : true,
610 | "alwaysDisplayInConsole" : false,
611 | "clientAuthenticatorType" : "client-secret",
612 | "secret" : "**********",
613 | "redirectUris" : [ "/admin/acme/console/*" ],
614 | "webOrigins" : [ "+" ],
615 | "notBefore" : 0,
616 | "bearerOnly" : false,
617 | "consentRequired" : false,
618 | "standardFlowEnabled" : true,
619 | "implicitFlowEnabled" : false,
620 | "directAccessGrantsEnabled" : false,
621 | "serviceAccountsEnabled" : false,
622 | "publicClient" : true,
623 | "frontchannelLogout" : false,
624 | "protocol" : "openid-connect",
625 | "attributes" : {
626 | "pkce.code.challenge.method" : "S256"
627 | },
628 | "authenticationFlowBindingOverrides" : { },
629 | "fullScopeAllowed" : false,
630 | "nodeReRegistrationTimeout" : 0,
631 | "protocolMappers" : [ {
632 | "id" : "2f459275-4d52-4cc7-b97a-93bd14dfc87b",
633 | "name" : "locale",
634 | "protocol" : "openid-connect",
635 | "protocolMapper" : "oidc-usermodel-attribute-mapper",
636 | "consentRequired" : false,
637 | "config" : {
638 | "userinfo.token.claim" : "true",
639 | "user.attribute" : "locale",
640 | "id.token.claim" : "true",
641 | "access.token.claim" : "true",
642 | "claim.name" : "locale",
643 | "jsonType.label" : "String"
644 | }
645 | } ],
646 | "defaultClientScopes" : [ "web-origins", "role_list", "profile", "roles", "email" ],
647 | "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ]
648 | }, {
649 | "id" : "3e52ddce-338c-4e42-87a9-840a0b6a31cf",
650 | "clientId" : "test-client",
651 | "surrogateAuthRequired" : false,
652 | "enabled" : true,
653 | "alwaysDisplayInConsole" : false,
654 | "clientAuthenticatorType" : "client-secret",
655 | "secret" : "3f455bd5-9495-41f8-9085-83ccebcc95f2",
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 | "saml.assertion.signature" : "false",
670 | "access.token.lifespan" : "10800",
671 | "saml.force.post.binding" : "false",
672 | "saml.multivalued.roles" : "false",
673 | "saml.encrypt" : "false",
674 | "backchannel.logout.revoke.offline.tokens" : "false",
675 | "saml.server.signature" : "false",
676 | "saml.server.signature.keyinfo.ext" : "false",
677 | "exclude.session.state.from.auth.response" : "false",
678 | "backchannel.logout.session.required" : "false",
679 | "client_credentials.use_refresh_token" : "false",
680 | "saml_force_name_id_format" : "false",
681 | "saml.client.signature" : "false",
682 | "tls.client.certificate.bound.access.tokens" : "false",
683 | "saml.authnstatement" : "false",
684 | "display.on.consent.screen" : "false",
685 | "saml.onetimeuse.condition" : "false"
686 | },
687 | "authenticationFlowBindingOverrides" : { },
688 | "fullScopeAllowed" : true,
689 | "nodeReRegistrationTimeout" : -1,
690 | "defaultClientScopes" : [ "role_list", "acme.api", "email" ],
691 | "optionalClientScopes" : [ "address", "phone", "acme.ageinfo", "acme.profile" ]
692 | } ],
693 | "clientScopes" : [ {
694 | "id" : "d1fcb261-d1d3-4158-b540-f0650fe52467",
695 | "name" : "acme.ageinfo",
696 | "description" : "Age Information",
697 | "protocol" : "openid-connect",
698 | "attributes" : {
699 | "include.in.token.scope" : "true",
700 | "display.on.consent.screen" : "true"
701 | },
702 | "protocolMappers" : [ {
703 | "id" : "451ff95b-bb55-4745-bd49-7d357a9310b3",
704 | "name" : "Age Info Claim Mapping",
705 | "protocol" : "openid-connect",
706 | "protocolMapper" : "acme-ageinfo-mapper",
707 | "consentRequired" : false,
708 | "config" : {
709 | "id.token.claim" : "true",
710 | "access.token.claim" : "false",
711 | "userinfo.token.claim" : "true"
712 | }
713 | } ]
714 | }, {
715 | "id" : "899aaa48-2e24-45a5-b7d6-9ab8102f9dd9",
716 | "name" : "acme.api",
717 | "description" : "Acme API Access",
718 | "protocol" : "openid-connect",
719 | "attributes" : {
720 | "include.in.token.scope" : "true",
721 | "display.on.consent.screen" : "true"
722 | }
723 | }, {
724 | "id" : "d959ecfe-693e-4289-8cd3-722e4ddc3ac6",
725 | "name" : "acme.profile",
726 | "description" : "Acme Profile Information",
727 | "protocol" : "openid-connect",
728 | "attributes" : {
729 | "include.in.token.scope" : "true",
730 | "display.on.consent.screen" : "true"
731 | },
732 | "protocolMappers" : [ {
733 | "id" : "7c1e76a9-2c08-45b6-a235-197ef1e9f1e2",
734 | "name" : "family name",
735 | "protocol" : "openid-connect",
736 | "protocolMapper" : "oidc-usermodel-property-mapper",
737 | "consentRequired" : false,
738 | "config" : {
739 | "userinfo.token.claim" : "true",
740 | "user.attribute" : "lastName",
741 | "id.token.claim" : "true",
742 | "access.token.claim" : "true",
743 | "claim.name" : "family_name",
744 | "jsonType.label" : "String"
745 | }
746 | }, {
747 | "id" : "b186b546-e46d-4e7d-bdec-329889b4fa03",
748 | "name" : "username",
749 | "protocol" : "openid-connect",
750 | "protocolMapper" : "oidc-usermodel-property-mapper",
751 | "consentRequired" : false,
752 | "config" : {
753 | "userinfo.token.claim" : "true",
754 | "user.attribute" : "username",
755 | "id.token.claim" : "true",
756 | "access.token.claim" : "true",
757 | "claim.name" : "preferred_username",
758 | "jsonType.label" : "String"
759 | }
760 | }, {
761 | "id" : "463183a6-a9b5-4bfd-9c93-b0237d26a194",
762 | "name" : "given name",
763 | "protocol" : "openid-connect",
764 | "protocolMapper" : "oidc-usermodel-property-mapper",
765 | "consentRequired" : false,
766 | "config" : {
767 | "userinfo.token.claim" : "true",
768 | "user.attribute" : "firstName",
769 | "id.token.claim" : "true",
770 | "access.token.claim" : "true",
771 | "claim.name" : "given_name",
772 | "jsonType.label" : "String"
773 | }
774 | }, {
775 | "id" : "ba5f7999-e7cf-40db-91c5-99175dad992e",
776 | "name" : "full name",
777 | "protocol" : "openid-connect",
778 | "protocolMapper" : "oidc-full-name-mapper",
779 | "consentRequired" : false,
780 | "config" : {
781 | "id.token.claim" : "true",
782 | "access.token.claim" : "true",
783 | "userinfo.token.claim" : "true"
784 | }
785 | } ]
786 | }, {
787 | "id" : "a28a09e8-a5f6-4337-820c-943ee574b419",
788 | "name" : "address",
789 | "description" : "OpenID Connect built-in scope: address",
790 | "protocol" : "openid-connect",
791 | "attributes" : {
792 | "include.in.token.scope" : "true",
793 | "display.on.consent.screen" : "true",
794 | "consent.screen.text" : "${addressScopeConsentText}"
795 | },
796 | "protocolMappers" : [ {
797 | "id" : "630f2115-8add-4a6c-8aab-f5afbf69cb50",
798 | "name" : "address",
799 | "protocol" : "openid-connect",
800 | "protocolMapper" : "oidc-address-mapper",
801 | "consentRequired" : false,
802 | "config" : {
803 | "user.attribute.formatted" : "formatted",
804 | "user.attribute.country" : "country",
805 | "user.attribute.postal_code" : "postal_code",
806 | "userinfo.token.claim" : "true",
807 | "user.attribute.street" : "street",
808 | "id.token.claim" : "true",
809 | "user.attribute.region" : "region",
810 | "access.token.claim" : "true",
811 | "user.attribute.locality" : "locality"
812 | }
813 | } ]
814 | }, {
815 | "id" : "740fdfe3-57b1-46c6-b34f-d20a44d81f86",
816 | "name" : "email",
817 | "description" : "OpenID Connect built-in scope: email",
818 | "protocol" : "openid-connect",
819 | "attributes" : {
820 | "include.in.token.scope" : "true",
821 | "display.on.consent.screen" : "true",
822 | "consent.screen.text" : "${emailScopeConsentText}"
823 | },
824 | "protocolMappers" : [ {
825 | "id" : "b6b2ebf2-1968-4d96-b617-dd9974ac3206",
826 | "name" : "email",
827 | "protocol" : "openid-connect",
828 | "protocolMapper" : "oidc-usermodel-property-mapper",
829 | "consentRequired" : false,
830 | "config" : {
831 | "userinfo.token.claim" : "true",
832 | "user.attribute" : "email",
833 | "id.token.claim" : "true",
834 | "access.token.claim" : "true",
835 | "claim.name" : "email",
836 | "jsonType.label" : "String"
837 | }
838 | }, {
839 | "id" : "15c34ab3-0c92-430d-b92c-f08f74da9ccd",
840 | "name" : "email verified",
841 | "protocol" : "openid-connect",
842 | "protocolMapper" : "oidc-usermodel-property-mapper",
843 | "consentRequired" : false,
844 | "config" : {
845 | "userinfo.token.claim" : "true",
846 | "user.attribute" : "emailVerified",
847 | "id.token.claim" : "true",
848 | "access.token.claim" : "true",
849 | "claim.name" : "email_verified",
850 | "jsonType.label" : "boolean"
851 | }
852 | } ]
853 | }, {
854 | "id" : "38617936-7698-403f-b786-e54383a28f0e",
855 | "name" : "microprofile-jwt",
856 | "description" : "Microprofile - JWT built-in scope",
857 | "protocol" : "openid-connect",
858 | "attributes" : {
859 | "include.in.token.scope" : "true",
860 | "display.on.consent.screen" : "false"
861 | },
862 | "protocolMappers" : [ {
863 | "id" : "eca70bcc-04bb-46d4-a2b8-5af4859f27a6",
864 | "name" : "groups",
865 | "protocol" : "openid-connect",
866 | "protocolMapper" : "oidc-usermodel-realm-role-mapper",
867 | "consentRequired" : false,
868 | "config" : {
869 | "multivalued" : "true",
870 | "userinfo.token.claim" : "true",
871 | "user.attribute" : "foo",
872 | "id.token.claim" : "true",
873 | "access.token.claim" : "true",
874 | "claim.name" : "groups",
875 | "jsonType.label" : "String"
876 | }
877 | }, {
878 | "id" : "22575165-960f-4716-93cc-2dfc97377edb",
879 | "name" : "upn",
880 | "protocol" : "openid-connect",
881 | "protocolMapper" : "oidc-usermodel-property-mapper",
882 | "consentRequired" : false,
883 | "config" : {
884 | "userinfo.token.claim" : "true",
885 | "user.attribute" : "username",
886 | "id.token.claim" : "true",
887 | "access.token.claim" : "true",
888 | "claim.name" : "upn",
889 | "jsonType.label" : "String"
890 | }
891 | } ]
892 | }, {
893 | "id" : "86cab515-2542-4a88-a197-9fd4ce1a1578",
894 | "name" : "offline_access",
895 | "description" : "OpenID Connect built-in scope: offline_access",
896 | "protocol" : "openid-connect",
897 | "attributes" : {
898 | "consent.screen.text" : "${offlineAccessScopeConsentText}",
899 | "display.on.consent.screen" : "true"
900 | }
901 | }, {
902 | "id" : "e3756163-8afb-4ddd-8f88-a9e73ad51fcf",
903 | "name" : "phone",
904 | "description" : "OpenID Connect built-in scope: phone",
905 | "protocol" : "openid-connect",
906 | "attributes" : {
907 | "include.in.token.scope" : "true",
908 | "display.on.consent.screen" : "true",
909 | "consent.screen.text" : "${phoneScopeConsentText}"
910 | },
911 | "protocolMappers" : [ {
912 | "id" : "1720c48e-4717-4443-bb7b-791ef87e9b62",
913 | "name" : "phone number",
914 | "protocol" : "openid-connect",
915 | "protocolMapper" : "oidc-usermodel-attribute-mapper",
916 | "consentRequired" : false,
917 | "config" : {
918 | "userinfo.token.claim" : "true",
919 | "user.attribute" : "phoneNumber",
920 | "id.token.claim" : "true",
921 | "access.token.claim" : "true",
922 | "claim.name" : "phone_number",
923 | "jsonType.label" : "String"
924 | }
925 | }, {
926 | "id" : "8d80394f-d5b7-4e66-80e5-1fc815c165a2",
927 | "name" : "phone number verified",
928 | "protocol" : "openid-connect",
929 | "protocolMapper" : "oidc-usermodel-attribute-mapper",
930 | "consentRequired" : false,
931 | "config" : {
932 | "userinfo.token.claim" : "true",
933 | "user.attribute" : "phoneNumberVerified",
934 | "id.token.claim" : "true",
935 | "access.token.claim" : "true",
936 | "claim.name" : "phone_number_verified",
937 | "jsonType.label" : "boolean"
938 | }
939 | } ]
940 | }, {
941 | "id" : "f6923454-f735-409e-832e-ba0160e4fd60",
942 | "name" : "profile",
943 | "description" : "OpenID Connect built-in scope: profile",
944 | "protocol" : "openid-connect",
945 | "attributes" : {
946 | "include.in.token.scope" : "true",
947 | "display.on.consent.screen" : "true",
948 | "consent.screen.text" : "${profileScopeConsentText}"
949 | },
950 | "protocolMappers" : [ {
951 | "id" : "5ce0007d-6460-4e4f-8d43-dd1c3092a4ce",
952 | "name" : "locale",
953 | "protocol" : "openid-connect",
954 | "protocolMapper" : "oidc-usermodel-attribute-mapper",
955 | "consentRequired" : false,
956 | "config" : {
957 | "userinfo.token.claim" : "true",
958 | "user.attribute" : "locale",
959 | "id.token.claim" : "true",
960 | "access.token.claim" : "true",
961 | "claim.name" : "locale",
962 | "jsonType.label" : "String"
963 | }
964 | }, {
965 | "id" : "591cb585-2eee-4875-94b5-ee242bc6d03b",
966 | "name" : "updated at",
967 | "protocol" : "openid-connect",
968 | "protocolMapper" : "oidc-usermodel-attribute-mapper",
969 | "consentRequired" : false,
970 | "config" : {
971 | "userinfo.token.claim" : "true",
972 | "user.attribute" : "updatedAt",
973 | "id.token.claim" : "true",
974 | "access.token.claim" : "true",
975 | "claim.name" : "updated_at",
976 | "jsonType.label" : "String"
977 | }
978 | }, {
979 | "id" : "ed63103c-50da-4d65-9a5a-6ccc1d73c17f",
980 | "name" : "gender",
981 | "protocol" : "openid-connect",
982 | "protocolMapper" : "oidc-usermodel-attribute-mapper",
983 | "consentRequired" : false,
984 | "config" : {
985 | "userinfo.token.claim" : "true",
986 | "user.attribute" : "gender",
987 | "id.token.claim" : "true",
988 | "access.token.claim" : "true",
989 | "claim.name" : "gender",
990 | "jsonType.label" : "String"
991 | }
992 | }, {
993 | "id" : "6fa5af50-3123-4cdb-b5f2-9d5e00124b13",
994 | "name" : "username",
995 | "protocol" : "openid-connect",
996 | "protocolMapper" : "oidc-usermodel-property-mapper",
997 | "consentRequired" : false,
998 | "config" : {
999 | "userinfo.token.claim" : "true",
1000 | "user.attribute" : "username",
1001 | "id.token.claim" : "true",
1002 | "access.token.claim" : "true",
1003 | "claim.name" : "preferred_username",
1004 | "jsonType.label" : "String"
1005 | }
1006 | }, {
1007 | "id" : "f0f521b3-0658-4d89-ae41-ae342d424dc2",
1008 | "name" : "full name",
1009 | "protocol" : "openid-connect",
1010 | "protocolMapper" : "oidc-full-name-mapper",
1011 | "consentRequired" : false,
1012 | "config" : {
1013 | "id.token.claim" : "true",
1014 | "access.token.claim" : "true",
1015 | "userinfo.token.claim" : "true"
1016 | }
1017 | }, {
1018 | "id" : "bf1e2b1f-7520-4231-a0c5-8e472d8607da",
1019 | "name" : "profile",
1020 | "protocol" : "openid-connect",
1021 | "protocolMapper" : "oidc-usermodel-attribute-mapper",
1022 | "consentRequired" : false,
1023 | "config" : {
1024 | "userinfo.token.claim" : "true",
1025 | "user.attribute" : "profile",
1026 | "id.token.claim" : "true",
1027 | "access.token.claim" : "true",
1028 | "claim.name" : "profile",
1029 | "jsonType.label" : "String"
1030 | }
1031 | }, {
1032 | "id" : "4a9a972e-1fa3-4b85-a6c9-94d50af68bb4",
1033 | "name" : "given name",
1034 | "protocol" : "openid-connect",
1035 | "protocolMapper" : "oidc-usermodel-property-mapper",
1036 | "consentRequired" : false,
1037 | "config" : {
1038 | "userinfo.token.claim" : "true",
1039 | "user.attribute" : "firstName",
1040 | "id.token.claim" : "true",
1041 | "access.token.claim" : "true",
1042 | "claim.name" : "given_name",
1043 | "jsonType.label" : "String"
1044 | }
1045 | }, {
1046 | "id" : "85f13da7-7a32-4e13-8bf8-74fca83c1637",
1047 | "name" : "nickname",
1048 | "protocol" : "openid-connect",
1049 | "protocolMapper" : "oidc-usermodel-attribute-mapper",
1050 | "consentRequired" : false,
1051 | "config" : {
1052 | "userinfo.token.claim" : "true",
1053 | "user.attribute" : "nickname",
1054 | "id.token.claim" : "true",
1055 | "access.token.claim" : "true",
1056 | "claim.name" : "nickname",
1057 | "jsonType.label" : "String"
1058 | }
1059 | }, {
1060 | "id" : "cfe31027-1996-410c-ad38-1307d73d33a4",
1061 | "name" : "zoneinfo",
1062 | "protocol" : "openid-connect",
1063 | "protocolMapper" : "oidc-usermodel-attribute-mapper",
1064 | "consentRequired" : false,
1065 | "config" : {
1066 | "userinfo.token.claim" : "true",
1067 | "user.attribute" : "zoneinfo",
1068 | "id.token.claim" : "true",
1069 | "access.token.claim" : "true",
1070 | "claim.name" : "zoneinfo",
1071 | "jsonType.label" : "String"
1072 | }
1073 | }, {
1074 | "id" : "6d9fe48e-b10b-42cf-9da0-6db943d68ebb",
1075 | "name" : "picture",
1076 | "protocol" : "openid-connect",
1077 | "protocolMapper" : "oidc-usermodel-attribute-mapper",
1078 | "consentRequired" : false,
1079 | "config" : {
1080 | "userinfo.token.claim" : "true",
1081 | "user.attribute" : "picture",
1082 | "id.token.claim" : "true",
1083 | "access.token.claim" : "true",
1084 | "claim.name" : "picture",
1085 | "jsonType.label" : "String"
1086 | }
1087 | }, {
1088 | "id" : "ceba7294-6ffc-46f9-959a-2467b88f958b",
1089 | "name" : "birthdate",
1090 | "protocol" : "openid-connect",
1091 | "protocolMapper" : "oidc-usermodel-attribute-mapper",
1092 | "consentRequired" : false,
1093 | "config" : {
1094 | "userinfo.token.claim" : "true",
1095 | "user.attribute" : "birthdate",
1096 | "id.token.claim" : "true",
1097 | "access.token.claim" : "true",
1098 | "claim.name" : "birthdate",
1099 | "jsonType.label" : "String"
1100 | }
1101 | }, {
1102 | "id" : "7fb278cf-46ee-4b3e-a67c-d33fc2511e46",
1103 | "name" : "middle name",
1104 | "protocol" : "openid-connect",
1105 | "protocolMapper" : "oidc-usermodel-attribute-mapper",
1106 | "consentRequired" : false,
1107 | "config" : {
1108 | "userinfo.token.claim" : "true",
1109 | "user.attribute" : "middleName",
1110 | "id.token.claim" : "true",
1111 | "access.token.claim" : "true",
1112 | "claim.name" : "middle_name",
1113 | "jsonType.label" : "String"
1114 | }
1115 | }, {
1116 | "id" : "1a18fa81-aa25-4841-8d2f-03da2b2d8335",
1117 | "name" : "website",
1118 | "protocol" : "openid-connect",
1119 | "protocolMapper" : "oidc-usermodel-attribute-mapper",
1120 | "consentRequired" : false,
1121 | "config" : {
1122 | "userinfo.token.claim" : "true",
1123 | "user.attribute" : "website",
1124 | "id.token.claim" : "true",
1125 | "access.token.claim" : "true",
1126 | "claim.name" : "website",
1127 | "jsonType.label" : "String"
1128 | }
1129 | }, {
1130 | "id" : "5eade531-7a8a-4ea8-bddd-14610fe359e0",
1131 | "name" : "family name",
1132 | "protocol" : "openid-connect",
1133 | "protocolMapper" : "oidc-usermodel-property-mapper",
1134 | "consentRequired" : false,
1135 | "config" : {
1136 | "userinfo.token.claim" : "true",
1137 | "user.attribute" : "lastName",
1138 | "id.token.claim" : "true",
1139 | "access.token.claim" : "true",
1140 | "claim.name" : "family_name",
1141 | "jsonType.label" : "String"
1142 | }
1143 | } ]
1144 | }, {
1145 | "id" : "4e3c2cdc-f0be-4328-b9d2-181864aeefb0",
1146 | "name" : "role_list",
1147 | "description" : "SAML role list",
1148 | "protocol" : "saml",
1149 | "attributes" : {
1150 | "consent.screen.text" : "${samlRoleListScopeConsentText}",
1151 | "display.on.consent.screen" : "true"
1152 | },
1153 | "protocolMappers" : [ {
1154 | "id" : "72319e95-e9dd-4e33-b61c-28dde2ddefe3",
1155 | "name" : "role list",
1156 | "protocol" : "saml",
1157 | "protocolMapper" : "saml-role-list-mapper",
1158 | "consentRequired" : false,
1159 | "config" : {
1160 | "single" : "false",
1161 | "attribute.nameformat" : "Basic",
1162 | "attribute.name" : "Role"
1163 | }
1164 | } ]
1165 | }, {
1166 | "id" : "f6f9d855-5921-4163-9394-68886511bfe9",
1167 | "name" : "roles",
1168 | "description" : "OpenID Connect scope for add user roles to the access token",
1169 | "protocol" : "openid-connect",
1170 | "attributes" : {
1171 | "include.in.token.scope" : "false",
1172 | "display.on.consent.screen" : "true",
1173 | "consent.screen.text" : "${rolesScopeConsentText}"
1174 | },
1175 | "protocolMappers" : [ {
1176 | "id" : "e4a6683d-b42d-472c-9590-6614a8ffecd2",
1177 | "name" : "realm roles",
1178 | "protocol" : "openid-connect",
1179 | "protocolMapper" : "oidc-usermodel-realm-role-mapper",
1180 | "consentRequired" : false,
1181 | "config" : {
1182 | "user.attribute" : "foo",
1183 | "access.token.claim" : "true",
1184 | "claim.name" : "realm_access.roles",
1185 | "jsonType.label" : "String",
1186 | "multivalued" : "true"
1187 | }
1188 | }, {
1189 | "id" : "d016c9d0-e8f9-4ec5-97f3-5a3a4bec4956",
1190 | "name" : "client roles",
1191 | "protocol" : "openid-connect",
1192 | "protocolMapper" : "oidc-usermodel-client-role-mapper",
1193 | "consentRequired" : false,
1194 | "config" : {
1195 | "user.attribute" : "foo",
1196 | "access.token.claim" : "true",
1197 | "claim.name" : "resource_access.${client_id}.roles",
1198 | "jsonType.label" : "String",
1199 | "multivalued" : "true"
1200 | }
1201 | }, {
1202 | "id" : "7c6d3738-194e-4a23-abca-442a0b183ad2",
1203 | "name" : "audience resolve",
1204 | "protocol" : "openid-connect",
1205 | "protocolMapper" : "oidc-audience-resolve-mapper",
1206 | "consentRequired" : false,
1207 | "config" : { }
1208 | } ]
1209 | }, {
1210 | "id" : "437cf929-79ce-4ab3-b116-e293294c16b1",
1211 | "name" : "web-origins",
1212 | "description" : "OpenID Connect scope for add allowed web origins to the access token",
1213 | "protocol" : "openid-connect",
1214 | "attributes" : {
1215 | "include.in.token.scope" : "false",
1216 | "display.on.consent.screen" : "false",
1217 | "consent.screen.text" : ""
1218 | },
1219 | "protocolMappers" : [ {
1220 | "id" : "ec4848c9-1fe8-4013-b377-d91fa7703b19",
1221 | "name" : "allowed web origins",
1222 | "protocol" : "openid-connect",
1223 | "protocolMapper" : "oidc-allowed-origins-mapper",
1224 | "consentRequired" : false,
1225 | "config" : { }
1226 | } ]
1227 | } ],
1228 | "defaultDefaultClientScopes" : [ "web-origins", "role_list", "email", "profile", "roles", "acme.api" ],
1229 | "defaultOptionalClientScopes" : [ "microprofile-jwt", "offline_access", "address", "phone" ],
1230 | "browserSecurityHeaders" : {
1231 | "contentSecurityPolicyReportOnly" : "",
1232 | "xContentTypeOptions" : "nosniff",
1233 | "xRobotsTag" : "none",
1234 | "xFrameOptions" : "SAMEORIGIN",
1235 | "contentSecurityPolicy" : "frame-src 'self'; frame-ancestors 'self'; object-src 'none';",
1236 | "xXSSProtection" : "1; mode=block",
1237 | "strictTransportSecurity" : "max-age=31536000; includeSubDomains"
1238 | },
1239 | "smtpServer" : { },
1240 | "eventsEnabled" : false,
1241 | "eventsListeners" : [ "jboss-logging", "acme-audit-listener" ],
1242 | "enabledEventTypes" : [ "SEND_RESET_PASSWORD", "UPDATE_CONSENT_ERROR", "GRANT_CONSENT", "REMOVE_TOTP", "REVOKE_GRANT", "UPDATE_TOTP", "LOGIN_ERROR", "CLIENT_LOGIN", "RESET_PASSWORD_ERROR", "IMPERSONATE_ERROR", "CODE_TO_TOKEN_ERROR", "CUSTOM_REQUIRED_ACTION", "RESTART_AUTHENTICATION", "IMPERSONATE", "UPDATE_PROFILE_ERROR", "LOGIN", "UPDATE_PASSWORD_ERROR", "CLIENT_INITIATED_ACCOUNT_LINKING", "TOKEN_EXCHANGE", "LOGOUT", "REGISTER", "DELETE_ACCOUNT_ERROR", "CLIENT_REGISTER", "IDENTITY_PROVIDER_LINK_ACCOUNT", "DELETE_ACCOUNT", "UPDATE_PASSWORD", "CLIENT_DELETE", "FEDERATED_IDENTITY_LINK_ERROR", "IDENTITY_PROVIDER_FIRST_LOGIN", "CLIENT_DELETE_ERROR", "VERIFY_EMAIL", "CLIENT_LOGIN_ERROR", "RESTART_AUTHENTICATION_ERROR", "EXECUTE_ACTIONS", "REMOVE_FEDERATED_IDENTITY_ERROR", "TOKEN_EXCHANGE_ERROR", "PERMISSION_TOKEN", "SEND_IDENTITY_PROVIDER_LINK_ERROR", "EXECUTE_ACTION_TOKEN_ERROR", "SEND_VERIFY_EMAIL", "EXECUTE_ACTIONS_ERROR", "REMOVE_FEDERATED_IDENTITY", "IDENTITY_PROVIDER_POST_LOGIN", "IDENTITY_PROVIDER_LINK_ACCOUNT_ERROR", "UPDATE_EMAIL", "REGISTER_ERROR", "REVOKE_GRANT_ERROR", "EXECUTE_ACTION_TOKEN", "LOGOUT_ERROR", "UPDATE_EMAIL_ERROR", "CLIENT_UPDATE_ERROR", "UPDATE_PROFILE", "CLIENT_REGISTER_ERROR", "FEDERATED_IDENTITY_LINK", "SEND_IDENTITY_PROVIDER_LINK", "SEND_VERIFY_EMAIL_ERROR", "RESET_PASSWORD", "CLIENT_INITIATED_ACCOUNT_LINKING_ERROR", "UPDATE_CONSENT", "REMOVE_TOTP_ERROR", "VERIFY_EMAIL_ERROR", "SEND_RESET_PASSWORD_ERROR", "CLIENT_UPDATE", "CUSTOM_REQUIRED_ACTION_ERROR", "IDENTITY_PROVIDER_POST_LOGIN_ERROR", "UPDATE_TOTP_ERROR", "CODE_TO_TOKEN", "GRANT_CONSENT_ERROR", "IDENTITY_PROVIDER_FIRST_LOGIN_ERROR" ],
1243 | "adminEventsEnabled" : false,
1244 | "adminEventsDetailsEnabled" : false,
1245 | "identityProviders" : [ ],
1246 | "identityProviderMappers" : [ ],
1247 | "components" : {
1248 | "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy" : [ {
1249 | "id" : "302dd88c-3106-4452-a3a2-3945c8c13fc3",
1250 | "name" : "Full Scope Disabled",
1251 | "providerId" : "scope",
1252 | "subType" : "anonymous",
1253 | "subComponents" : { },
1254 | "config" : { }
1255 | }, {
1256 | "id" : "0b4bb70e-3bf6-4a9f-b163-13e458f1cba6",
1257 | "name" : "Allowed Client Scopes",
1258 | "providerId" : "allowed-client-templates",
1259 | "subType" : "authenticated",
1260 | "subComponents" : { },
1261 | "config" : {
1262 | "allow-default-scopes" : [ "true" ]
1263 | }
1264 | }, {
1265 | "id" : "8c2743ed-554c-4acd-92d3-5720a24f4465",
1266 | "name" : "Allowed Protocol Mapper Types",
1267 | "providerId" : "allowed-protocol-mappers",
1268 | "subType" : "authenticated",
1269 | "subComponents" : { },
1270 | "config" : {
1271 | "allowed-protocol-mapper-types" : [ "oidc-usermodel-property-mapper", "saml-user-property-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-attribute-mapper", "oidc-address-mapper", "oidc-full-name-mapper", "saml-role-list-mapper", "saml-user-attribute-mapper" ]
1272 | }
1273 | }, {
1274 | "id" : "c1809668-78d3-470c-ac3f-ef415fb0c7fa",
1275 | "name" : "Max Clients Limit",
1276 | "providerId" : "max-clients",
1277 | "subType" : "anonymous",
1278 | "subComponents" : { },
1279 | "config" : {
1280 | "max-clients" : [ "200" ]
1281 | }
1282 | }, {
1283 | "id" : "d239c378-be85-48fa-9639-2f6811416d46",
1284 | "name" : "Allowed Protocol Mapper Types",
1285 | "providerId" : "allowed-protocol-mappers",
1286 | "subType" : "anonymous",
1287 | "subComponents" : { },
1288 | "config" : {
1289 | "allowed-protocol-mapper-types" : [ "oidc-address-mapper", "oidc-usermodel-attribute-mapper", "saml-user-property-mapper", "oidc-usermodel-property-mapper", "saml-user-attribute-mapper", "oidc-full-name-mapper", "saml-role-list-mapper", "oidc-sha256-pairwise-sub-mapper" ]
1290 | }
1291 | }, {
1292 | "id" : "a1778593-d5fa-4105-8130-f0258941cdfb",
1293 | "name" : "Trusted Hosts",
1294 | "providerId" : "trusted-hosts",
1295 | "subType" : "anonymous",
1296 | "subComponents" : { },
1297 | "config" : {
1298 | "host-sending-registration-request-must-match" : [ "true" ],
1299 | "client-uris-must-match" : [ "true" ]
1300 | }
1301 | }, {
1302 | "id" : "58a26f4f-a339-4d1f-bdef-4a83dab07362",
1303 | "name" : "Allowed Client Scopes",
1304 | "providerId" : "allowed-client-templates",
1305 | "subType" : "anonymous",
1306 | "subComponents" : { },
1307 | "config" : {
1308 | "allow-default-scopes" : [ "true" ]
1309 | }
1310 | }, {
1311 | "id" : "5cca0e64-c2bd-4c64-a11a-8ded0cc09300",
1312 | "name" : "Consent Required",
1313 | "providerId" : "consent-required",
1314 | "subType" : "anonymous",
1315 | "subComponents" : { },
1316 | "config" : { }
1317 | } ],
1318 | "org.keycloak.keys.KeyProvider" : [ {
1319 | "id" : "a1bbd6a6-7ba3-4eb7-a8ce-6b90aa3a433b",
1320 | "name" : "hmac-generated",
1321 | "providerId" : "hmac-generated",
1322 | "subComponents" : { },
1323 | "config" : {
1324 | "kid" : [ "32662424-89a4-4d1a-a48d-f30b21e903ff" ],
1325 | "secret" : [ "fci1EKUY6L2_eW1dmkLkAlA1TCVIemN2wohv973GsC1Oa1ydAkylTxVKY_45YN8Q2ZKKOOqvNRmQy-_R1B2x3w" ],
1326 | "priority" : [ "100" ],
1327 | "algorithm" : [ "HS256" ]
1328 | }
1329 | }, {
1330 | "id" : "8c44d893-0ea2-4db1-83c3-3d24d023e478",
1331 | "name" : "rsa-generated",
1332 | "providerId" : "rsa-generated",
1333 | "subComponents" : { },
1334 | "config" : {
1335 | "privateKey" : [ "MIIEowIBAAKCAQEAgPkPUBI4XGapny3yUkItGdllOQXGn+8NO2K3zfB4HPLLg2h8kRTD5jWDkxknVNHc4NSd1/At0cfGwcxuDPnBoXHCldV9w59HJwbDEVdk4u1dcg0VB+YPOt3elFRxMqSbpb2b+7qxKOl6dbOGLf5l6O0WCg8OJokJCmaOetXObaaiDVdclsp9GniMzVLB1vpjOyx3nOVS21RcYzI1nfLweleFXATL7QCy5/5BuFeVIcNLCLxf3wGI+amcFtKvWeHzWiXYusaJIOiJmWcD1coFBV5P0UNYKm1/8BKbYlvUugD+h2sqgznvm45mh1sVh69UISCOwo8aU+Siirxk4Gf8/QIDAQABAoIBAD4MFDLYYScK+OWsrByo25vI+6qgPbtpvTrptjWsT4zVvdT9apg9njVdX2xgOIzU3eeIQlvFn7WB3/wSRouViHMMEKoW6Ic5VHjRBv2Lxuxpd4BMDOcc5gzS+qbvrPnJOVxWSPmlCl/9Wz3O3Wm5LvwNO4IhVhRx7tiDGF6+B662Pjp5GGM8mrt40AzPdMsVeF3ZyT+gXHS+marIDRgKmheRhaBMJFYC7nfjaxAjRMbbidGLTizpKNooG5ZxXz7FGiRpVEBXZYyeC1UhA7YMcusTq6Kz2VNln7evePSIIHz99b4kJP8VrT8Yxn7+SVeN7hLG9prF/R8SOJHw8f520X0CgYEA35PLcwyUM2uZzZD3Vx2zUYjh5XgvZfjSTxlQg/VQJAXbYVOniK1X8NpVnyCW4e1AhQIWGpihQfgVe/auTjPTdoxrxE8RoDM8yYST4BBpgNqbcjdtLwUK4srceQlSaTO5C8pUz4i4cXpFE65XWHq3+myPR7cILb7K41qYCCoGe1sCgYEAk60eeHTwYyEGpnEdEgWXOrjC5KBK8SehAhzxQpVF+8QZj2omQYjByVzEnYeiADe3KcoqkGUU0x85KBYZPfc2WuJe9yLm4udTImg95XYc8iyK7sM1q5eR/YFTZEUinVxyGUXivyWlvl2ESnImeqihmF2wPgKZUNE5Bq3T7kq50IcCgYBIoHEBcX+e6IAwx7uhH/PFM6r16MG05UwkB7wg8YpT+VcXWZ5dhrm/cp1HsMVypKhFzLSzdQtFK7qG504d9zXlF55WSb0XBi3j5F5I9evfwKOoSZr9IC02GOHfq4iKxhOBYfuE4wvPSQGxb/vNsSecgLFWgX11prmvexlR5ZzvawKBgC/Kcbb32Serc3R/3LGNX6CgVGoaucYLVh7R8P3kQw60KrVv28uPj28z92knkLTTUxJSG645GCEu1Jd1d1vHWi7VXXhLMj8yL4ROCeHtdHanFZspT4AlgBhzNuKXQRl95mrpY/UKIPZXW02gXXWKhylBAJ1AyA8Qdo0Dyjcuta2XAoGBALJLiRMe/V2ubQNePuTAXXK7Ikj3Bqs9Qu7JRTaKVRiwbPDmPvq43FRK2sVtJPOq3BS2o+7n+8ZmE0Ywd/wA0d/qcL6pUD5SMfe9UsWMXsju/QAw4vQ4mw4WPkX8x8kI76t1CPM93zu0Ke/iwzr7CikJ1vRIgQowgzrTpYqsfumn" ],
1336 | "certificate" : [ "MIIClzCCAX8CBgF4ovFwVjANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARhY21lMB4XDTIxMDQwNTE2NDg1NVoXDTMxMDQwNTE2NTAzNVowDzENMAsGA1UEAwwEYWNtZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAID5D1ASOFxmqZ8t8lJCLRnZZTkFxp/vDTtit83weBzyy4NofJEUw+Y1g5MZJ1TR3ODUndfwLdHHxsHMbgz5waFxwpXVfcOfRycGwxFXZOLtXXINFQfmDzrd3pRUcTKkm6W9m/u6sSjpenWzhi3+ZejtFgoPDiaJCQpmjnrVzm2mog1XXJbKfRp4jM1Swdb6Yzssd5zlUttUXGMyNZ3y8HpXhVwEy+0Asuf+QbhXlSHDSwi8X98BiPmpnBbSr1nh81ol2LrGiSDoiZlnA9XKBQVeT9FDWCptf/ASm2Jb1LoA/odrKoM575uOZodbFYevVCEgjsKPGlPkooq8ZOBn/P0CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAN+VETiLp8km3WrfVCoXI8ZJ2eARxnoZF3aSfpHx0i+hxRCJzGFM81NKZZKVyqbGsJla/k5NEyJOUpo2iXmnSO2mnDz3g4ACKek2cKHjr3xu56Zw7SZaScKiKQwfc/zTPbfzNIXkClfpxO2/EZRvwiw6e6U03kv0byd+BDknY9SByXgxG2z38CVGl0bUgB9UNZchFwK0OFSL74S/+YEYchao35gThhDHu1Fq5AWpo0YlmOss83iSeFYanMUBH55UETj0dsF0a4sd3piuPqCUDTQNoVGUXnpKAWI9Jvof70aoH1lnTBta9qbNLyTsecgoHHJwZ9WKvF5bmJqBWjIm8MA==" ],
1337 | "priority" : [ "100" ]
1338 | }
1339 | }, {
1340 | "id" : "b173a97d-c779-444a-9d02-c13a1b7f1941",
1341 | "name" : "aes-generated",
1342 | "providerId" : "aes-generated",
1343 | "subComponents" : { },
1344 | "config" : {
1345 | "kid" : [ "fd1e9b64-973a-401c-bc55-1e67207d2827" ],
1346 | "secret" : [ "fpOb_Zeq0DzUEbms62tfVw" ],
1347 | "priority" : [ "100" ]
1348 | }
1349 | } ]
1350 | },
1351 | "internationalizationEnabled" : false,
1352 | "supportedLocales" : [ ],
1353 | "authenticationFlows" : [ {
1354 | "id" : "a422f934-8436-42bc-b5dc-029657790dbe",
1355 | "alias" : "Account verification options",
1356 | "description" : "Method with which to verity the existing account",
1357 | "providerId" : "basic-flow",
1358 | "topLevel" : false,
1359 | "builtIn" : true,
1360 | "authenticationExecutions" : [ {
1361 | "authenticator" : "idp-email-verification",
1362 | "requirement" : "ALTERNATIVE",
1363 | "priority" : 10,
1364 | "userSetupAllowed" : false,
1365 | "autheticatorFlow" : false
1366 | }, {
1367 | "requirement" : "ALTERNATIVE",
1368 | "priority" : 20,
1369 | "flowAlias" : "Verify Existing Account by Re-authentication",
1370 | "userSetupAllowed" : false,
1371 | "autheticatorFlow" : true
1372 | } ]
1373 | }, {
1374 | "id" : "c7618c56-0bf4-4c30-9827-937747f697f3",
1375 | "alias" : "Authentication Options",
1376 | "description" : "Authentication options.",
1377 | "providerId" : "basic-flow",
1378 | "topLevel" : false,
1379 | "builtIn" : true,
1380 | "authenticationExecutions" : [ {
1381 | "authenticator" : "basic-auth",
1382 | "requirement" : "REQUIRED",
1383 | "priority" : 10,
1384 | "userSetupAllowed" : false,
1385 | "autheticatorFlow" : false
1386 | }, {
1387 | "authenticator" : "basic-auth-otp",
1388 | "requirement" : "DISABLED",
1389 | "priority" : 20,
1390 | "userSetupAllowed" : false,
1391 | "autheticatorFlow" : false
1392 | }, {
1393 | "authenticator" : "auth-spnego",
1394 | "requirement" : "DISABLED",
1395 | "priority" : 30,
1396 | "userSetupAllowed" : false,
1397 | "autheticatorFlow" : false
1398 | } ]
1399 | }, {
1400 | "id" : "86c2b8f6-39c0-4500-ae31-9056be930884",
1401 | "alias" : "Browser - Conditional OTP",
1402 | "description" : "Flow to determine if the OTP is required for the authentication",
1403 | "providerId" : "basic-flow",
1404 | "topLevel" : false,
1405 | "builtIn" : true,
1406 | "authenticationExecutions" : [ {
1407 | "authenticator" : "conditional-user-configured",
1408 | "requirement" : "REQUIRED",
1409 | "priority" : 10,
1410 | "userSetupAllowed" : false,
1411 | "autheticatorFlow" : false
1412 | }, {
1413 | "authenticator" : "auth-otp-form",
1414 | "requirement" : "REQUIRED",
1415 | "priority" : 20,
1416 | "userSetupAllowed" : false,
1417 | "autheticatorFlow" : false
1418 | } ]
1419 | }, {
1420 | "id" : "a62322c3-9c76-412c-b154-2d91ec5b15dd",
1421 | "alias" : "Direct Grant - Conditional OTP",
1422 | "description" : "Flow to determine if the OTP is required for the authentication",
1423 | "providerId" : "basic-flow",
1424 | "topLevel" : false,
1425 | "builtIn" : true,
1426 | "authenticationExecutions" : [ {
1427 | "authenticator" : "conditional-user-configured",
1428 | "requirement" : "REQUIRED",
1429 | "priority" : 10,
1430 | "userSetupAllowed" : false,
1431 | "autheticatorFlow" : false
1432 | }, {
1433 | "authenticator" : "direct-grant-validate-otp",
1434 | "requirement" : "REQUIRED",
1435 | "priority" : 20,
1436 | "userSetupAllowed" : false,
1437 | "autheticatorFlow" : false
1438 | } ]
1439 | }, {
1440 | "id" : "ae76de99-88bc-4ed0-919d-c6659419f3c0",
1441 | "alias" : "First broker login - Conditional OTP",
1442 | "description" : "Flow to determine if the OTP is required for the authentication",
1443 | "providerId" : "basic-flow",
1444 | "topLevel" : false,
1445 | "builtIn" : true,
1446 | "authenticationExecutions" : [ {
1447 | "authenticator" : "conditional-user-configured",
1448 | "requirement" : "REQUIRED",
1449 | "priority" : 10,
1450 | "userSetupAllowed" : false,
1451 | "autheticatorFlow" : false
1452 | }, {
1453 | "authenticator" : "auth-otp-form",
1454 | "requirement" : "REQUIRED",
1455 | "priority" : 20,
1456 | "userSetupAllowed" : false,
1457 | "autheticatorFlow" : false
1458 | } ]
1459 | }, {
1460 | "id" : "0742a721-05fd-4b35-8e5e-5037897f19c9",
1461 | "alias" : "Handle Existing Account",
1462 | "description" : "Handle what to do if there is existing account with same email/username like authenticated identity provider",
1463 | "providerId" : "basic-flow",
1464 | "topLevel" : false,
1465 | "builtIn" : true,
1466 | "authenticationExecutions" : [ {
1467 | "authenticator" : "idp-confirm-link",
1468 | "requirement" : "REQUIRED",
1469 | "priority" : 10,
1470 | "userSetupAllowed" : false,
1471 | "autheticatorFlow" : false
1472 | }, {
1473 | "requirement" : "REQUIRED",
1474 | "priority" : 20,
1475 | "flowAlias" : "Account verification options",
1476 | "userSetupAllowed" : false,
1477 | "autheticatorFlow" : true
1478 | } ]
1479 | }, {
1480 | "id" : "d63317fe-7b59-4157-b868-c3eaf27b180a",
1481 | "alias" : "Reset - Conditional OTP",
1482 | "description" : "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.",
1483 | "providerId" : "basic-flow",
1484 | "topLevel" : false,
1485 | "builtIn" : true,
1486 | "authenticationExecutions" : [ {
1487 | "authenticator" : "conditional-user-configured",
1488 | "requirement" : "REQUIRED",
1489 | "priority" : 10,
1490 | "userSetupAllowed" : false,
1491 | "autheticatorFlow" : false
1492 | }, {
1493 | "authenticator" : "reset-otp",
1494 | "requirement" : "REQUIRED",
1495 | "priority" : 20,
1496 | "userSetupAllowed" : false,
1497 | "autheticatorFlow" : false
1498 | } ]
1499 | }, {
1500 | "id" : "f1fadbc9-92bb-4d15-8e90-41f7bcae30a8",
1501 | "alias" : "User creation or linking",
1502 | "description" : "Flow for the existing/non-existing user alternatives",
1503 | "providerId" : "basic-flow",
1504 | "topLevel" : false,
1505 | "builtIn" : true,
1506 | "authenticationExecutions" : [ {
1507 | "authenticatorConfig" : "create unique user config",
1508 | "authenticator" : "idp-create-user-if-unique",
1509 | "requirement" : "ALTERNATIVE",
1510 | "priority" : 10,
1511 | "userSetupAllowed" : false,
1512 | "autheticatorFlow" : false
1513 | }, {
1514 | "requirement" : "ALTERNATIVE",
1515 | "priority" : 20,
1516 | "flowAlias" : "Handle Existing Account",
1517 | "userSetupAllowed" : false,
1518 | "autheticatorFlow" : true
1519 | } ]
1520 | }, {
1521 | "id" : "b5c4686c-22c6-4beb-92bb-ab7c312ea1f0",
1522 | "alias" : "Verify Existing Account by Re-authentication",
1523 | "description" : "Reauthentication of existing account",
1524 | "providerId" : "basic-flow",
1525 | "topLevel" : false,
1526 | "builtIn" : true,
1527 | "authenticationExecutions" : [ {
1528 | "authenticator" : "idp-username-password-form",
1529 | "requirement" : "REQUIRED",
1530 | "priority" : 10,
1531 | "userSetupAllowed" : false,
1532 | "autheticatorFlow" : false
1533 | }, {
1534 | "requirement" : "CONDITIONAL",
1535 | "priority" : 20,
1536 | "flowAlias" : "First broker login - Conditional OTP",
1537 | "userSetupAllowed" : false,
1538 | "autheticatorFlow" : true
1539 | } ]
1540 | }, {
1541 | "id" : "d2701f9c-b177-4996-939d-83436dda5b02",
1542 | "alias" : "browser",
1543 | "description" : "browser based authentication",
1544 | "providerId" : "basic-flow",
1545 | "topLevel" : true,
1546 | "builtIn" : true,
1547 | "authenticationExecutions" : [ {
1548 | "authenticator" : "auth-cookie",
1549 | "requirement" : "ALTERNATIVE",
1550 | "priority" : 10,
1551 | "userSetupAllowed" : false,
1552 | "autheticatorFlow" : false
1553 | }, {
1554 | "authenticator" : "auth-spnego",
1555 | "requirement" : "DISABLED",
1556 | "priority" : 20,
1557 | "userSetupAllowed" : false,
1558 | "autheticatorFlow" : false
1559 | }, {
1560 | "authenticator" : "identity-provider-redirector",
1561 | "requirement" : "ALTERNATIVE",
1562 | "priority" : 25,
1563 | "userSetupAllowed" : false,
1564 | "autheticatorFlow" : false
1565 | }, {
1566 | "requirement" : "ALTERNATIVE",
1567 | "priority" : 30,
1568 | "flowAlias" : "forms",
1569 | "userSetupAllowed" : false,
1570 | "autheticatorFlow" : true
1571 | } ]
1572 | }, {
1573 | "id" : "e0bbebbf-cf5f-4c67-947c-3da17958297a",
1574 | "alias" : "clients",
1575 | "description" : "Base authentication for clients",
1576 | "providerId" : "client-flow",
1577 | "topLevel" : true,
1578 | "builtIn" : true,
1579 | "authenticationExecutions" : [ {
1580 | "authenticator" : "client-secret",
1581 | "requirement" : "ALTERNATIVE",
1582 | "priority" : 10,
1583 | "userSetupAllowed" : false,
1584 | "autheticatorFlow" : false
1585 | }, {
1586 | "authenticator" : "client-jwt",
1587 | "requirement" : "ALTERNATIVE",
1588 | "priority" : 20,
1589 | "userSetupAllowed" : false,
1590 | "autheticatorFlow" : false
1591 | }, {
1592 | "authenticator" : "client-secret-jwt",
1593 | "requirement" : "ALTERNATIVE",
1594 | "priority" : 30,
1595 | "userSetupAllowed" : false,
1596 | "autheticatorFlow" : false
1597 | }, {
1598 | "authenticator" : "client-x509",
1599 | "requirement" : "ALTERNATIVE",
1600 | "priority" : 40,
1601 | "userSetupAllowed" : false,
1602 | "autheticatorFlow" : false
1603 | } ]
1604 | }, {
1605 | "id" : "cb7810e3-f13c-48c6-bc7e-438df1b1cb9c",
1606 | "alias" : "direct grant",
1607 | "description" : "OpenID Connect Resource Owner Grant",
1608 | "providerId" : "basic-flow",
1609 | "topLevel" : true,
1610 | "builtIn" : true,
1611 | "authenticationExecutions" : [ {
1612 | "authenticator" : "direct-grant-validate-username",
1613 | "requirement" : "REQUIRED",
1614 | "priority" : 10,
1615 | "userSetupAllowed" : false,
1616 | "autheticatorFlow" : false
1617 | }, {
1618 | "authenticator" : "direct-grant-validate-password",
1619 | "requirement" : "REQUIRED",
1620 | "priority" : 20,
1621 | "userSetupAllowed" : false,
1622 | "autheticatorFlow" : false
1623 | }, {
1624 | "requirement" : "CONDITIONAL",
1625 | "priority" : 30,
1626 | "flowAlias" : "Direct Grant - Conditional OTP",
1627 | "userSetupAllowed" : false,
1628 | "autheticatorFlow" : true
1629 | } ]
1630 | }, {
1631 | "id" : "cc8f25de-10d6-4cdc-ba4d-95a6e5e25525",
1632 | "alias" : "docker auth",
1633 | "description" : "Used by Docker clients to authenticate against the IDP",
1634 | "providerId" : "basic-flow",
1635 | "topLevel" : true,
1636 | "builtIn" : true,
1637 | "authenticationExecutions" : [ {
1638 | "authenticator" : "docker-http-basic-authenticator",
1639 | "requirement" : "REQUIRED",
1640 | "priority" : 10,
1641 | "userSetupAllowed" : false,
1642 | "autheticatorFlow" : false
1643 | } ]
1644 | }, {
1645 | "id" : "0a0408f6-e4d8-4ff5-b99c-504b7a23cb13",
1646 | "alias" : "first broker login",
1647 | "description" : "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account",
1648 | "providerId" : "basic-flow",
1649 | "topLevel" : true,
1650 | "builtIn" : true,
1651 | "authenticationExecutions" : [ {
1652 | "authenticatorConfig" : "review profile config",
1653 | "authenticator" : "idp-review-profile",
1654 | "requirement" : "REQUIRED",
1655 | "priority" : 10,
1656 | "userSetupAllowed" : false,
1657 | "autheticatorFlow" : false
1658 | }, {
1659 | "requirement" : "REQUIRED",
1660 | "priority" : 20,
1661 | "flowAlias" : "User creation or linking",
1662 | "userSetupAllowed" : false,
1663 | "autheticatorFlow" : true
1664 | } ]
1665 | }, {
1666 | "id" : "40e56cec-70f2-412b-865a-826de0ef2f61",
1667 | "alias" : "forms",
1668 | "description" : "Username, password, otp and other auth forms.",
1669 | "providerId" : "basic-flow",
1670 | "topLevel" : false,
1671 | "builtIn" : true,
1672 | "authenticationExecutions" : [ {
1673 | "authenticator" : "auth-username-password-form",
1674 | "requirement" : "REQUIRED",
1675 | "priority" : 10,
1676 | "userSetupAllowed" : false,
1677 | "autheticatorFlow" : false
1678 | }, {
1679 | "requirement" : "CONDITIONAL",
1680 | "priority" : 20,
1681 | "flowAlias" : "Browser - Conditional OTP",
1682 | "userSetupAllowed" : false,
1683 | "autheticatorFlow" : true
1684 | } ]
1685 | }, {
1686 | "id" : "c416db40-dc84-4f58-8d52-4c9a6afe4867",
1687 | "alias" : "http challenge",
1688 | "description" : "An authentication flow based on challenge-response HTTP Authentication Schemes",
1689 | "providerId" : "basic-flow",
1690 | "topLevel" : true,
1691 | "builtIn" : true,
1692 | "authenticationExecutions" : [ {
1693 | "authenticator" : "no-cookie-redirect",
1694 | "requirement" : "REQUIRED",
1695 | "priority" : 10,
1696 | "userSetupAllowed" : false,
1697 | "autheticatorFlow" : false
1698 | }, {
1699 | "requirement" : "REQUIRED",
1700 | "priority" : 20,
1701 | "flowAlias" : "Authentication Options",
1702 | "userSetupAllowed" : false,
1703 | "autheticatorFlow" : true
1704 | } ]
1705 | }, {
1706 | "id" : "d2f561df-ebf6-47cf-8fe0-1ab85cabf2cd",
1707 | "alias" : "registration",
1708 | "description" : "registration flow",
1709 | "providerId" : "basic-flow",
1710 | "topLevel" : true,
1711 | "builtIn" : true,
1712 | "authenticationExecutions" : [ {
1713 | "authenticator" : "registration-page-form",
1714 | "requirement" : "REQUIRED",
1715 | "priority" : 10,
1716 | "flowAlias" : "registration form",
1717 | "userSetupAllowed" : false,
1718 | "autheticatorFlow" : true
1719 | } ]
1720 | }, {
1721 | "id" : "f667e507-0b01-44ee-878c-c478164f7c33",
1722 | "alias" : "registration form",
1723 | "description" : "registration form",
1724 | "providerId" : "form-flow",
1725 | "topLevel" : false,
1726 | "builtIn" : true,
1727 | "authenticationExecutions" : [ {
1728 | "authenticator" : "registration-user-creation",
1729 | "requirement" : "REQUIRED",
1730 | "priority" : 20,
1731 | "userSetupAllowed" : false,
1732 | "autheticatorFlow" : false
1733 | }, {
1734 | "authenticator" : "registration-profile-action",
1735 | "requirement" : "REQUIRED",
1736 | "priority" : 40,
1737 | "userSetupAllowed" : false,
1738 | "autheticatorFlow" : false
1739 | }, {
1740 | "authenticator" : "registration-password-action",
1741 | "requirement" : "REQUIRED",
1742 | "priority" : 50,
1743 | "userSetupAllowed" : false,
1744 | "autheticatorFlow" : false
1745 | }, {
1746 | "authenticator" : "registration-recaptcha-action",
1747 | "requirement" : "DISABLED",
1748 | "priority" : 60,
1749 | "userSetupAllowed" : false,
1750 | "autheticatorFlow" : false
1751 | } ]
1752 | }, {
1753 | "id" : "9b4a8f0b-8ace-47f3-89ef-c0c259b219bc",
1754 | "alias" : "reset credentials",
1755 | "description" : "Reset credentials for a user if they forgot their password or something",
1756 | "providerId" : "basic-flow",
1757 | "topLevel" : true,
1758 | "builtIn" : true,
1759 | "authenticationExecutions" : [ {
1760 | "authenticator" : "reset-credentials-choose-user",
1761 | "requirement" : "REQUIRED",
1762 | "priority" : 10,
1763 | "userSetupAllowed" : false,
1764 | "autheticatorFlow" : false
1765 | }, {
1766 | "authenticator" : "reset-credential-email",
1767 | "requirement" : "REQUIRED",
1768 | "priority" : 20,
1769 | "userSetupAllowed" : false,
1770 | "autheticatorFlow" : false
1771 | }, {
1772 | "authenticator" : "reset-password",
1773 | "requirement" : "REQUIRED",
1774 | "priority" : 30,
1775 | "userSetupAllowed" : false,
1776 | "autheticatorFlow" : false
1777 | }, {
1778 | "requirement" : "CONDITIONAL",
1779 | "priority" : 40,
1780 | "flowAlias" : "Reset - Conditional OTP",
1781 | "userSetupAllowed" : false,
1782 | "autheticatorFlow" : true
1783 | } ]
1784 | }, {
1785 | "id" : "186bed01-f1e4-48c2-a4bd-1b28e32b99be",
1786 | "alias" : "saml ecp",
1787 | "description" : "SAML ECP Profile Authentication Flow",
1788 | "providerId" : "basic-flow",
1789 | "topLevel" : true,
1790 | "builtIn" : true,
1791 | "authenticationExecutions" : [ {
1792 | "authenticator" : "http-basic-authenticator",
1793 | "requirement" : "REQUIRED",
1794 | "priority" : 10,
1795 | "userSetupAllowed" : false,
1796 | "autheticatorFlow" : false
1797 | } ]
1798 | } ],
1799 | "authenticatorConfig" : [ {
1800 | "id" : "2a43b55e-be98-4089-8d7b-6cee7770ffe0",
1801 | "alias" : "create unique user config",
1802 | "config" : {
1803 | "require.password.update.after.registration" : "false"
1804 | }
1805 | }, {
1806 | "id" : "2eba2ed1-7a21-4720-a562-9e08141217eb",
1807 | "alias" : "review profile config",
1808 | "config" : {
1809 | "update.profile.on.first.login" : "missing"
1810 | }
1811 | } ],
1812 | "requiredActions" : [ {
1813 | "alias" : "CONFIGURE_TOTP",
1814 | "name" : "Configure OTP",
1815 | "providerId" : "CONFIGURE_TOTP",
1816 | "enabled" : true,
1817 | "defaultAction" : false,
1818 | "priority" : 10,
1819 | "config" : { }
1820 | }, {
1821 | "alias" : "terms_and_conditions",
1822 | "name" : "Terms and Conditions",
1823 | "providerId" : "terms_and_conditions",
1824 | "enabled" : false,
1825 | "defaultAction" : false,
1826 | "priority" : 20,
1827 | "config" : { }
1828 | }, {
1829 | "alias" : "UPDATE_PASSWORD",
1830 | "name" : "Update Password",
1831 | "providerId" : "UPDATE_PASSWORD",
1832 | "enabled" : true,
1833 | "defaultAction" : false,
1834 | "priority" : 30,
1835 | "config" : { }
1836 | }, {
1837 | "alias" : "UPDATE_PROFILE",
1838 | "name" : "Update Profile",
1839 | "providerId" : "UPDATE_PROFILE",
1840 | "enabled" : true,
1841 | "defaultAction" : false,
1842 | "priority" : 40,
1843 | "config" : { }
1844 | }, {
1845 | "alias" : "VERIFY_EMAIL",
1846 | "name" : "Verify Email",
1847 | "providerId" : "VERIFY_EMAIL",
1848 | "enabled" : true,
1849 | "defaultAction" : false,
1850 | "priority" : 50,
1851 | "config" : { }
1852 | }, {
1853 | "alias" : "delete_account",
1854 | "name" : "Delete Account",
1855 | "providerId" : "delete_account",
1856 | "enabled" : false,
1857 | "defaultAction" : false,
1858 | "priority" : 60,
1859 | "config" : { }
1860 | }, {
1861 | "alias" : "update_user_locale",
1862 | "name" : "Update User Locale",
1863 | "providerId" : "update_user_locale",
1864 | "enabled" : true,
1865 | "defaultAction" : false,
1866 | "priority" : 1000,
1867 | "config" : { }
1868 | } ],
1869 | "browserFlow" : "browser",
1870 | "registrationFlow" : "registration",
1871 | "directGrantFlow" : "direct grant",
1872 | "resetCredentialsFlow" : "reset credentials",
1873 | "clientAuthenticationFlow" : "clients",
1874 | "dockerAuthenticationFlow" : "docker auth",
1875 | "attributes" : {
1876 | "clientOfflineSessionMaxLifespan" : "0",
1877 | "clientSessionIdleTimeout" : "0",
1878 | "clientSessionMaxLifespan" : "0",
1879 | "clientOfflineSessionIdleTimeout" : "0"
1880 | },
1881 | "keycloakVersion" : "12.0.4",
1882 | "userManagedAccessAllowed" : false
1883 | }
--------------------------------------------------------------------------------