Repository: jhipster/jhipster-control-center
Pull request #78 (2020-07-13 12:29:48)

add feature oauth2
Co-authored-by: Adil Abed aelabedelalaoui@acm.org


Patch:
diff --git a/pom.xml b/pom.xml
index b78776d..021e86f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -59,6 +59,7 @@
         <archunit-junit5.version>0.13.1</archunit-junit5.version>
         <mapstruct.version>1.3.1.Final</mapstruct.version>
         <springfox.version>3.0.0-SNAPSHOT</springfox.version>
+        <oauth2-client.version>2.3.1.RELEASE</oauth2-client.version>
         <!-- Plugin versions -->
         <maven-clean-plugin.version>3.1.0</maven-clean-plugin.version>
         <maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
@@ -322,6 +323,16 @@
             <groupId>org.springframework.cloud</groupId>
             <artifactId>spring-cloud-starter-gateway</artifactId>
         </dependency>
+        <!-- Spring Oauth2 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-oauth2-client</artifactId>
+            <version>${oauth2-client.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-oauth2-resource-server</artifactId>
+        </dependency>
 
         <!-- jhipster-needle-maven-add-dependency -->
     </dependencies>
@@ -781,6 +792,15 @@
         </pluginManagement>
     </build>
     <profiles>
+        <profile>
+            <id>oauth2</id>
+            <dependencies>
+                <dependency>
+                    <groupId>org.springframework.cloud</groupId>
+                    <artifactId>spring-cloud-starter-security</artifactId>
+                </dependency>
+            </dependencies>
+        </profile>
         <profile>
             <id>swagger</id>
             <properties>
diff --git a/src/main/docker/app.yml b/src/main/docker/app.yml
index 30468ae..cde12d1 100644
--- a/src/main/docker/app.yml
+++ b/src/main/docker/app.yml
@@ -1,3 +1,17 @@
+## How to set authentication mode
+### Authentication mode
+# JHCC support two kinds of authentication mode: JWT or OAuth2
+### Authentication properties
+# According to the authentication mode chosen as a Spring profile, you have to set the right properties
+# Please note that current properties are set to run JHCC with default values, personalize them if needed
+# and remove those from other modes. You can only have one mode active.
+# By default, JWT is used.
+# In order to use OAuth2 mode, please:
+# - add to SPRING_PROFILES_ACTIVE this profile: oauth2
+# - uncomment SPRING_SECURITY_OAUTH2_CLIENT properties
+# - uncomment keycloak part
+
+
 version: '2'
 services:
   jhipstercontrolcenter-app:
@@ -7,5 +21,12 @@ services:
       - SPRING_PROFILES_ACTIVE=prod,swagger
       - MANAGEMENT_METRICS_EXPORT_PROMETHEUS_ENABLED=true
       - JHIPSTER_SLEEP=30 # gives time for other services to boot before the application
+#      - SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_OIDC_ISSUER_URI=http://keycloak:9080/auth/realms/jhipster
+#      - SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_ID=web_app
+#      - SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_SECRET=web_app
     ports:
       - 7419:7419
+#  keycloak:
+#    extends:
+#      file: keycloak.yml
+#      service: keycloak
diff --git a/src/main/docker/keycloak.yml b/src/main/docker/keycloak.yml
new file mode 100644
index 0000000..c4ca5bc
--- /dev/null
+++ b/src/main/docker/keycloak.yml
@@ -0,0 +1,25 @@
+version: '2'
+services:
+  keycloak:
+    image: jboss/keycloak:10.0.0
+    command:
+      [
+        '-b',
+        '0.0.0.0',
+        '-Dkeycloak.migration.action=import',
+        '-Dkeycloak.migration.provider=dir',
+        '-Dkeycloak.migration.dir=/opt/jboss/keycloak/realm-config',
+        '-Dkeycloak.migration.strategy=OVERWRITE_EXISTING',
+        '-Djboss.socket.binding.port-offset=1000',
+        '-Dkeycloak.profile.feature.upload_scripts=enabled',
+      ]
+    volumes:
+      - ./realm-config:/opt/jboss/keycloak/realm-config
+    environment:
+      - KEYCLOAK_USER=admin
+      - KEYCLOAK_PASSWORD=admin
+      - DB_VENDOR=h2
+    ports:
+      - 9080:9080
+      - 9443:9443
+      - 10990:10990
diff --git a/src/main/docker/realm-config/jhipster-realm.json b/src/main/docker/realm-config/jhipster-realm.json
new file mode 100644
index 0000000..5694d1e
--- /dev/null
+++ b/src/main/docker/realm-config/jhipster-realm.json
@@ -0,0 +1,2123 @@
+{
+  "id": "jhipster",
+  "realm": "jhipster",
+  "notBefore": 0,
+  "revokeRefreshToken": false,
+  "refreshTokenMaxReuse": 0,
+  "accessTokenLifespan": 300,
+  "accessTokenLifespanForImplicitFlow": 900,
+  "ssoSessionIdleTimeout": 1800,
+  "ssoSessionMaxLifespan": 36000,
+  "ssoSessionIdleTimeoutRememberMe": 0,
+  "ssoSessionMaxLifespanRememberMe": 0,
+  "offlineSessionIdleTimeout": 2592000,
+  "offlineSessionMaxLifespanEnabled": false,
+  "offlineSessionMaxLifespan": 5184000,
+  "clientSessionIdleTimeout": 0,
+  "clientSessionMaxLifespan": 0,
+  "accessCodeLifespan": 60,
+  "accessCodeLifespanUserAction": 300,
+  "accessCodeLifespanLogin": 1800,
+  "actionTokenGeneratedByAdminLifespan": 43200,
+  "actionTokenGeneratedByUserLifespan": 300,
+  "enabled": true,
+  "sslRequired": "external",
+  "registrationAllowed": false,
+  "registrationEmailAsUsername": false,
+  "rememberMe": false,
+  "verifyEmail": false,
+  "loginWithEmailAllowed": true,
+  "duplicateEmailsAllowed": false,
+  "resetPasswordAllowed": false,
+  "editUsernameAllowed": false,
+  "bruteForceProtected": false,
+  "permanentLockout": false,
+  "maxFailureWaitSeconds": 900,
+  "minimumQuickLoginWaitSeconds": 60,
+  "waitIncrementSeconds": 60,
+  "quickLoginCheckMilliSeconds": 1000,
+  "maxDeltaTimeSeconds": 43200,
+  "failureFactor": 30,
+  "roles": {
+    "realm": [
+      {
+        "id": "a2350d85-938e-440c-847c-35086fe0f1d8",
+        "name": "ROLE_ADMIN",
+        "description": "Jhipster administrator role",
+        "composite": false,
+        "clientRole": false,
+        "containerId": "jhipster",
+        "attributes": {}
+      },
+      {
+        "id": "932ca70d-a311-42f9-9042-431cef835b9e",
+        "name": "offline_access",
+        "description": "${role_offline-access}",
+        "composite": false,
+        "clientRole": false,
+        "containerId": "jhipster",
+        "attributes": {}
+      },
+      {
+        "id": "8b1a0e78-24be-49ed-bc74-6471bd8d0f8f",
+        "name": "ROLE_USER",
+        "description": "Jhipster user role",
+        "composite": false,
+        "clientRole": false,
+        "containerId": "jhipster",
+        "attributes": {}
+      },
+      {
+        "id": "96c0124c-0f00-4769-8cd3-f7dfd74a0af3",
+        "name": "uma_authorization",
+        "description": "${role_uma_authorization}",
+        "composite": false,
+        "clientRole": false,
+        "containerId": "jhipster",
+        "attributes": {}
+      }
+    ],
+    "client": {
+      "internal": [
+        {
+          "id": "6ceaaca5-30a4-444b-9078-723b7cc13591",
+          "name": "uma_protection",
+          "composite": false,
+          "clientRole": true,
+          "containerId": "98ae8603-4547-4218-8fb9-ebc550a0e10b",
+          "attributes": {}
+        }
+      ],
+      "realm-management": [
+        {
+          "id": "2f58d746-0582-47fe-9b67-30809d5ad461",
+          "name": "query-users",
+          "description": "${role_query-users}",
+          "composite": false,
+          "clientRole": true,
+          "containerId": "48fb7988-d909-445a-81db-fe825dca0db2",
+          "attributes": {}
+        },
+        {
+          "id": "4873b8a8-8035-4b5e-bdd9-99ac74a4a43b",
+          "name": "query-clients",
+          "description": "${role_query-clients}",
+          "composite": false,
+          "clientRole": true,
+          "containerId": "48fb7988-d909-445a-81db-fe825dca0db2",
+          "attributes": {}
+        },
+        {
+          "id": "5aa18f9b-2fb1-4edb-bdde-fb79273eee38",
+          "name": "view-events",
+          "description": "${role_view-events}",
+          "composite": false,
+          "clientRole": true,
+          "containerId": "48fb7988-d909-445a-81db-fe825dca0db2",
+          "attributes": {}
+        },
+        {
+          "id": "84752518-6f2f-451f-ab59-de3ce6674ba1",
+          "name": "view-users",
+          "description": "${role_view-users}",
+          "composite": true,
+          "composites": {
+            "client": {
+              "realm-management": ["query-users", "query-groups"]
+            }
+          },
+          "clientRole": true,
+          "containerId": "48fb7988-d909-445a-81db-fe825dca0db2",
+          "attributes": {}
+        },
+        {
+          "id": "c9fb6965-e734-46b4-a174-fa9d1fb7e1cc",
+          "name": "manage-events",
+          "description": "${role_manage-events}",
+          "composite": false,
+          "clientRole": true,
+          "containerId": "48fb7988-d909-445a-81db-fe825dca0db2",
+          "attributes": {}
+        },
+        {
+          "id": "42abef51-b33b-4eb3-8515-3c6b16ca3849",
+          "name": "manage-identity-providers",
+          "description": "${role_manage-identity-providers}",
+          "composite": false,
+          "clientRole": true,
+          "containerId": "48fb7988-d909-445a-81db-fe825dca0db2",
+          "attributes": {}
+        },
+        {
+          "id": "a3f9ba91-6ed5-47ff-a1cc-384c00cc203d",
+          "name": "impersonation",
+          "description": "${role_impersonation}",
+          "composite": false,
+          "clientRole": true,
+          "containerId": "48fb7988-d909-445a-81db-fe825dca0db2",
+          "attributes": {}
+        },
+        {
+          "id": "24493c26-5ea3-4e53-b949-ca27a06ff098",
+          "name": "manage-users",
+          "description": "${role_manage-users}",
+          "composite": false,
+          "clientRole": true,
+          "containerId": "48fb7988-d909-445a-81db-fe825dca0db2",
+          "attributes": {}
+        },
+        {
+          "id": "24ba1589-1141-4836-91bb-ad1b10db4944",
+          "name": "create-client",
+          "description": "${role_create-client}",
+          "composite": false,
+          "clientRole": true,
+          "containerId": "48fb7988-d909-445a-81db-fe825dca0db2",
+          "attributes": {}
+        },
+        {
+          "id": "38cbec3e-9ca1-4292-a513-09e86fdcf6b1",
+          "name": "view-identity-providers",
+          "description": "${role_view-identity-providers}",
+          "composite": false,
+          "clientRole": true,
+          "containerId": "48fb7988-d909-445a-81db-fe825dca0db2",
+          "attributes": {}
+        },
+        {
+          "id": "2d8a0ec8-111b-41a2-bf49-7f1ae086c5b1",
+          "name": "manage-realm",
+          "description": "${role_manage-realm}",
+          "composite": false,
+          "clientRole": true,
+          "containerId": "48fb7988-d909-445a-81db-fe825dca0db2",
+          "attributes": {}
+        },
+        {
+          "id": "4ee94804-bc06-4d02-8541-410768b88a49",
+          "name": "manage-authorization",
+          "description": "${role_manage-authorization}",
+          "composite": false,
+          "clientRole": true,
+          "containerId": "48fb7988-d909-445a-81db-fe825dca0db2",
+          "attributes": {}
+        },
+        {
+          "id": "3774414f-db0d-4928-b080-f22599622e97",
+          "name": "manage-clients",
+          "description": "${role_manage-clients}",
+          "composite": false,
+          "clientRole": true,
+          "containerId": "48fb7988-d909-445a-81db-fe825dca0db2",
+          "attributes": {}
+        },
+        {
+          "id": "2a54d26e-7a55-4fc9-92cb-45e613084562",
+          "name": "view-realm",
+          "description": "${role_view-realm}",
+          "composite": false,
+          "clientRole": true,
+          "containerId": "48fb7988-d909-445a-81db-fe825dca0db2",
+          "attributes": {}
+        },
+        {
+          "id": "8fdff21b-f46c-407c-b566-ce4fcc4c51d4",
+          "name": "view-authorization",
+          "description": "${role_view-authorization}",
+          "composite": false,
+          "clientRole": true,
+          "containerId": "48fb7988-d909-445a-81db-fe825dca0db2",
+          "attributes": {}
+        },
+        {
+          "id": "083a848a-49cb-457f-9615-120be8c74d9e",
+          "name": "realm-admin",
+          "description": "${role_realm-admin}",
+          "composite": true,
+          "composites": {
+            "client": {
+              "realm-management": [
+                "query-users",
+                "query-clients",
+                "view-events",
+                "view-users",
+                "manage-events",
+                "manage-identity-providers",
+                "impersonation",
+                "manage-users",
+                "create-client",
+                "manage-realm",
+                "view-identity-providers",
+                "manage-authorization",
+                "manage-clients",
+                "view-realm",
+                "view-authorization",
+                "view-clients",
+                "query-realms",
+                "query-groups"
+              ]
+            }
+          },
+          "clientRole": true,
+          "containerId": "48fb7988-d909-445a-81db-fe825dca0db2",
+          "attributes": {}
+        },
+        {
+          "id": "11bc32e5-55a6-431d-b6c7-32bd9504d070",
+          "name": "view-clients",
+          "description": "${role_view-clients}",
+          "composite": true,
+          "composites": {
+            "client": {
+              "realm-management": ["query-clients"]
+            }
+          },
+          "clientRole": true,
+          "containerId": "48fb7988-d909-445a-81db-fe825dca0db2",
+          "attributes": {}
+        },
+        {
+          "id": "5ea73aac-d0cf-4303-87a4-c9d7f5852734",
+          "name": "query-realms",
+          "description": "${role_query-realms}",
+          "composite": false,
+          "clientRole": true,
+          "containerId": "48fb7988-d909-445a-81db-fe825dca0db2",
+          "attributes": {}
+        },
+        {
+          "id": "8018bd1e-0b43-4b0c-8e5a-fbf31f8ee6db",
+          "name": "query-groups",
+          "description": "${role_query-groups}",
+          "composite": false,
+          "clientRole": true,
+          "containerId": "48fb7988-d909-445a-81db-fe825dca0db2",
+          "attributes": {}
+        }
+      ],
+      "security-admin-console": [],
+      "web_app": [],
+      "admin-cli": [],
+      "account-console": [],
+      "broker": [
+        {
+          "id": "225066d4-4a07-4550-a851-3d9359cbbac2",
+          "name": "read-token",
+          "description": "${role_read-token}",
+          "composite": false,
+          "clientRole": true,
+          "containerId": "274afcdb-7742-4a3a-acc5-17db61a4d200",
+          "attributes": {}
+        }
+      ],
+      "account": [
+        {
+          "id": "42dcf4f5-8f73-4907-bd90-669cccd66c23",
+          "name": "manage-consent",
+          "description": "${role_manage-consent}",
+          "composite": true,
+          "composites": {
+            "client": {
+              "account": ["view-consent"]
+            }
+          },
+          "clientRole": true,
+          "containerId": "e07da50b-cddc-4524-b630-436a5a6ba8ab",
+          "attributes": {}
+        },
+        {
+          "id": "354b3a09-47a6-4051-957c-c6a3ee28a190",
+          "name": "view-profile",
+          "description": "${role_view-profile}",
+          "composite": false,
+          "clientRole": true,
+          "containerId": "e07da50b-cddc-4524-b630-436a5a6ba8ab",
+          "attributes": {}
+        },
+        {
+          "id": "8d650dc2-d0e7-4ad7-8193-3ce1b7361b02",
+          "name": "manage-account-links",
+          "description": "${role_manage-account-links}",
+          "composite": false,
+          "clientRole": true,
+          "containerId": "e07da50b-cddc-4524-b630-436a5a6ba8ab",
+          "attributes": {}
+        },
+        {
+          "id": "21b65e52-b4e3-453e-b0f0-10a44a4a887d",
+          "name": "manage-account",
+          "description": "${role_manage-account}",
+          "composite": true,
+          "composites": {
+            "client": {
+              "account": ["manage-account-links"]
+            }
+          },
+          "clientRole": true,
+          "containerId": "e07da50b-cddc-4524-b630-436a5a6ba8ab",
+          "attributes": {}
+        },
+        {
+          "id": "5328d093-e39c-4a2d-b664-01ed54505337",
+          "name": "view-applications",
+          "description": "${role_view-applications}",
+          "composite": false,
+          "clientRole": true,
+          "containerId": "e07da50b-cddc-4524-b630-436a5a6ba8ab",
+          "attributes": {}
+        },
+        {
+          "id": "91f398b2-23a0-4cd3-8bc0-c3097df0e1e5",
+          "name": "view-consent",
+          "description": "${role_view-consent}",
+          "composite": false,
+          "clientRole": true,
+          "containerId": "e07da50b-cddc-4524-b630-436a5a6ba8ab",
+          "attributes": {}
+        }
+      ]
+    }
+  },
+  "groups": [
+    {
+      "id": "c4255caa-6cf7-40fb-8d5a-1b8799a438b8",
+      "name": "Admins",
+      "path": "/Admins",
+      "attributes": {},
+      "realmRoles": ["ROLE_ADMIN"],
+      "clientRoles": {},
+      "subGroups": []
+    },
+    {
+      "id": "ab0947d8-3b91-42a0-81e7-953a3c207316",
+      "name": "Users",
+      "path": "/Users",
+      "attributes": {},
+      "realmRoles": ["ROLE_USER"],
+      "clientRoles": {},
+      "subGroups": []
+    }
+  ],
+  "defaultRoles": ["offline_access", "uma_authorization"],
+  "requiredCredentials": ["password"],
+  "otpPolicyType": "totp",
+  "otpPolicyAlgorithm": "HmacSHA1",
+  "otpPolicyInitialCounter": 0,
+  "otpPolicyDigits": 6,
+  "otpPolicyLookAheadWindow": 1,
+  "otpPolicyPeriod": 30,
+  "otpSupportedApplications": ["FreeOTP", "Google Authenticator"],
+  "webAuthnPolicyRpEntityName": "keycloak",
+  "webAuthnPolicySignatureAlgorithms": ["ES256"],
+  "webAuthnPolicyRpId": "",
+  "webAuthnPolicyAttestationConveyancePreference": "not specified",
+  "webAuthnPolicyAuthenticatorAttachment": "not specified",
+  "webAuthnPolicyRequireResidentKey": "not specified",
+  "webAuthnPolicyUserVerificationRequirement": "not specified",
+  "webAuthnPolicyCreateTimeout": 0,
+  "webAuthnPolicyAvoidSameAuthenticatorRegister": false,
+  "webAuthnPolicyAcceptableAaguids": [],
+  "webAuthnPolicyPasswordlessRpEntityName": "keycloak",
+  "webAuthnPolicyPasswordlessSignatureAlgorithms": ["ES256"],
+  "webAuthnPolicyPasswordlessRpId": "",
+  "webAuthnPolicyPasswordlessAttestationConveyancePreference": "not specified",
+  "webAuthnPolicyPasswordlessAuthenticatorAttachment": "not specified",
+  "webAuthnPolicyPasswordlessRequireResidentKey": "not specified",
+  "webAuthnPolicyPasswordlessUserVerificationRequirement": "not specified",
+  "webAuthnPolicyPasswordlessCreateTimeout": 0,
+  "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false,
+  "webAuthnPolicyPasswordlessAcceptableAaguids": [],
+  "users": [
+    {
+      "id": "7e26ba47-6ff0-42ed-a425-ecaf01d121af",
+      "createdTimestamp": 1588436272389,
+      "username": "service-account-internal",
+      "enabled": true,
+      "totp": false,
+      "emailVerified": false,
+      "serviceAccountClientId": "internal",
+      "disableableCredentialTypes": [],
+      "requiredActions": [],
+      "realmRoles": ["offline_access", "uma_authorization"],
+      "clientRoles": {
+        "internal": ["uma_protection"],
+        "account": ["view-profile", "manage-account"]
+      },
+      "notBefore": 0,
+      "groups": []
+    }
+  ],
+  "scopeMappings": [
+    {
+      "clientScope": "offline_access",
+      "roles": ["offline_access"]
+    }
+  ],
+  "clientScopeMappings": {
+    "account": [
+      {
+        "client": "account-console",
+        "roles": ["manage-account"]
+      }
+    ]
+  },
+  "clients": [
+    {
+      "id": "e07da50b-cddc-4524-b630-436a5a6ba8ab",
+      "clientId": "account",
+      "name": "${client_account}",
+      "rootUrl": "${authBaseUrl}",
+      "baseUrl": "/realms/jhipster/account/",
+      "surrogateAuthRequired": false,
+      "enabled": true,
+      "alwaysDisplayInConsole": false,
+      "clientAuthenticatorType": "client-secret",
+      "secret": "**********",
+      "defaultRoles": ["manage-account", "view-profile"],
+      "redirectUris": ["/realms/jhipster/account/*"],
+      "webOrigins": [],
+      "notBefore": 0,
+      "bearerOnly": false,
+      "consentRequired": false,
+      "standardFlowEnabled": true,
+      "implicitFlowEnabled": false,
+      "directAccessGrantsEnabled": false,
+      "serviceAccountsEnabled": false,
+      "publicClient": false,
+      "frontchannelLogout": false,
+      "protocol": "openid-connect",
+      "attributes": {},
+      "authenticationFlowBindingOverrides": {},
+      "fullScopeAllowed": false,
+      "nodeReRegistrationTimeout": 0,
+      "defaultClientScopes": ["web-origins", "role_list", "roles", "profile", "email"],
+      "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"]
+    },
+    {
+      "id": "7b6ab455-242e-42aa-96d8-9c9e2b74da9d",
+      "clientId": "account-console",
+      "name": "${client_account-console}",
+      "rootUrl": "${authBaseUrl}",
+      "baseUrl": "/realms/jhipster/account/",
+      "surrogateAuthRequired": false,
+      "enabled": true,
+      "alwaysDisplayInConsole": false,
+      "clientAuthenticatorType": "client-secret",
+      "secret": "**********",
+      "redirectUris": ["/realms/jhipster/account/*"],
+      "webOrigins": [],
+      "notBefore": 0,
+      "bearerOnly": false,
+      "consentRequired": false,
+      "standardFlowEnabled": true,
+      "implicitFlowEnabled": false,
+      "directAccessGrantsEnabled": false,
+      "serviceAccountsEnabled": false,
+      "publicClient": true,
+      "frontchannelLogout": false,
+      "protocol": "openid-connect",
+      "attributes": {
+        "pkce.code.challenge.method": "S256"
+      },
+      "authenticationFlowBindingOverrides": {},
+      "fullScopeAllowed": false,
+      "nodeReRegistrationTimeout": 0,
+      "protocolMappers": [
+        {
+          "id": "013df1b5-9a5b-44f2-a0b6-5bf6d448556b",
+          "name": "audience resolve",
+          "protocol": "openid-connect",
+          "protocolMapper": "oidc-audience-resolve-mapper",
+          "consentRequired": false,
+          "config": {}
+        }
+      ],
+      "defaultClientScopes": ["web-origins", "role_list", "roles", "profile", "email"],
+      "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"]
+    },
+    {
+      "id": "5a9610bf-6a47-46a1-9442-9e43eacd98e8",
+      "clientId": "admin-cli",
+      "name": "${client_admin-cli}",
+      "surrogateAuthRequired": false,
+      "enabled": true,
+      "alwaysDisplayInConsole": false,
+      "clientAuthenticatorType": "client-secret",
+      "secret": "**********",
+      "redirectUris": [],
+      "webOrigins": [],
+      "notBefore": 0,
+      "bearerOnly": false,
+      "consentRequired": false,
+      "standardFlowEnabled": false,
+      "implicitFlowEnabled": false,
+      "directAccessGrantsEnabled": true,
+      "serviceAccountsEnabled": false,
+      "publicClient": true,
+      "frontchannelLogout": false,
+      "protocol": "openid-connect",
+      "attributes": {},
+      "authenticationFlowBindingOverrides": {},
+      "fullScopeAllowed": false,
+      "nodeReRegistrationTimeout": 0,
+      "defaultClientScopes": ["web-origins", "role_list", "roles", "profile", "email"],
+      "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"]
+    },
+    {
+      "id": "274afcdb-7742-4a3a-acc5-17db61a4d200",
+      "clientId": "broker",
+      "name": "${client_broker}",
+      "surrogateAuthRequired": false,
+      "enabled": true,
+      "alwaysDisplayInConsole": false,
+      "clientAuthenticatorType": "client-secret",
+      "secret": "**********",
+      "redirectUris": [],
+      "webOrigins": [],
+      "notBefore": 0,
+      "bearerOnly": false,
+      "consentRequired": false,
+      "standardFlowEnabled": true,
+      "implicitFlowEnabled": false,
+      "directAccessGrantsEnabled": false,
+      "serviceAccountsEnabled": false,
+      "publicClient": false,
+      "frontchannelLogout": false,
+      "protocol": "openid-connect",
+      "attributes": {},
+      "authenticationFlowBindingOverrides": {},
+      "fullScopeAllowed": false,
+      "nodeReRegistrationTimeout": 0,
+      "defaultClientScopes": ["web-origins", "role_list", "roles", "profile", "email"],
+      "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"]
+    },
+    {
+      "id": "98ae8603-4547-4218-8fb9-ebc550a0e10b",
+      "clientId": "internal",
+      "surrogateAuthRequired": false,
+      "enabled": true,
+      "alwaysDisplayInConsole": false,
+      "clientAuthenticatorType": "client-secret",
+      "secret": "internal",
+      "redirectUris": [],
+      "webOrigins": [],
+      "notBefore": 0,
+      "bearerOnly": false,
+      "consentRequired": false,
+      "standardFlowEnabled": false,
+      "implicitFlowEnabled": false,
+      "directAccessGrantsEnabled": false,
+      "serviceAccountsEnabled": true,
+      "authorizationServicesEnabled": true,
+      "publicClient": false,
+      "frontchannelLogout": false,
+      "protocol": "openid-connect",
+      "attributes": {
+        "saml.assertion.signature": "false",
+        "saml.force.post.binding": "false",
+        "saml.multivalued.roles": "false",
+        "saml.encrypt": "false",
+        "saml.server.signature": "false",
+        "saml.server.signature.keyinfo.ext": "false",
+        "exclude.session.state.from.auth.response": "false",
+        "saml_force_name_id_format": "false",
+        "saml.client.signature": "false",
+        "tls.client.certificate.bound.access.tokens": "false",
+        "saml.authnstatement": "false",
+        "display.on.consent.screen": "false",
+        "saml.onetimeuse.condition": "false"
+      },
+      "authenticationFlowBindingOverrides": {},
+      "fullScopeAllowed": true,
+      "nodeReRegistrationTimeout": -1,
+      "protocolMappers": [
+        {
+          "id": "46513189-074c-4ba5-bfa7-29bae63faaaf",
+          "name": "Client IP Address",
+          "protocol": "openid-connect",
+          "protocolMapper": "oidc-usersessionmodel-note-mapper",
+          "consentRequired": false,
+          "config": {
+            "user.session.note": "clientAddress",
+            "userinfo.token.claim": "true",
+            "id.token.claim": "true",
+            "access.token.claim": "true",
+            "claim.name": "clientAddress",
+            "jsonType.label": "String"
+          }
+        },
+        {
+          "id": "974887a2-e14a-4afd-84fc-a848009079ae",
+          "name": "Client ID",
+          "protocol": "openid-connect",
+          "protocolMapper": "oidc-usersessionmodel-note-mapper",
+          "consentRequired": false,
+          "config": {
+            "user.session.note": "clientId",
+            "userinfo.token.claim": "true",
+            "id.token.claim": "true",
+            "access.token.claim": "true",
+            "claim.name": "clientId",
+            "jsonType.label": "String"
+          }
+        },
+        {
+          "id": "1b1a1f44-66df-4670-badb-46a3d361ec7c",
+          "name": "Client Host",
+          "protocol": "openid-connect",
+          "protocolMapper": "oidc-usersessionmodel-note-mapper",
+          "consentRequired": false,
+          "config": {
+            "user.session.note": "clientHost",
+            "userinfo.token.claim": "true",
+            "id.token.claim": "true",
+            "access.token.claim": "true",
+            "claim.name": "clientHost",
+            "jsonType.label": "String"
+          }
+        }
+      ],
+      "defaultClientScopes": ["web-origins", "role_list", "roles", "profile", "email"],
+      "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"],
+      "authorizationSettings": {
+        "allowRemoteResourceManagement": false,
+        "policyEnforcementMode": "ENFORCING",
+        "resources": [
+          {
+            "name": "Default Resource",
+            "type": "urn:internal:resources:default",
+            "ownerManagedAccess": false,
+            "attributes": {},
+            "_id": "9e347a4d-ab6b-4075-9d24-23d20dfd30bc",
+            "uris": ["/*"]
+          }
+        ],
+        "policies": [
+          {
+            "id": "ff09b194-0615-4cfd-9851-74adf540dabd",
+            "name": "Default Policy",
+            "description": "A policy that grants access only for users within this realm",
+            "type": "js",
+            "logic": "POSITIVE",
+            "decisionStrategy": "AFFIRMATIVE",
+            "config": {
+              "code": "// by default, grants any permission associated with this policy\n$evaluation.grant();\n"
+            }
+          },
+          {
+            "id": "4b5c72cc-b8b8-4bfc-95ac-61815669f985",
+            "name": "Default Permission",
+            "description": "A permission that applies to the default resource type",
+            "type": "resource",
+            "logic": "POSITIVE",
+            "decisionStrategy": "UNANIMOUS",
+            "config": {
+              "defaultResourceType": "urn:internal:resources:default",
+              "applyPolicies": "[\"Default Policy\"]"
+            }
+          }
+        ],
+        "scopes": [],
+        "decisionStrategy": "UNANIMOUS"
+      }
+    },
+    {
+      "id": "48fb7988-d909-445a-81db-fe825dca0db2",
+      "clientId": "realm-management",
+      "name": "${client_realm-management}",
+      "surrogateAuthRequired": false,
+      "enabled": true,
+      "alwaysDisplayInConsole": false,
+      "clientAuthenticatorType": "client-secret",
+      "secret": "**********",
+      "redirectUris": [],
+      "webOrigins": [],
+      "notBefore": 0,
+      "bearerOnly": true,
+      "consentRequired": false,
+      "standardFlowEnabled": true,
+      "implicitFlowEnabled": false,
+      "directAccessGrantsEnabled": false,
+      "serviceAccountsEnabled": false,
+      "publicClient": false,
+      "frontchannelLogout": false,
+      "protocol": "openid-connect",
+      "attributes": {},
+      "authenticationFlowBindingOverrides": {},
+      "fullScopeAllowed": false,
+      "nodeReRegistrationTimeout": 0,
+      "defaultClientScopes": ["role_list", "profile", "email"],
+      "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"]
+    },
+    {
+      "id": "0aa176c1-8d28-4a8b-8e97-7e4b49a289ff",
+      "clientId": "security-admin-console",
+      "name": "${client_security-admin-console}",
+      "rootUrl": "${authAdminUrl}",
+      "baseUrl": "/admin/jhipster/console/",
+      "surrogateAuthRequired": false,
+      "enabled": true,
+      "alwaysDisplayInConsole": false,
+      "clientAuthenticatorType": "client-secret",
+      "secret": "**********",
+      "redirectUris": ["/admin/jhipster/console/*"],
+      "webOrigins": ["+"],
+      "notBefore": 0,
+      "bearerOnly": false,
+      "consentRequired": false,
+      "standardFlowEnabled": true,
+      "implicitFlowEnabled": false,
+      "directAccessGrantsEnabled": false,
+      "serviceAccountsEnabled": false,
+      "publicClient": true,
+      "frontchannelLogout": false,
+      "protocol": "openid-connect",
+      "attributes": {
+        "pkce.code.challenge.method": "S256"
+      },
+      "authenticationFlowBindingOverrides": {},
+      "fullScopeAllowed": false,
+      "nodeReRegistrationTimeout": 0,
+      "protocolMappers": [
+        {
+          "id": "abfaa4cd-4bc1-4223-be5d-c56c641369f2",
+          "name": "locale",
+          "protocol": "openid-connect",
+          "protocolMapper": "oidc-usermodel-attribute-mapper",
+          "consentRequired": false,
+          "config": {
+            "userinfo.token.claim": "true",
+            "user.attribute": "locale",
+            "id.token.claim": "true",
+            "access.token.claim": "true",
+            "claim.name": "locale",
+            "jsonType.label": "String"
+          }
+        }
+      ],
+      "defaultClientScopes": ["web-origins", "role_list", "roles", "profile", "email"],
+      "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"]
+    },
+    {
+      "id": "1eabef67-6473-4ba8-b07c-14bdbae4aaed",
+      "clientId": "web_app",
+      "rootUrl": "http://localhost:7419",
+      "adminUrl": "http://localhost:7419",
+      "surrogateAuthRequired": false,
+      "enabled": true,
+      "alwaysDisplayInConsole": false,
+      "clientAuthenticatorType": "client-secret",
+      "secret": "web_app",
+      "redirectUris": ["http://localhost:*", "https://localhost:*", "http://127.0.0.1:*", "https://127.0.0.1:*", "dev.localhost.ionic:*"],
+      "webOrigins": ["*"],
+      "notBefore": 0,
+      "bearerOnly": false,
+      "consentRequired": false,
+      "standardFlowEnabled": true,
+      "implicitFlowEnabled": true,
+      "directAccessGrantsEnabled": false,
+      "serviceAccountsEnabled": false,
+      "publicClient": true,
+      "frontchannelLogout": false,
+      "protocol": "openid-connect",
+      "attributes": {
+        "saml.assertion.signature": "false",
+        "saml.force.post.binding": "false",
+        "saml.multivalued.roles": "false",
+        "saml.encrypt": "false",
+        "saml.server.signature": "false",
+        "saml.server.signature.keyinfo.ext": "false",
+        "exclude.session.state.from.auth.response": "false",
+        "saml_force_name_id_format": "false",
+        "saml.client.signature": "false",
+        "tls.client.certificate.bound.access.tokens": "false",
+        "saml.authnstatement": "false",
+        "display.on.consent.screen": "false",
+        "saml.onetimeuse.condition": "false"
+      },
+      "authenticationFlowBindingOverrides": {},
+      "fullScopeAllowed": true,
+      "nodeReRegistrationTimeout": -1,
+      "defaultClientScopes": ["web-origins", "jhipster", "role_list", "roles", "profile", "email"],
+      "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"]
+    }
+  ],
+  "clientScopes": [
+    {
+      "id": "1dc1e050-891a-4f5b-ac9d-5ea0c2e3c05e",
+      "name": "address",
+      "description": "OpenID Connect built-in scope: address",
+      "protocol": "openid-connect",
+      "attributes": {
+        "consent.screen.text": "${addressScopeConsentText}",
+        "display.on.consent.screen": "true"
+      },
+      "protocolMappers": [
+        {
+          "id": "b9a92105-8ca5-45d1-8a99-626255ac174f",
+          "name": "address",
+          "protocol": "openid-connect",
+          "protocolMapper": "oidc-address-mapper",
+          "consentRequired": false,
+          "config": {
+            "user.attribute.formatted": "formatted",
+            "user.attribute.country": "country",
+            "user.attribute.postal_code": "postal_code",
+            "userinfo.token.claim": "true",
+            "user.attribute.street": "street",
+            "id.token.claim": "true",
+            "user.attribute.region": "region",
+            "access.token.claim": "true",
+            "user.attribute.locality": "locality"
+          }
+        }
+      ]
+    },
+    {
+      "id": "39e1693b-a924-4fbb-b98c-520869771f83",
+      "name": "email",
+      "description": "OpenID Connect built-in scope: email",
+      "protocol": "openid-connect",
+      "attributes": {
+        "consent.screen.text": "${emailScopeConsentText}",
+        "display.on.consent.screen": "true"
+      },
+      "protocolMappers": [
+        {
+          "id": "76f898bc-70e4-4e0a-a259-ae4cb58260f9",
+          "name": "email verified",
+          "protocol": "openid-connect",
+          "protocolMapper": "oidc-usermodel-property-mapper",
+          "consentRequired": false,
+          "config": {
+            "userinfo.token.claim": "true",
+            "user.attribute": "emailVerified",
+            "id.token.claim": "true",
+            "access.token.claim": "true",
+            "claim.name": "email_verified",
+            "jsonType.label": "boolean"
+          }
+        },
+        {
+          "id": "e348c7f8-e835-4539-a80b-b588510a82a9",
+          "name": "email",
+          "protocol": "openid-connect",
+          "protocolMapper": "oidc-usermodel-property-mapper",
+          "consentRequired": false,
+          "config": {
+            "userinfo.token.claim": "true",
+            "user.attribute": "email",
+            "id.token.claim": "true",
+            "access.token.claim": "true",
+            "claim.name": "email",
+            "jsonType.label": "String"
+          }
+        }
+      ]
+    },
+    {
+      "id": "3880d5d9-fced-4446-97fe-0434f2bb76ea",
+      "name": "jhipster",
+      "description": "Jhipster specific claims",
+      "protocol": "openid-connect",
+      "attributes": {
+        "display.on.consent.screen": "false"
+      },
+      "protocolMappers": [
+        {
+          "id": "e7c536ca-1711-4ed5-9f21-20a25435f475",
+          "name": "login",
+          "protocol": "openid-connect",
+          "protocolMapper": "oidc-usermodel-attribute-mapper",
+          "consentRequired": false,
+          "config": {
+            "user.attribute": "preferred_username",
+            "claim.name": "login",
+            "jsonType.label": "String",
+            "userinfo.token.claim": "true"
+          }
+        },
+        {
+          "id": "646e3a98-5f0c-4192-91f0-9719c295b278",
+          "name": "langKey",
+          "protocol": "openid-connect",
+          "protocolMapper": "oidc-usermodel-attribute-mapper",
+          "consentRequired": false,
+          "config": {
+            "user.attribute": "langKey",
+            "claim.name": "langKey",
+            "jsonType.label": "String",
+            "userinfo.token.claim": "true"
+          }
+        },
+        {
+          "id": "70b3e85f-e7b4-4c30-9d10-e0b589776951",
+          "name": "roles",
+          "protocol": "openid-connect",
+          "protocolMapper": "oidc-usermodel-realm-role-mapper",
+          "consentRequired": false,
+          "config": {
+            "access.token.claim": "true",
+            "claim.name": "roles",
+            "jsonType.label": "String",
+            "multivalued": "true",
+            "userinfo.token.claim": "true"
+          }
+        }
+      ]
+    },
+    {
+      "id": "a72d436e-111b-445b-804a-967d249f6455",
+      "name": "microprofile-jwt",
+      "description": "Microprofile - JWT built-in scope",
+      "protocol": "openid-connect",
+      "attributes": {
+        "include.in.token.scope": "true",
+        "display.on.consent.screen": "false"
+      },
+      "protocolMappers": [
+        {
+          "id": "dec0c468-3c11-429b-844e-dfd632b20ba7",
+          "name": "upn",
+          "protocol": "openid-connect",
+          "protocolMapper": "oidc-usermodel-property-mapper",
+          "consentRequired": false,
+          "config": {
+            "userinfo.token.claim": "true",
+            "user.attribute": "username",
+            "id.token.claim": "true",
+            "access.token.claim": "true",
+            "claim.name": "upn",
+            "jsonType.label": "String"
+          }
+        },
+        {
+          "id": "1ac09ef6-ab67-4379-b15a-5e54e50783be",
+          "name": "groups",
+          "protocol": "openid-connect",
+          "protocolMapper": "oidc-usermodel-realm-role-mapper",
+          "consentRequired": false,
+          "config": {
+            "multivalued": "true",
+            "userinfo.token.claim": "true",
+            "user.attribute": "foo",
+            "id.token.claim": "true",
+            "access.token.claim": "true",
+            "claim.name": "groups",
+            "jsonType.label": "String"
+          }
+        }
+      ]
+    },
+    {
+      "id": "5253d2be-3116-4510-ac05-99619ce2494c",
+      "name": "offline_access",
+      "description": "OpenID Connect built-in scope: offline_access",
+      "protocol": "openid-connect",
+      "attributes": {
+        "consent.screen.text": "${offlineAccessScopeConsentText}",
+        "display.on.consent.screen": "true"
+      }
+    },
+    {
+      "id": "0d3b55db-e68b-4c83-91d4-7370a6810a24",
+      "name": "phone",
+      "description": "OpenID Connect built-in scope: phone",
+      "protocol": "openid-connect",
+      "attributes": {
+        "consent.screen.text": "${phoneScopeConsentText}",
+        "display.on.consent.screen": "true"
+      },
+      "protocolMappers": [
+        {
+          "id": "bbc582f4-4749-42b8-9c65-71f4edfd3979",
+          "name": "phone number",
+          "protocol": "openid-connect",
+          "protocolMapper": "oidc-usermodel-attribute-mapper",
+          "consentRequired": false,
+          "config": {
+            "userinfo.token.claim": "true",
+            "user.attribute": "phoneNumber",
+            "id.token.claim": "true",
+            "access.token.claim": "true",
+            "claim.name": "phone_number",
+            "jsonType.label": "String"
+          }
+        },
+        {
+          "id": "74840763-9b35-4c9a-8789-4008fedc26c2",
+          "name": "phone number verified",
+          "protocol": "openid-connect",
+          "protocolMapper": "oidc-usermodel-attribute-mapper",
+          "consentRequired": false,
+          "config": {
+            "userinfo.token.claim": "true",
+            "user.attribute": "phoneNumberVerified",
+            "id.token.claim": "true",
+            "access.token.claim": "true",
+            "claim.name": "phone_number_verified",
+            "jsonType.label": "boolean"
+          }
+        }
+      ]
+    },
+    {
+      "id": "d5de6a8a-8894-4e72-b6ef-f9bf3a7a6541",
+      "name": "profile",
+      "description": "OpenID Connect built-in scope: profile",
+      "protocol": "openid-connect",
+      "attributes": {
+        "consent.screen.text": "${profileScopeConsentText}",
+        "display.on.consent.screen": "true"
+      },
+      "protocolMappers": [
+        {
+          "id": "ecb34ff7-d27d-4696-b536-0512044b21a9",
+          "name": "website",
+          "protocol": "openid-connect",
+          "protocolMapper": "oidc-usermodel-attribute-mapper",
+          "consentRequired": false,
+          "config": {
+            "userinfo.token.claim": "true",
+            "user.attribute": "website",
+            "id.token.claim": "true",
+            "access.token.claim": "true",
+            "claim.name": "website",
+            "jsonType.label": "String"
+          }
+        },
+        {
+          "id": "f3cf3c8b-891a-48a1-97b3-1d10d55ddecd",
+          "name": "locale",
+          "protocol": "openid-connect",
+          "protocolMapper": "oidc-usermodel-attribute-mapper",
+          "consentRequired": false,
+          "config": {
+            "userinfo.token.claim": "true",
+            "user.attribute": "locale",
+            "id.token.claim": "true",
+            "access.token.claim": "true",
+            "claim.name": "locale",
+            "jsonType.label": "String"
+          }
+        },
+        {
+          "id": "fa3b86c8-abaf-4261-b48d-41cd3cf2dc6a",
+          "name": "full name",
+          "protocol": "openid-connect",
+          "protocolMapper": "oidc-full-name-mapper",
+          "consentRequired": false,
+          "config": {
+            "id.token.claim": "true",
+            "access.token.claim": "true",
+            "userinfo.token.claim": "true"
+          }
+        },
+        {
+          "id": "219532f1-3c5c-4b30-a018-ae99adb6fc87",
+          "name": "birthdate",
+          "protocol": "openid-connect",
+          "protocolMapper": "oidc-usermodel-attribute-mapper",
+          "consentRequired": false,
+          "config": {
+            "userinfo.token.claim": "true",
+            "user.attribute": "birthdate",
+            "id.token.claim": "true",
+            "access.token.claim": "true",
+            "claim.name": "birthdate",
+            "jsonType.label": "String"
+          }
+        },
+        {
+          "id": "c443f4cd-1174-49a7-a2b3-65d5ccde5efa",
+          "name": "nickname",
+          "protocol": "openid-connect",
+          "protocolMapper": "oidc-usermodel-attribute-mapper",
+          "consentRequired": false,
+          "config": {
+            "userinfo.token.claim": "true",
+            "user.attribute": "nickname",
+            "id.token.claim": "true",
+            "access.token.claim": "true",
+            "claim.name": "nickname",
+            "jsonType.label": "String"
+          }
+        },
+        {
+          "id": "14631b9c-83eb-48ab-a224-29f047015e52",
+          "name": "username",
+          "protocol": "openid-connect",
+          "protocolMapper": "oidc-usermodel-property-mapper",
+          "consentRequired": false,
+          "config": {
+            "userinfo.token.claim": "true",
+            "user.attribute": "username",
+            "id.token.claim": "true",
+            "access.token.claim": "true",
+            "claim.name": "preferred_username",
+            "jsonType.label": "String"
+          }
+        },
+        {
+          "id": "cedd1f1b-951a-4332-9fa1-2edf1a266283",
+          "name": "middle name",
+          "protocol": "openid-connect",
+          "protocolMapper": "oidc-usermodel-attribute-mapper",
+          "consentRequired": false,
+          "config": {
+            "userinfo.token.claim": "true",
+            "user.attribute": "middleName",
+            "id.token.claim": "true",
+            "access.token.claim": "true",
+            "claim.name": "middle_name",
+            "jsonType.label": "String"
+          }
+        },
+        {
+          "id": "5a33d5e4-e124-412e-be4d-b6b28b41382a",
+          "name": "family name",
+          "protocol": "openid-connect",
+          "protocolMapper": "oidc-usermodel-property-mapper",
+          "consentRequired": false,
+          "config": {
+            "userinfo.token.claim": "true",
+            "user.attribute": "lastName",
+            "id.token.claim": "true",
+            "access.token.claim": "true",
+            "claim.name": "family_name",
+            "jsonType.label": "String"
+          }
+        },
+        {
+          "id": "c4122959-6738-4883-b50a-acd0033a477a",
+          "name": "profile",
+          "protocol": "openid-connect",
+          "protocolMapper": "oidc-usermodel-attribute-mapper",
+          "consentRequired": false,
+          "config": {
+            "userinfo.token.claim": "true",
+            "user.attribute": "profile",
+            "id.token.claim": "true",
+            "access.token.claim": "true",
+            "claim.name": "profile",
+            "jsonType.label": "String"
+          }
+        },
+        {
+          "id": "d7db1b88-2c3c-419a-91cf-19ad13355a56",
+          "name": "zoneinfo",
+          "protocol": "openid-connect",
+          "protocolMapper": "oidc-usermodel-attribute-mapper",
+          "consentRequired": false,
+          "config": {
+            "userinfo.token.claim": "true",
+            "user.attribute": "zoneinfo",
+            "id.token.claim": "true",
+            "access.token.claim": "true",
+            "claim.name": "zoneinfo",
+            "jsonType.label": "String"
+          }
+        },
+        {
+          "id": "dfd19868-5c87-4a51-80e0-2a82dfabb16c",
+          "name": "updated at",
+          "protocol": "openid-connect",
+          "protocolMapper": "oidc-usermodel-attribute-mapper",
+          "consentRequired": false,
+          "config": {
+            "userinfo.token.claim": "true",
+            "user.attribute": "updatedAt",
+            "id.token.claim": "true",
+            "access.token.claim": "true",
+            "claim.name": "updated_at",
+            "jsonType.label": "String"
+          }
+        },
+        {
+          "id": "19551b04-fc0c-44c4-b2bf-966da87ba3c3",
+          "name": "given name",
+          "protocol": "openid-connect",
+          "protocolMapper": "oidc-usermodel-property-mapper",
+          "consentRequired": false,
+          "config": {
+            "userinfo.token.claim": "true",
+            "user.attribute": "firstName",
+            "id.token.claim": "true",
+            "access.token.claim": "true",
+            "claim.name": "given_name",
+            "jsonType.label": "String"
+          }
+        },
+        {
+          "id": "c422b63f-e809-41c2-854c-e801e8e25485",
+          "name": "gender",
+          "protocol": "openid-connect",
+          "protocolMapper": "oidc-usermodel-attribute-mapper",
+          "consentRequired": false,
+          "config": {
+            "userinfo.token.claim": "true",
+            "user.attribute": "gender",
+            "id.token.claim": "true",
+            "access.token.claim": "true",
+            "claim.name": "gender",
+            "jsonType.label": "String"
+          }
+        },
+        {
+          "id": "2ccf435c-c255-4715-8dcd-15091f97c5a5",
+          "name": "picture",
+          "protocol": "openid-connect",
+          "protocolMapper": "oidc-usermodel-attribute-mapper",
+          "consentRequired": false,
+          "config": {
+            "userinfo.token.claim": "true",
+            "user.attribute": "picture",
+            "id.token.claim": "true",
+            "access.token.claim": "true",
+            "claim.name": "picture",
+            "jsonType.label": "String"
+          }
+        }
+      ]
+    },
+    {
+      "id": "391f1641-cb22-41a3-a0ed-752d9264aaf5",
+      "name": "role_list",
+      "description": "SAML role list",
+      "protocol": "saml",
+      "attributes": {
+        "consent.screen.text": "${samlRoleListScopeConsentText}",
+        "display.on.consent.screen": "true"
+      },
+      "protocolMappers": [
+        {
+          "id": "7cbb4477-af0c-4394-a754-b72a1b4638e0",
+          "name": "role list",
+          "protocol": "saml",
+          "protocolMapper": "saml-role-list-mapper",
+          "consentRequired": false,
+          "config": {
+            "single": "false",
+            "attribute.nameformat": "Basic",
+            "attribute.name": "Role"
+          }
+        }
+      ]
+    },
+    {
+      "id": "021d9ac1-9650-401c-b2a2-efa2f3e6b70c",
+      "name": "roles",
+      "description": "OpenID Connect scope for add user roles to the access token",
+      "protocol": "openid-connect",
+      "attributes": {
+        "include.in.token.scope": "false",
+        "display.on.consent.screen": "true",
+        "consent.screen.text": "${rolesScopeConsentText}"
+      },
+      "protocolMappers": [
+        {
+          "id": "bbbd7dc1-63d0-468f-9463-4772833ef2fa",
+          "name": "client roles",
+          "protocol": "openid-connect",
+          "protocolMapper": "oidc-usermodel-client-role-mapper",
+          "consentRequired": false,
+          "config": {
+            "user.attribute": "foo",
+            "access.token.claim": "true",
+            "claim.name": "resource_access.${client_id}.roles",
+            "jsonType.label": "String",
+            "multivalued": "true"
+          }
+        },
+        {
+          "id": "9271d821-b803-406d-8574-2f5a2693f065",
+          "name": "realm roles",
+          "protocol": "openid-connect",
+          "protocolMapper": "oidc-usermodel-realm-role-mapper",
+          "consentRequired": false,
+          "config": {
+            "user.attribute": "foo",
+            "access.token.claim": "true",
+            "claim.name": "realm_access.roles",
+            "jsonType.label": "String",
+            "multivalued": "true"
+          }
+        },
+        {
+          "id": "90b1a715-e12e-4af8-89f6-82a370d57d1a",
+          "name": "audience resolve",
+          "protocol": "openid-connect",
+          "protocolMapper": "oidc-audience-resolve-mapper",
+          "consentRequired": false,
+          "config": {}
+        }
+      ]
+    },
+    {
+      "id": "84fdcb72-668b-408e-aaea-110d594afe5e",
+      "name": "web-origins",
+      "description": "OpenID Connect scope for add allowed web origins to the access token",
+      "protocol": "openid-connect",
+      "attributes": {
+        "include.in.token.scope": "false",
+        "display.on.consent.screen": "false",
+        "consent.screen.text": ""
+      },
+      "protocolMappers": [
+        {
+          "id": "134b3451-cf2d-4ead-8fce-bf24b32f014c",
+          "name": "allowed web origins",
+          "protocol": "openid-connect",
+          "protocolMapper": "oidc-allowed-origins-mapper",
+          "consentRequired": false,
+          "config": {}
+        }
+      ]
+    }
+  ],
+  "defaultDefaultClientScopes": ["roles", "role_list", "email", "web-origins", "profile"],
+  "defaultOptionalClientScopes": ["phone", "address", "offline_access", "microprofile-jwt"],
+  "browserSecurityHeaders": {
+    "contentSecurityPolicyReportOnly": "",
+    "xContentTypeOptions": "nosniff",
+    "xRobotsTag": "none",
+    "xFrameOptions": "SAMEORIGIN",
+    "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';",
+    "xXSSProtection": "1; mode=block",
+    "strictTransportSecurity": "max-age=31536000; includeSubDomains"
+  },
+  "smtpServer": {},
+  "eventsEnabled": false,
+  "eventsListeners": ["jboss-logging"],
+  "enabledEventTypes": [],
+  "adminEventsEnabled": false,
+  "adminEventsDetailsEnabled": false,
+  "components": {
+    "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [
+      {
+        "id": "96bb5289-e057-4a3d-a273-89372bbc8cc0",
+        "name": "Allowed Client Scopes",
+        "providerId": "allowed-client-templates",
+        "subType": "authenticated",
+        "subComponents": {},
+        "config": {
+          "allow-default-scopes": ["true"]
+        }
+      },
+      {
+        "id": "5782d02c-2ba5-47d1-9732-dfaaf0e5cfdf",
+        "name": "Allowed Protocol Mapper Types",
+        "providerId": "allowed-protocol-mappers",
+        "subType": "authenticated",
+        "subComponents": {},
+        "config": {
+          "allowed-protocol-mapper-types": [
+            "oidc-full-name-mapper",
+            "saml-user-property-mapper",
+            "oidc-usermodel-attribute-mapper",
+            "saml-user-attribute-mapper",
+            "oidc-address-mapper",
+            "saml-role-list-mapper",
+            "oidc-usermodel-property-mapper",
+            "oidc-sha256-pairwise-sub-mapper"
+          ]
+        }
+      },
+      {
+        "id": "c8d395e2-dd81-4118-b838-7095be5cc1c8",
+        "name": "Allowed Protocol Mapper Types",
+        "providerId": "allowed-protocol-mappers",
+        "subType": "anonymous",
+        "subComponents": {},
+        "config": {
+          "allowed-protocol-mapper-types": [
+            "oidc-address-mapper",
+            "oidc-full-name-mapper",
+            "oidc-usermodel-attribute-mapper",
+            "oidc-usermodel-property-mapper",
+            "saml-user-attribute-mapper",
+            "saml-user-property-mapper",
+            "oidc-sha256-pairwise-sub-mapper",
+            "saml-role-list-mapper"
+          ]
+        }
+      },
+      {
+        "id": "1175c6f8-2d58-437f-bb21-4660c8c1a62c",
+        "name": "Full Scope Disabled",
+        "providerId": "scope",
+        "subType": "anonymous",
+        "subComponents": {},
+        "config": {}
+      },
+      {
+        "id": "4bd2778a-908f-4ac3-873f-61e674eecc2f",
+        "name": "Consent Required",
+        "providerId": "consent-required",
+        "subType": "anonymous",
+        "subComponents": {},
+        "config": {}
+      },
+      {
+        "id": "65809493-b60f-4b44-b1dc-28a22772c321",
+        "name": "Allowed Client Scopes",
+        "providerId": "allowed-client-templates",
+        "subType": "anonymous",
+        "subComponents": {},
+        "config": {
+          "allow-default-scopes": ["true"]
+        }
+      },
+      {
+        "id": "950acf43-614d-47a3-a7c9-d5072433c4b8",
+        "name": "Max Clients Limit",
+        "providerId": "max-clients",
+        "subType": "anonymous",
+        "subComponents": {},
+        "config": {
+          "max-clients": ["200"]
+        }
+      },
+      {
+        "id": "e2a65f9f-5ad8-4634-ab3a-810409a3e067",
+        "name": "Trusted Hosts",
+        "providerId": "trusted-hosts",
+        "subType": "anonymous",
+        "subComponents": {},
+        "config": {
+          "host-sending-registration-request-must-match": ["true"],
+          "client-uris-must-match": ["true"]
+        }
+      }
+    ],
+    "org.keycloak.keys.KeyProvider": [
+      {
+        "id": "8ab3be1d-3e01-4eb6-8ff2-d116b02ffc48",
+        "name": "aes-generated",
+        "providerId": "aes-generated",
+        "subComponents": {},
+        "config": {
+          "priority": ["100"]
+        }
+      },
+      {
+        "id": "2a3661d8-3594-4872-881a-9f2d394b675d",
+        "name": "rsa-generated",
+        "providerId": "rsa-generated",
+        "subComponents": {},
+        "config": {
+          "priority": ["100"]
+        }
+      },
+      {
+        "id": "1d291cea-3d41-41cb-9ab6-ef1513fe3fe8",
+        "name": "hmac-generated",
+        "providerId": "hmac-generated",
+        "subComponents": {},
+        "config": {
+          "priority": ["100"]
+        }
+      }
+    ]
+  },
+  "internationalizationEnabled": false,
+  "supportedLocales": [],
+  "authenticationFlows": [
+    {
+      "id": "20a3af86-1f13-489d-bb67-db3b7df52875",
+      "alias": "Handle Existing Account",
+      "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider",
+      "providerId": "basic-flow",
+      "topLevel": false,
+      "builtIn": true,
+      "authenticationExecutions": [
+        {
+          "authenticator": "idp-confirm-link",
+          "requirement": "REQUIRED",
+          "priority": 10,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "requirement": "REQUIRED",
+          "priority": 20,
+          "flowAlias": "Handle Existing Account - Alternatives - 0",
+          "userSetupAllowed": false,
+          "autheticatorFlow": true
+        }
+      ]
+    },
+    {
+      "id": "bd5b10aa-4005-4ea6-8995-9d1caac7b53e",
+      "alias": "Handle Existing Account - Alternatives - 0",
+      "description": "Subflow of Handle Existing Account with alternative executions",
+      "providerId": "basic-flow",
+      "topLevel": false,
+      "builtIn": true,
+      "authenticationExecutions": [
+        {
+          "authenticator": "idp-email-verification",
+          "requirement": "ALTERNATIVE",
+          "priority": 10,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "requirement": "ALTERNATIVE",
+          "priority": 20,
+          "flowAlias": "Verify Existing Account by Re-authentication",
+          "userSetupAllowed": false,
+          "autheticatorFlow": true
+        }
+      ]
+    },
+    {
+      "id": "1a5ca248-a893-40ad-b184-63f59c06e988",
+      "alias": "Verify Existing Account by Re-authentication",
+      "description": "Reauthentication of existing account",
+      "providerId": "basic-flow",
+      "topLevel": false,
+      "builtIn": true,
+      "authenticationExecutions": [
+        {
+          "authenticator": "idp-username-password-form",
+          "requirement": "REQUIRED",
+          "priority": 10,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "requirement": "CONDITIONAL",
+          "priority": 20,
+          "flowAlias": "Verify Existing Account by Re-authentication - auth-otp-form - Conditional",
+          "userSetupAllowed": false,
+          "autheticatorFlow": true
+        }
+      ]
+    },
+    {
+      "id": "e13aa76b-dfe1-4158-82f2-c4bad23cf32a",
+      "alias": "Verify Existing Account by Re-authentication - auth-otp-form - Conditional",
+      "description": "Flow to determine if the auth-otp-form authenticator should be used or not.",
+      "providerId": "basic-flow",
+      "topLevel": false,
+      "builtIn": true,
+      "authenticationExecutions": [
+        {
+          "authenticator": "conditional-user-configured",
+          "requirement": "REQUIRED",
+          "priority": 10,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "authenticator": "auth-otp-form",
+          "requirement": "REQUIRED",
+          "priority": 20,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        }
+      ]
+    },
+    {
+      "id": "173ece89-908a-4c0e-9a6e-ccf6c871a2fc",
+      "alias": "browser",
+      "description": "browser based authentication",
+      "providerId": "basic-flow",
+      "topLevel": true,
+      "builtIn": true,
+      "authenticationExecutions": [
+        {
+          "authenticator": "auth-cookie",
+          "requirement": "ALTERNATIVE",
+          "priority": 10,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "authenticator": "auth-spnego",
+          "requirement": "DISABLED",
+          "priority": 20,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "authenticator": "identity-provider-redirector",
+          "requirement": "ALTERNATIVE",
+          "priority": 25,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "requirement": "ALTERNATIVE",
+          "priority": 30,
+          "flowAlias": "forms",
+          "userSetupAllowed": false,
+          "autheticatorFlow": true
+        }
+      ]
+    },
+    {
+      "id": "4f469fff-9c0f-40d5-94d2-87ce8ecd34b7",
+      "alias": "clients",
+      "description": "Base authentication for clients",
+      "providerId": "client-flow",
+      "topLevel": true,
+      "builtIn": true,
+      "authenticationExecutions": [
+        {
+          "authenticator": "client-secret",
+          "requirement": "ALTERNATIVE",
+          "priority": 10,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "authenticator": "client-jwt",
+          "requirement": "ALTERNATIVE",
+          "priority": 20,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "authenticator": "client-secret-jwt",
+          "requirement": "ALTERNATIVE",
+          "priority": 30,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "authenticator": "client-x509",
+          "requirement": "ALTERNATIVE",
+          "priority": 40,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        }
+      ]
+    },
+    {
+      "id": "d17ed54c-9c98-4155-b8eb-c697101a325d",
+      "alias": "direct grant",
+      "description": "OpenID Connect Resource Owner Grant",
+      "providerId": "basic-flow",
+      "topLevel": true,
+      "builtIn": true,
+      "authenticationExecutions": [
+        {
+          "authenticator": "direct-grant-validate-username",
+          "requirement": "REQUIRED",
+          "priority": 10,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "authenticator": "direct-grant-validate-password",
+          "requirement": "REQUIRED",
+          "priority": 20,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "requirement": "CONDITIONAL",
+          "priority": 30,
+          "flowAlias": "direct grant - direct-grant-validate-otp - Conditional",
+          "userSetupAllowed": false,
+          "autheticatorFlow": true
+        }
+      ]
+    },
+    {
+      "id": "b4d3e6be-fd6d-4588-a4cc-cfe4420c1fba",
+      "alias": "direct grant - direct-grant-validate-otp - Conditional",
+      "description": "Flow to determine if the direct-grant-validate-otp authenticator should be used or not.",
+      "providerId": "basic-flow",
+      "topLevel": false,
+      "builtIn": true,
+      "authenticationExecutions": [
+        {
+          "authenticator": "conditional-user-configured",
+          "requirement": "REQUIRED",
+          "priority": 10,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "authenticator": "direct-grant-validate-otp",
+          "requirement": "REQUIRED",
+          "priority": 20,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        }
+      ]
+    },
+    {
+      "id": "7c5067b6-513d-4676-9afa-47b199c7194e",
+      "alias": "docker auth",
+      "description": "Used by Docker clients to authenticate against the IDP",
+      "providerId": "basic-flow",
+      "topLevel": true,
+      "builtIn": true,
+      "authenticationExecutions": [
+        {
+          "authenticator": "docker-http-basic-authenticator",
+          "requirement": "REQUIRED",
+          "priority": 10,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        }
+      ]
+    },
+    {
+      "id": "76be1624-c049-4e13-8ecd-8d01ac9433ac",
+      "alias": "first broker login",
+      "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account",
+      "providerId": "basic-flow",
+      "topLevel": true,
+      "builtIn": true,
+      "authenticationExecutions": [
+        {
+          "authenticatorConfig": "review profile config",
+          "authenticator": "idp-review-profile",
+          "requirement": "REQUIRED",
+          "priority": 10,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "requirement": "REQUIRED",
+          "priority": 20,
+          "flowAlias": "first broker login - Alternatives - 0",
+          "userSetupAllowed": false,
+          "autheticatorFlow": true
+        }
+      ]
+    },
+    {
+      "id": "91ee6c4e-dedd-4024-b6a8-c332ef2444a5",
+      "alias": "first broker login - Alternatives - 0",
+      "description": "Subflow of first broker login with alternative executions",
+      "providerId": "basic-flow",
+      "topLevel": false,
+      "builtIn": true,
+      "authenticationExecutions": [
+        {
+          "authenticatorConfig": "create unique user config",
+          "authenticator": "idp-create-user-if-unique",
+          "requirement": "ALTERNATIVE",
+          "priority": 10,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "requirement": "ALTERNATIVE",
+          "priority": 20,
+          "flowAlias": "Handle Existing Account",
+          "userSetupAllowed": false,
+          "autheticatorFlow": true
+        }
+      ]
+    },
+    {
+      "id": "2dc1cb10-ea2c-48a2-8131-3c950784a60e",
+      "alias": "forms",
+      "description": "Username, password, otp and other auth forms.",
+      "providerId": "basic-flow",
+      "topLevel": false,
+      "builtIn": true,
+      "authenticationExecutions": [
+        {
+          "authenticator": "auth-username-password-form",
+          "requirement": "REQUIRED",
+          "priority": 10,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "requirement": "CONDITIONAL",
+          "priority": 20,
+          "flowAlias": "forms - auth-otp-form - Conditional",
+          "userSetupAllowed": false,
+          "autheticatorFlow": true
+        }
+      ]
+    },
+    {
+      "id": "7cdad543-5bdf-4f4a-a527-34e1b30889de",
+      "alias": "forms - auth-otp-form - Conditional",
+      "description": "Flow to determine if the auth-otp-form authenticator should be used or not.",
+      "providerId": "basic-flow",
+      "topLevel": false,
+      "builtIn": true,
+      "authenticationExecutions": [
+        {
+          "authenticator": "conditional-user-configured",
+          "requirement": "REQUIRED",
+          "priority": 10,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "authenticator": "auth-otp-form",
+          "requirement": "REQUIRED",
+          "priority": 20,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        }
+      ]
+    },
+    {
+      "id": "98562c33-8964-42c4-8a69-204226d0c4c7",
+      "alias": "http challenge",
+      "description": "An authentication flow based on challenge-response HTTP Authentication Schemes",
+      "providerId": "basic-flow",
+      "topLevel": true,
+      "builtIn": true,
+      "authenticationExecutions": [
+        {
+          "authenticator": "no-cookie-redirect",
+          "requirement": "REQUIRED",
+          "priority": 10,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "authenticator": "basic-auth",
+          "requirement": "REQUIRED",
+          "priority": 20,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "authenticator": "basic-auth-otp",
+          "requirement": "DISABLED",
+          "priority": 30,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "authenticator": "auth-spnego",
+          "requirement": "DISABLED",
+          "priority": 40,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        }
+      ]
+    },
+    {
+      "id": "6f1cb686-0852-4600-8eec-7c00cb465b35",
+      "alias": "registration",
+      "description": "registration flow",
+      "providerId": "basic-flow",
+      "topLevel": true,
+      "builtIn": true,
+      "authenticationExecutions": [
+        {
+          "authenticator": "registration-page-form",
+          "requirement": "REQUIRED",
+          "priority": 10,
+          "flowAlias": "registration form",
+          "userSetupAllowed": false,
+          "autheticatorFlow": true
+        }
+      ]
+    },
+    {
+      "id": "f335a41a-4044-43bf-ab0a-c39ea0a9246e",
+      "alias": "registration form",
+      "description": "registration form",
+      "providerId": "form-flow",
+      "topLevel": false,
+      "builtIn": true,
+      "authenticationExecutions": [
+        {
+          "authenticator": "registration-user-creation",
+          "requirement": "REQUIRED",
+          "priority": 20,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "authenticator": "registration-profile-action",
+          "requirement": "REQUIRED",
+          "priority": 40,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "authenticator": "registration-password-action",
+          "requirement": "REQUIRED",
+          "priority": 50,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "authenticator": "registration-recaptcha-action",
+          "requirement": "DISABLED",
+          "priority": 60,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        }
+      ]
+    },
+    {
+      "id": "d1364557-be6a-4e58-9db0-d1fda2305e6e",
+      "alias": "reset credentials",
+      "description": "Reset credentials for a user if they forgot their password or something",
+      "providerId": "basic-flow",
+      "topLevel": true,
+      "builtIn": true,
+      "authenticationExecutions": [
+        {
+          "authenticator": "reset-credentials-choose-user",
+          "requirement": "REQUIRED",
+          "priority": 10,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "authenticator": "reset-credential-email",
+          "requirement": "REQUIRED",
+          "priority": 20,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "authenticator": "reset-password",
+          "requirement": "REQUIRED",
+          "priority": 30,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "requirement": "CONDITIONAL",
+          "priority": 40,
+          "flowAlias": "reset credentials - reset-otp - Conditional",
+          "userSetupAllowed": false,
+          "autheticatorFlow": true
+        }
+      ]
+    },
+    {
+      "id": "558b807f-fed5-4c79-a80e-136a4468fbc5",
+      "alias": "reset credentials - reset-otp - Conditional",
+      "description": "Flow to determine if the reset-otp authenticator should be used or not.",
+      "providerId": "basic-flow",
+      "topLevel": false,
+      "builtIn": true,
+      "authenticationExecutions": [
+        {
+          "authenticator": "conditional-user-configured",
+          "requirement": "REQUIRED",
+          "priority": 10,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        },
+        {
+          "authenticator": "reset-otp",
+          "requirement": "REQUIRED",
+          "priority": 20,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        }
+      ]
+    },
+    {
+      "id": "e86247fb-5e43-41fb-88f4-355a32d2b89d",
+      "alias": "saml ecp",
+      "description": "SAML ECP Profile Authentication Flow",
+      "providerId": "basic-flow",
+      "topLevel": true,
+      "builtIn": true,
+      "authenticationExecutions": [
+        {
+          "authenticator": "http-basic-authenticator",
+          "requirement": "REQUIRED",
+          "priority": 10,
+          "userSetupAllowed": false,
+          "autheticatorFlow": false
+        }
+      ]
+    }
+  ],
+  "authenticatorConfig": [
+    {
+      "id": "d2ce2a29-3a8f-4797-9529-b5e498d835b7",
+      "alias": "create unique user config",
+      "config": {
+        "require.password.update.after.registration": "false"
+      }
+    },
+    {
+      "id": "949fe63b-47a4-41b6-9872-0aabe1806b57",
+      "alias": "review profile config",
+      "config": {
+        "update.profile.on.first.login": "missing"
+      }
+    }
+  ],
+  "requiredActions": [
+    {
+      "alias": "CONFIGURE_TOTP",
+      "name": "Configure OTP",
+      "providerId": "CONFIGURE_TOTP",
+      "enabled": true,
+      "defaultAction": false,
+      "priority": 10,
+      "config": {}
+    },
+    {
+      "alias": "terms_and_conditions",
+      "name": "Terms and Conditions",
+      "providerId": "terms_and_conditions",
+      "enabled": false,
+      "defaultAction": false,
+      "priority": 20,
+      "config": {}
+    },
+    {
+      "alias": "UPDATE_PASSWORD",
+      "name": "Update Password",
+      "providerId": "UPDATE_PASSWORD",
+      "enabled": true,
+      "defaultAction": false,
+      "priority": 30,
+      "config": {}
+    },
+    {
+      "alias": "UPDATE_PROFILE",
+      "name": "Update Profile",
+      "providerId": "UPDATE_PROFILE",
+      "enabled": true,
+      "defaultAction": false,
+      "priority": 40,
+      "config": {}
+    },
+    {
+      "alias": "VERIFY_EMAIL",
+      "name": "Verify Email",
+      "providerId": "VERIFY_EMAIL",
+      "enabled": true,
+      "defaultAction": false,
+      "priority": 50,
+      "config": {}
+    },
+    {
+      "alias": "update_user_locale",
+      "name": "Update User Locale",
+      "providerId": "update_user_locale",
+      "enabled": true,
+      "defaultAction": false,
+      "priority": 1000,
+      "config": {}
+    }
+  ],
+  "browserFlow": "browser",
+  "registrationFlow": "registration",
+  "directGrantFlow": "direct grant",
+  "resetCredentialsFlow": "reset credentials",
+  "clientAuthenticationFlow": "clients",
+  "dockerAuthenticationFlow": "docker auth",
+  "attributes": {},
+  "keycloakVersion": "10.0.0",
+  "userManagedAccessAllowed": false
+}
diff --git a/src/main/docker/realm-config/jhipster-users-0.json b/src/main/docker/realm-config/jhipster-users-0.json
new file mode 100644
index 0000000..a72dfdc
--- /dev/null
+++ b/src/main/docker/realm-config/jhipster-users-0.json
@@ -0,0 +1,80 @@
+{
+  "realm": "jhipster",
+  "users": [
+    {
+      "id": "4c973896-5761-41fc-8217-07c5d13a004b",
+      "createdTimestamp": 1505479415590,
+      "username": "admin",
+      "enabled": true,
+      "totp": false,
+      "emailVerified": true,
+      "firstName": "Admin",
+      "lastName": "Administrator",
+      "email": "admin@localhost",
+      "credentials": [
+        {
+          "id": "b860462b-9b02-48ba-9523-d3a8926a917b",
+          "type": "password",
+          "createdDate": 1505479429154,
+          "secretData": "{\"value\":\"4pf9K2jWSCcHC+CwsZP/qidN5pSmDUe6AX6wBerSGdBVKkExay8MWKx+EKmaaObZW6FVsD8vdW/ZsyUFD9gJ1Q==\",\"salt\":\"1/qNkZ5kr77jOMOBPBogGw==\"}",
+          "credentialData": "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\"}"
+        }
+      ],
+      "disableableCredentialTypes": [],
+      "requiredActions": [],
+      "realmRoles": ["offline_access", "uma_authorization"],
+      "clientRoles": {
+        "account": ["view-profile", "manage-account"]
+      },
+      "notBefore": 0,
+      "groups": ["/Admins", "/Users"]
+    },
+    {
+      "id": "e3ab6777-e2d7-474e-b831-64e785d8be1f",
+      "createdTimestamp": 1576348683889,
+      "username": "service-account-internal",
+      "enabled": true,
+      "totp": false,
+      "emailVerified": false,
+      "serviceAccountClientId": "internal",
+      "credentials": [],
+      "disableableCredentialTypes": [],
+      "requiredActions": [],
+      "realmRoles": ["offline_access", "uma_authorization"],
+      "clientRoles": {
+        "internal": ["uma_protection"],
+        "account": ["view-profile", "manage-account"]
+      },
+      "notBefore": 0,
+      "groups": []
+    },
+    {
+      "id": "c4af4e2f-b432-4c3b-8405-cca86cd5b97b",
+      "createdTimestamp": 1505479373742,
+      "username": "user",
+      "enabled": true,
+      "totp": false,
+      "emailVerified": true,
+      "firstName": "",
+      "lastName": "User",
+      "email": "user@localhost",
+      "credentials": [
+        {
+          "id": "7821832b-1e82-45a2-b8d3-f1a6ad909e64",
+          "type": "password",
+          "createdDate": 1505479392766,
+          "secretData": "{\"value\":\"MbKsMgWPnZyImih8s4SaoCSCq+XIY/c6S9F93sXEidHF1TjPWxCqMkec0+o3860CMLXHt3az61cIJOWI0FW9aw==\",\"salt\":\"fmpBI1r8R1u75hDLMUlwBw==\"}",
+          "credentialData": "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\"}"
+        }
+      ],
+      "disableableCredentialTypes": [],
+      "requiredActions": [],
+      "realmRoles": ["offline_access", "uma_authorization"],
+      "clientRoles": {
+        "account": ["view-profile", "manage-account"]
+      },
+      "notBefore": 0,
+      "groups": ["/Users"]
+    }
+  ]
+}
diff --git a/src/main/java/tech/jhipster/controlcenter/config/Constants.java b/src/main/java/tech/jhipster/controlcenter/config/Constants.java
new file mode 100644
index 0000000..9a3e0c7
--- /dev/null
+++ b/src/main/java/tech/jhipster/controlcenter/config/Constants.java
@@ -0,0 +1,7 @@
+package tech.jhipster.controlcenter.config;
+
+public class Constants {
+    public static final String PROFILE_OAUTH2 = "oauth2";
+
+    private Constants() {}
+}
diff --git a/src/main/java/tech/jhipster/controlcenter/config/SecurityConfiguration.java b/src/main/java/tech/jhipster/controlcenter/config/JwtSecurityConfiguration.java
similarity index 95%
rename from src/main/java/tech/jhipster/controlcenter/config/SecurityConfiguration.java
rename to src/main/java/tech/jhipster/controlcenter/config/JwtSecurityConfiguration.java
index 80569af..1cadbbf 100644
--- a/src/main/java/tech/jhipster/controlcenter/config/SecurityConfiguration.java
+++ b/src/main/java/tech/jhipster/controlcenter/config/JwtSecurityConfiguration.java
@@ -5,6 +5,7 @@ import static org.springframework.security.web.server.util.matcher.ServerWebExch
 import org.springframework.boot.autoconfigure.security.SecurityProperties;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Import;
+import org.springframework.context.annotation.Profile;
 import org.springframework.http.HttpMethod;
 import org.springframework.security.authentication.ReactiveAuthenticationManager;
 import org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager;
@@ -29,12 +30,13 @@ import tech.jhipster.controlcenter.web.filter.SpaWebFilter;
 @EnableWebFluxSecurity
 @EnableReactiveMethodSecurity
 @Import(SecurityProblemSupport.class)
-public class SecurityConfiguration {
+@Profile("!" + Constants.PROFILE_OAUTH2)
+public class JwtSecurityConfiguration {
     private final TokenProvider tokenProvider;
 
     private final SecurityProblemSupport problemSupport;
 
-    public SecurityConfiguration(TokenProvider tokenProvider, SecurityProblemSupport problemSupport) {
+    public JwtSecurityConfiguration(TokenProvider tokenProvider, SecurityProblemSupport problemSupport) {
         this.tokenProvider = tokenProvider;
         this.problemSupport = problemSupport;
     }
diff --git a/src/main/java/tech/jhipster/controlcenter/config/Oauth2SecurityConfiguration.java b/src/main/java/tech/jhipster/controlcenter/config/Oauth2SecurityConfiguration.java
new file mode 100644
index 0000000..a81cf2b
--- /dev/null
+++ b/src/main/java/tech/jhipster/controlcenter/config/Oauth2SecurityConfiguration.java
@@ -0,0 +1,221 @@
+package tech.jhipster.controlcenter.config;
+
+import static org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers.pathMatchers;
+
+import io.github.jhipster.config.JHipsterProperties;
+import io.github.jhipster.web.filter.reactive.CookieCsrfFilter;
+import java.util.HashSet;
+import java.util.Set;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.security.SecurityProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Import;
+import org.springframework.context.annotation.Profile;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.authentication.ReactiveAuthenticationManager;
+import org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager;
+import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
+import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
+import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
+import org.springframework.security.config.web.server.ServerHttpSecurity;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
+import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.oauth2.client.oidc.userinfo.OidcReactiveOAuth2UserService;
+import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
+import org.springframework.security.oauth2.client.userinfo.ReactiveOAuth2UserService;
+import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
+import org.springframework.security.oauth2.core.OAuth2TokenValidator;
+import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
+import org.springframework.security.oauth2.core.oidc.user.OidcUser;
+import org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;
+import org.springframework.security.oauth2.jwt.Jwt;
+import org.springframework.security.oauth2.jwt.JwtValidators;
+import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder;
+import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
+import org.springframework.security.oauth2.jwt.ReactiveJwtDecoders;
+import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
+import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter;
+import org.springframework.security.web.server.SecurityWebFilterChain;
+import org.springframework.security.web.server.csrf.CookieServerCsrfTokenRepository;
+import org.springframework.security.web.server.header.ReferrerPolicyServerHttpHeadersWriter;
+import org.springframework.security.web.server.util.matcher.NegatedServerWebExchangeMatcher;
+import org.springframework.security.web.server.util.matcher.OrServerWebExchangeMatcher;
+import org.springframework.util.StringUtils;
+import org.zalando.problem.spring.webflux.advice.security.SecurityProblemSupport;
+import reactor.core.publisher.Mono;
+import tech.jhipster.controlcenter.security.AuthoritiesConstants;
+import tech.jhipster.controlcenter.security.SecurityUtils;
+import tech.jhipster.controlcenter.security.jwt.JWTFilter;
+import tech.jhipster.controlcenter.security.jwt.TokenProvider;
+import tech.jhipster.controlcenter.security.oauth2.AudienceValidator;
+import tech.jhipster.controlcenter.security.oauth2.JwtGrantedAuthorityConverter;
+import tech.jhipster.controlcenter.web.filter.SpaWebFilter;
+
+@EnableWebFluxSecurity
+@EnableReactiveMethodSecurity
+@Import(SecurityProblemSupport.class)
+@Profile(Constants.PROFILE_OAUTH2)
+public class Oauth2SecurityConfiguration {
+    @Value("${spring.security.oauth2.client.provider.oidc.issuer-uri}")
+    private String issuerUri;
+
+    @Value("${spring.security.oauth2.client.registration.oidc.client-id}")
+    private String cliendId;
+
+    @Value("${spring.security.oauth2.client.registration.oidc.client-secret}")
+    private String clientSecret;
+
+    private final TokenProvider tokenProvider;
+
+    private final JHipsterProperties jHipsterProperties;
+
+    private final SecurityProblemSupport problemSupport;
+
+    public Oauth2SecurityConfiguration(
+        TokenProvider tokenProvider,
+        JHipsterProperties jHipsterProperties,
+        SecurityProblemSupport problemSupport
+    ) {
+        this.tokenProvider = tokenProvider;
+        this.jHipsterProperties = jHipsterProperties;
+        this.problemSupport = problemSupport;
+    }
+
+    @Bean
+    public MapReactiveUserDetailsService userDetailsService(SecurityProperties properties) {
+        SecurityProperties.User user = properties.getUser();
+        UserDetails userDetails = User
+            .withUsername(user.getName())
+            .password("{noop}" + user.getPassword())
+            .roles(StringUtils.toStringArray(user.getRoles()))
+            .build();
+        return new MapReactiveUserDetailsService(userDetails);
+    }
+
+    @Bean
+    public ReactiveAuthenticationManager reactiveAuthenticationManager(ReactiveUserDetailsService userDetailsService) {
+        return new UserDetailsRepositoryReactiveAuthenticationManager(userDetailsService);
+    }
+
+    @Bean
+    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
+        // @formatter:off
+        // Authenticate through configured OpenID Provider
+        http.oauth2Login();
+
+        http.oauth2ResourceServer().jwt().jwtAuthenticationConverter(jwtAuthenticationConverter());
+        http.oauth2Client();
+
+        http
+            .securityMatcher(
+                new NegatedServerWebExchangeMatcher(
+                    new OrServerWebExchangeMatcher(
+                        pathMatchers("/app/**", "/i18n/**", "/content/**", "/swagger-ui/**", "/test/**", "/webjars/**"),
+                        pathMatchers(HttpMethod.OPTIONS, "/**")
+                    )
+                )
+            )
+            .addFilterAt(new CookieCsrfFilter(), SecurityWebFiltersOrder.REACTOR_CONTEXT)
+            .addFilterAt(new SpaWebFilter(), SecurityWebFiltersOrder.AUTHENTICATION)
+            .addFilterAt(new JWTFilter(tokenProvider), SecurityWebFiltersOrder.HTTP_BASIC)
+            .exceptionHandling()
+                .accessDeniedHandler(problemSupport)
+                .authenticationEntryPoint(problemSupport)
+            .and()
+                .headers()
+                    .contentSecurityPolicy(
+                        "default-src 'self'; frame-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://storage.googleapis.com; style-src 'self' https://fonts.googleapis.com 'unsafe-inline'; img-src 'self' data:; font-src 'self' https://fonts.gstatic.com data:"
+                    )
+                .and()
+                    .referrerPolicy(ReferrerPolicyServerHttpHeadersWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN)
+                .and()
+                    .featurePolicy(
+                        "geolocation 'none'; midi 'none'; sync-xhr 'none'; microphone 'none'; camera 'none'; magnetometer 'none'; gyroscope 'none'; speaker 'none'; fullscreen 'self'; payment 'none'"
+                    )
+                .and()
+                    .frameOptions().disable();
+
+        // Require authentication for all requests
+        http
+            .authorizeExchange()
+            .pathMatchers("/").permitAll()
+            .pathMatchers("/*.*").permitAll()
+            .pathMatchers("/api/auth-info").permitAll()
+            .pathMatchers("/api/authenticate").permitAll()
+            .pathMatchers("/api/**").authenticated()
+            // jhcc-custom : begin
+            .pathMatchers("/services/**", "/gateway/**", "/v2/api-docs", "/swagger-ui/index.html").authenticated()
+            .pathMatchers("/swagger-resources/**").permitAll()
+            .pathMatchers("/management/health").permitAll()
+            .pathMatchers("/management/info").permitAll()
+            .pathMatchers("/management/prometheus").permitAll()
+            .pathMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN);
+        // jhcc-custom : end
+
+        // for xsrf-token
+        http.csrf().csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse());
+
+        // @formatter:on
+        return http.build();
+    }
+
+    Converter<Jwt, Mono<AbstractAuthenticationToken>> jwtAuthenticationConverter() {
+        JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
+        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(new JwtGrantedAuthorityConverter());
+        return new ReactiveJwtAuthenticationConverterAdapter(jwtAuthenticationConverter);
+    }
+
+    /**
+     * Map authorities from "groups" or "roles" claim in ID Token.
+     *
+     * @return a {@link ReactiveOAuth2UserService} that has the groups from the IdP.
+     */
+    @Bean
+    public ReactiveOAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {
+        final OidcReactiveOAuth2UserService delegate = new OidcReactiveOAuth2UserService();
+
+        return userRequest -> {
+            // Delegate to the default implementation for loading a user
+            return delegate
+                .loadUser(userRequest)
+                .map(
+                    user -> {
+                        Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
+
+                        user
+                            .getAuthorities()
+                            .forEach(
+                                authority -> {
+                                    if (authority instanceof OidcUserAuthority) {
+                                        OidcUserAuthority oidcUserAuthority = (OidcUserAuthority) authority;
+                                        mappedAuthorities.addAll(
+                                            SecurityUtils.extractAuthorityFromClaims(oidcUserAuthority.getUserInfo().getClaims())
+                                        );
+                                    }
+                                }
+                            );
+
+                        return new DefaultOidcUser(mappedAuthorities, user.getIdToken(), user.getUserInfo());
+                    }
+                );
+        };
+    }
+
+    @Bean
+    ReactiveJwtDecoder jwtDecoder() {
+        NimbusReactiveJwtDecoder jwtDecoder = (NimbusReactiveJwtDecoder) ReactiveJwtDecoders.fromOidcIssuerLocation(issuerUri);
+
+        OAuth2TokenValidator<Jwt> audienceValidator = new AudienceValidator(jHipsterProperties.getSecurity().getOauth2().getAudience());
+        OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuerUri);
+        OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer, audienceValidator);
+
+        jwtDecoder.setJwtValidator(withAudience);
+
+        return jwtDecoder;
+    }
+}
diff --git a/src/main/java/tech/jhipster/controlcenter/config/WebConfigurer.java b/src/main/java/tech/jhipster/controlcenter/config/WebConfigurer.java
index 90a0b8c..2f5ded4 100644
--- a/src/main/java/tech/jhipster/controlcenter/config/WebConfigurer.java
+++ b/src/main/java/tech/jhipster/controlcenter/config/WebConfigurer.java
@@ -46,6 +46,9 @@ public class WebConfigurer implements WebFluxConfigurer {
             source.registerCorsConfiguration("/api/**", config);
             source.registerCorsConfiguration("/management/**", config);
             source.registerCorsConfiguration("/v2/api-docs", config);
+            source.registerCorsConfiguration("/*/api/**", config);
+            source.registerCorsConfiguration("/services/*/api/**", config);
+            source.registerCorsConfiguration("/*/management/**", config);
         }
         return new CorsWebFilter(source);
     }
diff --git a/src/main/java/tech/jhipster/controlcenter/config/apidoc/SwaggerConfiguration.java b/src/main/java/tech/jhipster/controlcenter/config/apidoc/SwaggerConfiguration.java
index d2a61d0..dd5f77c 100644
--- a/src/main/java/tech/jhipster/controlcenter/config/apidoc/SwaggerConfiguration.java
+++ b/src/main/java/tech/jhipster/controlcenter/config/apidoc/SwaggerConfiguration.java
@@ -3,7 +3,6 @@ package tech.jhipster.controlcenter.config.apidoc;
 import io.github.jhipster.config.JHipsterConstants;
 import java.util.ArrayList;
 import java.util.List;
-import org.springframework.cloud.gateway.route.Route;
 import org.springframework.cloud.gateway.route.RouteLocator;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
@@ -44,16 +43,22 @@ public class SwaggerConfiguration implements SwaggerResourcesProvider {
         List<SwaggerResource> resources = new ArrayList<>();
         resources.add(swaggerResource("jhipster-control-center", "/v2/api-docs"));
         //Add the registered microservices swagger docs as additional swagger resources
-        List<Route> routes = routeLocator.getRoutes().collectList().block();
-        routes.forEach(
-            route -> {
-                String predicate = route.getPredicate().toString();
-                if (!predicate.contains("consul")) {
-                    String path = predicate.substring(predicate.indexOf("[") + 1, predicate.indexOf("]"));
-                    resources.add(swaggerResource(route.getId(), path.replace("**", "v2/api-docs")));
+        routeLocator
+            .getRoutes()
+            .collectList()
+            .subscribe(
+                routes -> {
+                    routes.forEach(
+                        route -> {
+                            String predicate = route.getPredicate().toString();
+                            if (!predicate.contains("consul")) {
+                                String path = predicate.substring(predicate.indexOf("[") + 1, predicate.indexOf("]"));
+                                resources.add(swaggerResource(route.getId(), path.replace("**", "v2/api-docs")));
+                            }
+                        }
+                    );
                 }
-            }
-        );
+            );
         return resources;
     }
 
diff --git a/src/main/java/tech/jhipster/controlcenter/security/SecurityUtils.java b/src/main/java/tech/jhipster/controlcenter/security/SecurityUtils.java
index 1349b34..84ed0ca 100644
--- a/src/main/java/tech/jhipster/controlcenter/security/SecurityUtils.java
+++ b/src/main/java/tech/jhipster/controlcenter/security/SecurityUtils.java
@@ -1,10 +1,15 @@
 package tech.jhipster.controlcenter.security;
 
+import java.util.*;
+import java.util.stream.Collectors;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
 import org.springframework.security.core.context.ReactiveSecurityContextHolder;
 import org.springframework.security.core.context.SecurityContext;
 import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
+import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
 import reactor.core.publisher.Mono;
 
 /**
@@ -32,6 +37,13 @@ public final class SecurityUtils {
         } else if (authentication.getPrincipal() instanceof UserDetails) {
             UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal();
             return springSecurityUser.getUsername();
+        } else if (authentication instanceof JwtAuthenticationToken) {
+            return (String) ((JwtAuthenticationToken) authentication).getToken().getClaims().get("preferred_username");
+        } else if (authentication.getPrincipal() instanceof DefaultOidcUser) {
+            Map<String, Object> attributes = ((DefaultOidcUser) authentication.getPrincipal()).getAttributes();
+            if (attributes.containsKey("preferred_username")) {
+                return (String) attributes.get("preferred_username");
+            }
         } else if (authentication.getPrincipal() instanceof String) {
             return (String) authentication.getPrincipal();
         }
@@ -79,4 +91,17 @@ public final class SecurityUtils {
             .map(Authentication::getAuthorities)
             .map(authorities -> authorities.stream().map(GrantedAuthority::getAuthority).anyMatch(authority::equals));
     }
+
+    public static List<GrantedAuthority> extractAuthorityFromClaims(Map<String, Object> claims) {
+        return mapRolesToGrantedAuthorities(getRolesFromClaims(claims));
+    }
+
+    @SuppressWarnings("unchecked")
+    private static Collection<String> getRolesFromClaims(Map<String, Object> claims) {
+        return (Collection<String>) claims.getOrDefault("groups", claims.getOrDefault("roles", new ArrayList<>()));
+    }
+
+    private static List<GrantedAuthority> mapRolesToGrantedAuthorities(Collection<String> roles) {
+        return roles.stream().filter(role -> role.startsWith("ROLE_")).map(SimpleGrantedAuthority::new).collect(Collectors.toList());
+    }
 }
diff --git a/src/main/java/tech/jhipster/controlcenter/security/jwt/JWTRelayGatewayFilterFactory.java b/src/main/java/tech/jhipster/controlcenter/security/jwt/JWTRelayGatewayFilterFactory.java
index 874c1e3..3e0c431 100644
--- a/src/main/java/tech/jhipster/controlcenter/security/jwt/JWTRelayGatewayFilterFactory.java
+++ b/src/main/java/tech/jhipster/controlcenter/security/jwt/JWTRelayGatewayFilterFactory.java
@@ -7,7 +7,6 @@ import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFac
 import org.springframework.http.server.reactive.ServerHttpRequest;
 import org.springframework.stereotype.Component;
 import org.springframework.util.StringUtils;
-import tech.jhipster.controlcenter.security.jwt.TokenProvider;
 
 @Component
 public class JWTRelayGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> {
diff --git a/src/main/java/tech/jhipster/controlcenter/security/oauth2/AudienceValidator.java b/src/main/java/tech/jhipster/controlcenter/security/oauth2/AudienceValidator.java
new file mode 100644
index 0000000..bfe970a
--- /dev/null
+++ b/src/main/java/tech/jhipster/controlcenter/security/oauth2/AudienceValidator.java
@@ -0,0 +1,32 @@
+package tech.jhipster.controlcenter.security.oauth2;
+
+import java.util.List;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.oauth2.core.OAuth2Error;
+import org.springframework.security.oauth2.core.OAuth2TokenValidator;
+import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
+import org.springframework.security.oauth2.jwt.Jwt;
+import org.springframework.util.Assert;
+
+public class AudienceValidator implements OAuth2TokenValidator<Jwt> {
+    private final Logger log = LoggerFactory.getLogger(AudienceValidator.class);
+    private final OAuth2Error error = new OAuth2Error("invalid_token", "The required audience is missing", null);
+
+    private final List<String> allowedAudience;
+
+    public AudienceValidator(List<String> allowedAudience) {
+        Assert.notEmpty(allowedAudience, "Allowed audience should not be null or empty.");
+        this.allowedAudience = allowedAudience;
+    }
+
+    public OAuth2TokenValidatorResult validate(Jwt jwt) {
+        List<String> audience = jwt.getAudience();
+        if (audience.stream().anyMatch(allowedAudience::contains)) {
+            return OAuth2TokenValidatorResult.success();
+        } else {
+            log.warn("Invalid audience: {}", audience);
+            return OAuth2TokenValidatorResult.failure(error);
+        }
+    }
+}
diff --git a/src/main/java/tech/jhipster/controlcenter/security/oauth2/JwtGrantedAuthorityConverter.java b/src/main/java/tech/jhipster/controlcenter/security/oauth2/JwtGrantedAuthorityConverter.java
new file mode 100644
index 0000000..22629e1
--- /dev/null
+++ b/src/main/java/tech/jhipster/controlcenter/security/oauth2/JwtGrantedAuthorityConverter.java
@@ -0,0 +1,21 @@
+package tech.jhipster.controlcenter.security.oauth2;
+
+import java.util.Collection;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.oauth2.jwt.Jwt;
+import org.springframework.stereotype.Component;
+import tech.jhipster.controlcenter.security.SecurityUtils;
+
+@Component
+public class JwtGrantedAuthorityConverter implements Converter<Jwt, Collection<GrantedAuthority>> {
+
+    public JwtGrantedAuthorityConverter() {
+        // Bean extracting authority.
+    }
+
+    @Override
+    public Collection<GrantedAuthority> convert(Jwt jwt) {
+        return SecurityUtils.extractAuthorityFromClaims(jwt.getClaims());
+    }
+}
diff --git a/src/main/java/tech/jhipster/controlcenter/security/oauth2/TokenRelayWithTokenRefreshGatewayFilterFactory.java b/src/main/java/tech/jhipster/controlcenter/security/oauth2/TokenRelayWithTokenRefreshGatewayFilterFactory.java
new file mode 100644
index 0000000..13f8ea4
--- /dev/null
+++ b/src/main/java/tech/jhipster/controlcenter/security/oauth2/TokenRelayWithTokenRefreshGatewayFilterFactory.java
@@ -0,0 +1,97 @@
+package tech.jhipster.controlcenter.security.oauth2;
+
+import java.time.Duration;
+import org.springframework.cloud.gateway.filter.GatewayFilter;
+import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
+import org.springframework.context.annotation.Profile;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;
+import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
+import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientManager;
+import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientProvider;
+import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientProviderBuilder;
+import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
+import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
+import org.springframework.security.oauth2.client.web.DefaultReactiveOAuth2AuthorizedClientManager;
+import org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction;
+import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;
+import org.springframework.security.oauth2.core.OAuth2AccessToken;
+import org.springframework.stereotype.Component;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+import tech.jhipster.controlcenter.config.Constants;
+
+/**
+ * Token Relay Gateway Filter with Token Refresh. This can be removed when issue {@see https://github.com/spring-cloud/spring-cloud-security/issues/175} is closed.
+ * Implementierung in Anlehnung an {@link ServerOAuth2AuthorizedClientExchangeFilterFunction}
+ */
+@Component
+@Profile(Constants.PROFILE_OAUTH2)
+public class TokenRelayWithTokenRefreshGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> {
+    private final ReactiveOAuth2AuthorizedClientManager authorizedClientManager;
+
+    private static final Duration accessTokenExpiresSkew = Duration.ofSeconds(3);
+
+    public TokenRelayWithTokenRefreshGatewayFilterFactory(
+        ServerOAuth2AuthorizedClientRepository authorizedClientRepository,
+        ReactiveClientRegistrationRepository clientRegistrationRepository
+    ) {
+        super(Object.class);
+        this.authorizedClientManager = createDefaultAuthorizedClientManager(clientRegistrationRepository, authorizedClientRepository);
+    }
+
+    private static ReactiveOAuth2AuthorizedClientManager createDefaultAuthorizedClientManager(
+        ReactiveClientRegistrationRepository clientRegistrationRepository,
+        ServerOAuth2AuthorizedClientRepository authorizedClientRepository
+    ) {
+        final ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder
+            .builder()
+            .authorizationCode()
+            .refreshToken(configurer -> configurer.clockSkew(accessTokenExpiresSkew))
+            .clientCredentials(configurer -> configurer.clockSkew(accessTokenExpiresSkew))
+            .password(configurer -> configurer.clockSkew(accessTokenExpiresSkew))
+            .build();
+        final DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager = new DefaultReactiveOAuth2AuthorizedClientManager(
+            clientRegistrationRepository,
+            authorizedClientRepository
+        );
+        authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
+
+        return authorizedClientManager;
+    }
+
+    public GatewayFilter apply() {
+        return apply((Object) null);
+    }
+
+    @Override
+    public GatewayFilter apply(Object config) {
+        return (exchange, chain) ->
+            exchange
+                .getPrincipal()
+                // .log("token-relay-filter")
+                .filter(principal -> principal instanceof OAuth2AuthenticationToken)
+                .cast(OAuth2AuthenticationToken.class)
+                .flatMap(this::authorizeClient)
+                .map(OAuth2AuthorizedClient::getAccessToken)
+                .map(token -> withBearerAuth(exchange, token))
+                // adjustable behavior if empty
+                .defaultIfEmpty(exchange)
+                .flatMap(chain::filter);
+    }
+
+    private ServerWebExchange withBearerAuth(ServerWebExchange exchange, OAuth2AccessToken accessToken) {
+        return exchange.mutate().request(r -> r.headers(headers -> headers.setBearerAuth(accessToken.getTokenValue()))).build();
+    }
+
+    private Mono<OAuth2AuthorizedClient> authorizeClient(OAuth2AuthenticationToken oAuth2AuthenticationToken) {
+        final String clientRegistrationId = oAuth2AuthenticationToken.getAuthorizedClientRegistrationId();
+        return Mono.defer(
+            () -> authorizedClientManager.authorize(createOAuth2AuthorizeRequest(clientRegistrationId, oAuth2AuthenticationToken))
+        );
+    }
+
+    private OAuth2AuthorizeRequest createOAuth2AuthorizeRequest(String clientRegistrationId, Authentication principal) {
+        return OAuth2AuthorizeRequest.withClientRegistrationId(clientRegistrationId).principal(principal).build();
+    }
+}
diff --git a/src/main/java/tech/jhipster/controlcenter/web/filter/SpaWebFilter.java b/src/main/java/tech/jhipster/controlcenter/web/filter/SpaWebFilter.java
index 7331212..e4b8a37 100644
--- a/src/main/java/tech/jhipster/controlcenter/web/filter/SpaWebFilter.java
+++ b/src/main/java/tech/jhipster/controlcenter/web/filter/SpaWebFilter.java
@@ -19,6 +19,7 @@ public class SpaWebFilter implements WebFilter {
             !path.startsWith("/api") &&
             !path.startsWith("/management") &&
             // jhcc-custom
+            !path.startsWith("/login") &&
             !path.startsWith("/gateway") &&
             !path.startsWith("/services") &&
             !path.startsWith("/swagger") &&
diff --git a/src/main/java/tech/jhipster/controlcenter/web/rest/AccountResource.java b/src/main/java/tech/jhipster/controlcenter/web/rest/AccountResource.java
index 88efa38..7d88db8 100644
--- a/src/main/java/tech/jhipster/controlcenter/web/rest/AccountResource.java
+++ b/src/main/java/tech/jhipster/controlcenter/web/rest/AccountResource.java
@@ -4,9 +4,11 @@ import com.fasterxml.jackson.annotation.JsonCreator;
 import java.util.Set;
 import java.util.stream.Collectors;
 import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.annotation.AuthenticationPrincipal;
 import org.springframework.security.core.context.ReactiveSecurityContextHolder;
 import org.springframework.security.core.context.SecurityContext;
 import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.oauth2.core.oidc.user.OidcUser;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
@@ -25,29 +27,35 @@ public class AccountResource {
      * @throws AccountResourceException {@code 500 (Internal Server Error)} if the user couldn't be returned.
      */
     @GetMapping("/account")
-    public Mono<UserVM> getAccount() {
-        return ReactiveSecurityContextHolder
-            .getContext()
-            .map(SecurityContext::getAuthentication)
-            .map(
-                authentication -> {
-                    String login;
-                    if (authentication.getPrincipal() instanceof UserDetails) {
-                        login = ((UserDetails) authentication.getPrincipal()).getUsername();
-                    } else if (authentication.getPrincipal() instanceof String) {
-                        login = (String) authentication.getPrincipal();
-                    } else {
-                        throw new AccountResourceException();
+    public Mono<UserVM> getAccount(@AuthenticationPrincipal OidcUser user) {
+        if (user == null) {
+            return ReactiveSecurityContextHolder
+                .getContext()
+                .map(SecurityContext::getAuthentication)
+                .map(
+                    authentication -> {
+                        String login;
+                        if (authentication.getPrincipal() instanceof UserDetails) {
+                            login = ((UserDetails) authentication.getPrincipal()).getUsername();
+                        } else if (authentication.getPrincipal() instanceof String) {
+                            login = (String) authentication.getPrincipal();
+                        } else {
+                            throw new AccountResourceException();
+                        }
+                        Set<String> authorities = authentication
+                            .getAuthorities()
+                            .stream()
+                            .map(GrantedAuthority::getAuthority)
+                            .collect(Collectors.toSet());
+                        return new UserVM(login, authorities);
                     }
-                    Set<String> authorities = authentication
-                        .getAuthorities()
-                        .stream()
-                        .map(GrantedAuthority::getAuthority)
-                        .collect(Collectors.toSet());
-                    return new UserVM(login, authorities);
-                }
-            )
-            .switchIfEmpty(Mono.error(new AccountResourceException()));
+                )
+                .switchIfEmpty(Mono.error(new AccountResourceException()));
+        } else {
+            String login = user.getName();
+            Set<String> authorities = user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toSet());
+            return Mono.just(new UserVM(login, authorities));
+        }
     }
 
     private static class UserVM {
diff --git a/src/main/java/tech/jhipster/controlcenter/web/rest/AuthInfoResource.java b/src/main/java/tech/jhipster/controlcenter/web/rest/AuthInfoResource.java
new file mode 100644
index 0000000..9d4d66f
--- /dev/null
+++ b/src/main/java/tech/jhipster/controlcenter/web/rest/AuthInfoResource.java
@@ -0,0 +1,50 @@
+package tech.jhipster.controlcenter.web.rest;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * Resource to return information about OIDC properties
+ */
+@RestController
+@RequestMapping("/api")
+public class AuthInfoResource {
+    @Value("${spring.security.oauth2.client.provider.oidc.issuer-uri:}")
+    private String issuer;
+
+    @Value("${spring.security.oauth2.client.registration.oidc.client-id:}")
+    private String clientId;
+
+    @GetMapping("/auth-info")
+    public AuthInfoVM getAuthInfo() {
+        return new AuthInfoVM(issuer, clientId);
+    }
+
+    class AuthInfoVM {
+        private String issuer;
+        private String clientId;
+
+        AuthInfoVM(String issuer, String clientId) {
+            this.issuer = issuer;
+            this.clientId = clientId;
+        }
+
+        public String getIssuer() {
+            return this.issuer;
+        }
+
+        public void setIssuer(String issuer) {
+            this.issuer = issuer;
+        }
+
+        public String getClientId() {
+            return clientId;
+        }
+
+        public void setClientId(String clientId) {
+            this.clientId = clientId;
+        }
+    }
+}
diff --git a/src/main/java/tech/jhipster/controlcenter/web/rest/LogoutResource.java b/src/main/java/tech/jhipster/controlcenter/web/rest/LogoutResource.java
new file mode 100644
index 0000000..b543c24
--- /dev/null
+++ b/src/main/java/tech/jhipster/controlcenter/web/rest/LogoutResource.java
@@ -0,0 +1,51 @@
+package tech.jhipster.controlcenter.web.rest;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.springframework.context.annotation.Profile;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.core.annotation.AuthenticationPrincipal;
+import org.springframework.security.oauth2.client.registration.ClientRegistration;
+import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
+import org.springframework.security.oauth2.core.oidc.OidcIdToken;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.server.WebSession;
+import reactor.core.publisher.Mono;
+import tech.jhipster.controlcenter.config.Constants;
+
+/**
+ * REST controller for managing global OIDC logout.
+ */
+@RestController
+@Profile(Constants.PROFILE_OAUTH2)
+public class LogoutResource {
+    private final Mono<ClientRegistration> registration;
+
+    public LogoutResource(ReactiveClientRegistrationRepository registrations) {
+        this.registration = registrations.findByRegistrationId("oidc");
+    }
+
+    /**
+     * {@code POST  /api/logout} : logout the current user.
+     *
+     * @param idToken the ID token.
+     * @return the {@link ResponseEntity} with status {@code 200 (OK)} and a body with a global logout URL and ID token.
+     */
+    @PostMapping("/api/logout")
+    public Mono<Map<String, String>> logout(@AuthenticationPrincipal(expression = "idToken") OidcIdToken idToken, WebSession session) {
+        return session
+            .invalidate()
+            .then(
+                this.registration.map(oidc -> oidc.getProviderDetails().getConfigurationMetadata().get("end_session_endpoint").toString())
+                    .map(
+                        logoutUrl -> {
+                            Map<String, String> logoutDetails = new HashMap<>();
+                            logoutDetails.put("logoutUrl", logoutUrl);
+                            logoutDetails.put("idToken", idToken.getTokenValue());
+                            return logoutDetails;
+                        }
+                    )
+            );
+    }
+}
diff --git a/src/main/java/tech/jhipster/controlcenter/web/rest/UserJWTController.java b/src/main/java/tech/jhipster/controlcenter/web/rest/UserJWTController.java
index fa8d5f7..e8d4307 100644
--- a/src/main/java/tech/jhipster/controlcenter/web/rest/UserJWTController.java
+++ b/src/main/java/tech/jhipster/controlcenter/web/rest/UserJWTController.java
@@ -2,6 +2,7 @@ package tech.jhipster.controlcenter.web.rest;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
 import javax.validation.Valid;
+import org.springframework.context.annotation.Profile;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
@@ -10,6 +11,7 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio
 import org.springframework.web.bind.annotation.*;
 import reactor.core.publisher.Mono;
 import reactor.core.scheduler.Schedulers;
+import tech.jhipster.controlcenter.config.Constants;
 import tech.jhipster.controlcenter.security.jwt.JWTFilter;
 import tech.jhipster.controlcenter.security.jwt.TokenProvider;
 import tech.jhipster.controlcenter.web.rest.vm.LoginVM;
@@ -19,6 +21,7 @@ import tech.jhipster.controlcenter.web.rest.vm.LoginVM;
  */
 @RestController
 @RequestMapping("/api")
+@Profile("!" + Constants.PROFILE_OAUTH2)
 public class UserJWTController {
     private final TokenProvider tokenProvider;
 
diff --git a/src/main/resources/config/application-oauth2.yml b/src/main/resources/config/application-oauth2.yml
new file mode 100644
index 0000000..a0451ec
--- /dev/null
+++ b/src/main/resources/config/application-oauth2.yml
@@ -0,0 +1,18 @@
+spring:
+  security:
+    oauth2:
+      client:
+        provider:
+          oidc:
+            issuer-uri: http://localhost:9080/auth/realms/jhipster
+        registration:
+          oidc:
+            client-id: web_app
+            client-secret: web_app
+      resourceserver:
+        jwt:
+          issuer-uri: http://localhost:8090/auth/realms/jhipster
+  cloud:
+    gateway:
+      default-filters:
+        - TokenRelayWithTokenRefresh
diff --git a/src/main/resources/config/application.yml b/src/main/resources/config/application.yml
index 79f9978..2869745 100644
--- a/src/main/resources/config/application.yml
+++ b/src/main/resources/config/application.yml
@@ -147,6 +147,11 @@ jhipster:
     contact-email:
     license:
     license-url:
+  security:
+    oauth2:
+      audience:
+        - account
+        - api://default
 
 # jhcc-custom
 eureka:
diff --git a/src/main/webapp/app/account/account.service.ts b/src/main/webapp/app/account/account.service.ts
index 14aab31..1582561 100644
--- a/src/main/webapp/app/account/account.service.ts
+++ b/src/main/webapp/app/account/account.service.ts
@@ -3,7 +3,7 @@ import { Store } from 'vuex';
 import VueRouter from 'vue-router';
 
 export default class AccountService {
-  constructor(private store: Store<any>, private router: VueRouter) {
+  constructor(private store: Store<any>, private cookie: any, private router: VueRouter) {
     this.init();
   }
 
@@ -40,7 +40,8 @@ export default class AccountService {
           }
           resolve(true);
         })
-        .catch(() => {
+        .catch(error => {
+          console.log(error);
           this.store.commit('logout');
           resolve(false);
         });
@@ -53,7 +54,12 @@ export default class AccountService {
     }
 
     if (!this.authenticated || !this.userAuthorities) {
-      const token = localStorage.getItem('jhi-authenticationToken') || sessionStorage.getItem('jhi-authenticationToken');
+      const token =
+        this.cookie.get('JSESSIONID_CONTROLCENTER') ||
+        this.cookie.get('XSRF-TOKEN') ||
+        localStorage.getItem('jhi-authenticationToken') ||
+        sessionStorage.getItem('jhi-authenticationToken');
+
       if (!this.store.getters.account && !this.store.getters.logon && token) {
         return this.retrieveAccount();
       } else {
diff --git a/src/main/webapp/app/account/login.service.ts b/src/main/webapp/app/account/login.service.ts
index aae1900..f9e3a22 100644
--- a/src/main/webapp/app/account/login.service.ts
+++ b/src/main/webapp/app/account/login.service.ts
@@ -1,8 +1,36 @@
 import Vue from 'vue';
 import axios, { AxiosPromise } from 'axios';
+import { SERVER_API_URL } from '@/constants';
 
 export default class LoginService {
   public openLogin(instance: Vue): void {
     instance.$emit('bv::show::modal', 'login-page');
   }
+
+  public login(loc = window.location) {
+    let port = location.port ? ':' + location.port : '';
+    if (port === ':9000') {
+      port = ':7419';
+    }
+
+    let contextPath = location.pathname;
+    if (contextPath.endsWith('accessdenied')) {
+      contextPath = contextPath.substring(0, contextPath.indexOf('accessdenied'));
+    }
+    if (!contextPath.endsWith('/')) {
+      contextPath = contextPath + '/';
+    }
+    // If you have configured multiple OIDC providers, then, you can update this URL to /login.
+    // It will show a Spring Security generated login page with links to configured OIDC providers.
+    loc.href = `//${loc.hostname}${port}${contextPath}oauth2/authorization/oidc`;
+  }
+
+  public logout(): AxiosPromise<any> {
+    return axios.post('api/logout');
+  }
+
+  public getProfileInfo(): AxiosPromise<string[]> {
+    const infoUrl = SERVER_API_URL + 'management/info';
+    return axios.get(infoUrl);
+  }
 }
diff --git a/src/main/webapp/app/core/home/home.component.ts b/src/main/webapp/app/core/home/home.component.ts
index 10fa7d5..4b3fc28 100644
--- a/src/main/webapp/app/core/home/home.component.ts
+++ b/src/main/webapp/app/core/home/home.component.ts
@@ -7,8 +7,19 @@ export default class Home extends Vue {
   @Inject('loginService')
   private loginService: () => LoginService;
 
+  // jhcc-custom
+  /* istanbul ignore next */
   public openLogin(): void {
-    this.loginService().openLogin((<any>this).$root);
+    this.loginService()
+      .getProfileInfo()
+      .then(response => {
+        const profiles: string[] = response.data['activeProfiles'];
+        if (profiles.includes('oauth2')) {
+          this.loginService().login();
+        } else {
+          this.loginService().openLogin((<any>this).$root);
+        }
+      });
   }
 
   public get authenticated(): boolean {
diff --git a/src/main/webapp/app/core/jhi-navbar/jhi-navbar.component.ts b/src/main/webapp/app/core/jhi-navbar/jhi-navbar.component.ts
index 317dc99..06fdb37 100644
--- a/src/main/webapp/app/core/jhi-navbar/jhi-navbar.component.ts
+++ b/src/main/webapp/app/core/jhi-navbar/jhi-navbar.component.ts
@@ -2,6 +2,7 @@ import { Component, Inject, Vue } from 'vue-property-decorator';
 import { VERSION } from '@/constants';
 import LoginService from '@/account/login.service';
 import AccountService from '@/account/account.service';
+import { profile } from 'console';
 
 @Component
 export default class JhiNavbar extends Vue {
@@ -23,15 +24,50 @@ export default class JhiNavbar extends Vue {
     });
   }
 
+  // jhcc-custom
+  /* istanbul ignore next */
   public logout(): void {
-    localStorage.removeItem('jhi-authenticationToken');
-    sessionStorage.removeItem('jhi-authenticationToken');
-    this.$store.commit('logout');
-    this.$router.push('/');
+    this.loginService()
+      .getProfileInfo()
+      .then(profileInfo => {
+        const profiles: string[] = profileInfo.data['activeProfiles'];
+        if (profiles.includes('oauth2')) {
+          this.loginService()
+            .logout()
+            .then(response => {
+              this.$store.commit('logout');
+              this.$router.push('/');
+              const data = response.data;
+              let logoutUrl = data.logoutUrl;
+              // if Keycloak, uri has protocol/openid-connect/token
+              if (logoutUrl.indexOf('/protocol') > -1) {
+                logoutUrl = logoutUrl + '?redirect_uri=' + window.location.origin;
+              } else {
+                // Okta
+                logoutUrl = logoutUrl + '?id_token_hint=' + data.idToken + '&post_logout_redirect_uri=' + window.location.origin;
+              }
+              window.location.href = logoutUrl;
+            });
+        } else {
+          localStorage.removeItem('jhi-authenticationToken');
+          sessionStorage.removeItem('jhi-authenticationToken');
+          this.$store.commit('logout');
+          this.$router.push('/');
+        }
+      });
   }
 
   public openLogin(): void {
-    this.loginService().openLogin((<any>this).$root);
+    this.loginService()
+      .getProfileInfo()
+      .then(response => {
+        const profiles: string[] = response.data['activeProfiles'];
+        if (profiles.includes('oauth2')) {
+          this.loginService().login();
+        } else {
+          this.loginService().openLogin((<any>this).$root);
+        }
+      });
   }
 
   public get authenticated(): boolean {
diff --git a/src/main/webapp/app/main.ts b/src/main/webapp/app/main.ts
index a23c079..2e0f417 100644
--- a/src/main/webapp/app/main.ts
+++ b/src/main/webapp/app/main.ts
@@ -55,7 +55,8 @@ const store = config.initVueXStore(Vue);
 
 const alertService = new AlertService(store);
 const loginService = new LoginService();
-const accountService = new AccountService(store, router);
+const accountService = new AccountService(store, (<any>Vue).cookie, router);
+
 // jhcc-custom begin
 const refreshService = new RefreshService(store);
 const routesService = new RoutesService(store);
diff --git a/src/main/webapp/swagger-ui/index.html b/src/main/webapp/swagger-ui/index.html
index 0b5ddce..c9c078d 100644
--- a/src/main/webapp/swagger-ui/index.html
+++ b/src/main/webapp/swagger-ui/index.html
@@ -17,7 +17,16 @@
 
 
 <script type="text/javascript">
-
+    function getCSRF() {
+        var name = "XSRF-TOKEN=";
+        var ca = document.cookie.split(';');
+        for(var i=0; i<ca.length; i++) {
+            var c = ca[i];
+            while (c.charAt(0) === ' ') c = c.substring(1);
+            if (c.indexOf(name) !== -1) return c.substring(name.length,c.length);
+        }
+        return "";
+    }
 
     window.onload = function () {
         var urls = [];
@@ -41,13 +50,27 @@
                 plugins: [
                     SwaggerUIBundle.plugins.DownloadUrl
                 ],
-                requestInterceptor: function(req) {
-                    var authToken = localStorage.getItem("jhi-authenticationToken")
-                        || sessionStorage.getItem("jhi-authenticationToken");
-                    if (authToken) {
-                        req.headers['Authorization'] = "Bearer " + authToken;
-                    }
-                    return req;
+                requestInterceptor: function (req) {
+                    return axios.get("/management/info").then(function(response) {
+                        var oauth2 = false;
+                        response.data.activeProfiles.forEach(function (activeProfile) {
+                            if (activeProfile === 'oauth2') {
+                                oauth2 = true;
+                            }
+                        });
+                        if (oauth2) {
+                            // OAuth2
+                            req.headers['X-XSRF-TOKEN'] = getCSRF();
+                        } else {
+                            // working only with JWT authentication
+                            var authToken = JSON.parse(localStorage.getItem("jhi-authenticationtoken")
+                                || sessionStorage.getItem("jhi-authenticationtoken"));
+                            if (authToken) {
+                                req.headers['Authorization'] = "Bearer " + authToken;
+                            }
+                        }
+                        return req;
+                    });
                 }
             });
 
diff --git a/webpack/webpack.dev.js b/webpack/webpack.dev.js
index 8d76442..978f919 100644
--- a/webpack/webpack.dev.js
+++ b/webpack/webpack.dev.js
@@ -33,7 +33,18 @@ module.exports = merge(baseWebpackConfig, {
     port: 9060,
     proxy: [
       {
-        context: ['/api', '/services', '/management', '/gateway', '/swagger-resources', '/v2/api-docs', '/h2-console', '/auth'],
+        context: [
+          '/api',
+          '/services',
+          '/management',
+          '/gateway',
+          '/swagger-resources',
+          '/v2/api-docs',
+          '/h2-console',
+          '/oauth2',
+          '/login',
+          '/auth',
+        ],
         target: 'http://127.0.0.1:7419',
         secure: false,
         headers: { host: 'localhost:9000' },


Test patch:
diff --git a/src/test/java/tech/jhipster/controlcenter/config/Oauth2SecurityConfigurationTest.java b/src/test/java/tech/jhipster/controlcenter/config/Oauth2SecurityConfigurationTest.java
new file mode 100644
index 0000000..fa4cceb
--- /dev/null
+++ b/src/test/java/tech/jhipster/controlcenter/config/Oauth2SecurityConfigurationTest.java
@@ -0,0 +1,74 @@
+package tech.jhipster.controlcenter.config;
+
+import static org.mockito.Mockito.mock;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.springframework.boot.test.context.TestConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.security.oauth2.client.InMemoryReactiveOAuth2AuthorizedClientService;
+import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
+import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientService;
+import org.springframework.security.oauth2.client.registration.ClientRegistration;
+import org.springframework.security.oauth2.client.registration.InMemoryReactiveClientRegistrationRepository;
+import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
+import org.springframework.security.oauth2.client.web.AuthenticatedPrincipalOAuth2AuthorizedClientRepository;
+import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
+import org.springframework.security.oauth2.core.AuthorizationGrantType;
+import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
+import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
+
+/**
+ * This class allows you to run unit and integration tests without an IdP.
+ */
+@TestConfiguration
+public class Oauth2SecurityConfigurationTest {
+    private final ClientRegistration clientRegistration;
+
+    public Oauth2SecurityConfigurationTest() {
+        this.clientRegistration = clientRegistration().build();
+    }
+
+    @Bean
+    ReactiveClientRegistrationRepository clientRegistrationRepository() {
+        return new InMemoryReactiveClientRegistrationRepository(clientRegistration);
+    }
+
+    private ClientRegistration.Builder clientRegistration() {
+        Map<String, Object> metadata = new HashMap<>();
+        metadata.put("end_session_endpoint", "https://jhipster.org/logout");
+
+        return ClientRegistration
+            .withRegistrationId("oidc")
+            .redirectUriTemplate("{baseUrl}/{action}/oauth2/code/{registrationId}")
+            .clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
+            .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
+            .scope("read:user")
+            .authorizationUri("https://jhipster.org/login/oauth/authorize")
+            .tokenUri("https://jhipster.org/login/oauth/access_token")
+            .jwkSetUri("https://jhipster.org/oauth/jwk")
+            .userInfoUri("https://api.jhipster.org/user")
+            .providerConfigurationMetadata(metadata)
+            .userNameAttributeName("id")
+            .clientName("Client Name")
+            .clientId("client-id")
+            .clientSecret("client-secret");
+    }
+
+    @Bean
+    ReactiveJwtDecoder jwtDecoder() {
+        return mock(ReactiveJwtDecoder.class);
+    }
+
+    @Bean
+    public ReactiveOAuth2AuthorizedClientService authorizedClientService(
+        ReactiveClientRegistrationRepository clientRegistrationRepository
+    ) {
+        return new InMemoryReactiveOAuth2AuthorizedClientService(clientRegistrationRepository);
+    }
+
+    @Bean
+    public OAuth2AuthorizedClientRepository authorizedClientRepository(OAuth2AuthorizedClientService authorizedClientService) {
+        return new AuthenticatedPrincipalOAuth2AuthorizedClientRepository(authorizedClientService);
+    }
+}
diff --git a/src/test/java/tech/jhipster/controlcenter/security/oauth2/AudienceValidatorTest.java b/src/test/java/tech/jhipster/controlcenter/security/oauth2/AudienceValidatorTest.java
new file mode 100644
index 0000000..2f813ce
--- /dev/null
+++ b/src/test/java/tech/jhipster/controlcenter/security/oauth2/AudienceValidatorTest.java
@@ -0,0 +1,39 @@
+package tech.jhipster.controlcenter.security.oauth2;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.jupiter.api.Test;
+import org.springframework.security.oauth2.jwt.Jwt;
+
+/**
+ * Test class for the {@link AudienceValidator} utility class.
+ */
+public class AudienceValidatorTest {
+    private final AudienceValidator validator = new AudienceValidator(Arrays.asList("api://default"));
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testInvalidAudience() {
+        Map<String, Object> claims = new HashMap<>();
+        claims.put("aud", "bar");
+        Jwt badJwt = mock(Jwt.class);
+        when(badJwt.getAudience()).thenReturn(new ArrayList(claims.values()));
+        assertThat(validator.validate(badJwt).hasErrors()).isTrue();
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testValidAudience() {
+        Map<String, Object> claims = new HashMap<>();
+        claims.put("aud", "api://default");
+        Jwt jwt = mock(Jwt.class);
+        when(jwt.getAudience()).thenReturn(new ArrayList(claims.values()));
+        assertThat(validator.validate(jwt).hasErrors()).isFalse();
+    }
+}
diff --git a/src/test/javascript/spec/app/account/account.service.spec.ts b/src/test/javascript/spec/app/account/account.service.spec.ts
index a12fe34..125bd71 100644
--- a/src/test/javascript/spec/app/account/account.service.spec.ts
+++ b/src/test/javascript/spec/app/account/account.service.spec.ts
@@ -10,6 +10,11 @@ jest.mock('axios', () => ({
   get: jest.fn(),
   post: jest.fn(),
 }));
+const mockedCookie = {
+  get: jest.fn(() => {
+    return 'token';
+  }),
+};
 
 const localVue = createLocalVue();
 
@@ -26,7 +31,7 @@ describe('Account Service test suite', () => {
   it('should init service and do not retrieve account', async () => {
     mockedAxios.get.mockReturnValue(Promise.resolve({ data: { 'display-ribbon-on-profiles': 'dev', activeProfiles: ['dev', 'test'] } }));
 
-    accountService = await new AccountService(store, router);
+    accountService = await new AccountService(store, mockedCookie, router);
 
     expect(store.getters.logon).toBe(false);
     expect(accountService.authenticated).toBe(false);
@@ -41,7 +46,7 @@ describe('Account Service test suite', () => {
     localStorage.setItem('jhi-authenticationToken', 'token');
 
     mockedAxios.get.mockReturnValue(Promise.resolve({}));
-    accountService = await new AccountService(store, router);
+    accountService = await new AccountService(store, mockedCookie, router);
 
     expect((<any>router).history.current.fullPath).toBe('/');
     expect(store.getters.logon).toBe(false);
@@ -54,7 +59,7 @@ describe('Account Service test suite', () => {
     localStorage.setItem('jhi-authenticationToken', 'token');
 
     mockedAxios.get = jest.fn(apiName => (apiName === 'api/account' ? Promise.reject() : Promise.resolve({})));
-    accountService = await new AccountService(store, router);
+    accountService = await new AccountService(store, mockedCookie, router);
 
     expect((<any>router).history.current.fullPath).toBe('/');
     expect(accountService.authenticated).toBe(false);
@@ -66,7 +71,7 @@ describe('Account Service test suite', () => {
     localStorage.setItem('jhi-authenticationToken', 'token');
 
     mockedAxios.get = jest.fn(apiName => Promise.reject());
-    accountService = await new AccountService(store, router);
+    accountService = await new AccountService(store, mockedCookie, router);
 
     return accountService.hasAnyAuthorityAndCheckAuth('USER').then((value: boolean) => {
       expect(value).toBe(false);
@@ -77,7 +82,7 @@ describe('Account Service test suite', () => {
     localStorage.setItem('jhi-authenticationToken', 'token');
 
     mockedAxios.get.mockReturnValue(Promise.resolve({ data: { authorities: ['USER'] } }));
-    accountService = await new AccountService(store, router);
+    accountService = await new AccountService(store, mockedCookie, router);
 
     return accountService.hasAnyAuthorityAndCheckAuth('USER').then((value: boolean) => {
       expect(value).toBe(true);
@@ -86,7 +91,7 @@ describe('Account Service test suite', () => {
 
   it('should init service as not authentified and not return any authorities admin and not retrieve account', async () => {
     mockedAxios.get = jest.fn(apiName => (apiName === 'api/account' ? Promise.reject() : Promise.resolve({})));
-    accountService = await new AccountService(store, router);
+    accountService = await new AccountService(store, mockedCookie, router);
 
     return accountService.hasAnyAuthorityAndCheckAuth('ADMIN').then((value: boolean) => {
       expect(value).toBe(false);
@@ -95,7 +100,7 @@ describe('Account Service test suite', () => {
 
   it('should init service as not authentified and return authority user', async () => {
     mockedAxios.get = jest.fn(apiName => (apiName === 'api/account' ? Promise.reject() : Promise.resolve({})));
-    accountService = await new AccountService(store, router);
+    accountService = await new AccountService(store, mockedCookie, router);
 
     return accountService.hasAnyAuthorityAndCheckAuth('USER').then((value: boolean) => {
       expect(value).toBe(true);
diff --git a/src/test/javascript/spec/app/account/login-form/login-form.component.spec.ts b/src/test/javascript/spec/app/account/login-form/login-form.component.spec.ts
index ac67a6a..7c1c5a7 100644
--- a/src/test/javascript/spec/app/account/login-form/login-form.component.spec.ts
+++ b/src/test/javascript/spec/app/account/login-form/login-form.component.spec.ts
@@ -16,6 +16,11 @@ localVue.component('b-form-group', {});
 localVue.component('b-form-checkbox', {});
 localVue.component('b-link', {});
 const mockedAxios: any = axios;
+const mockedCookie = {
+  get: jest.fn(() => {
+    return 'token';
+  }),
+};
 
 config.initVueApp(localVue);
 const store = config.initVueXStore(localVue);
@@ -38,7 +43,7 @@ describe('LoginForm Component', () => {
       store,
       localVue,
       provide: {
-        accountService: () => new AccountService(store, router),
+        accountService: () => new AccountService(store, mockedCookie, router),
       },
     });
     loginForm = wrapper.vm;
diff --git a/src/test/javascript/spec/app/core/error/error.component.spec.ts b/src/test/javascript/spec/app/core/error/error.component.spec.ts
index c065c56..d963f71 100644
--- a/src/test/javascript/spec/app/core/error/error.component.spec.ts
+++ b/src/test/javascript/spec/app/core/error/error.component.spec.ts
@@ -16,7 +16,12 @@ describe('Error component', () => {
   let loginService: LoginService;
 
   beforeEach(() => {
-    loginService = { openLogin: jest.fn() };
+    loginService = {
+      openLogin: jest.fn(),
+      login: jest.fn(),
+      logout: jest.fn(),
+      getProfileInfo: jest.fn(),
+    };
     wrapper = shallowMount<ErrorClass>(Error, {
       store,
       router,
diff --git a/src/test/javascript/spec/app/core/home/home.component.spec.ts b/src/test/javascript/spec/app/core/home/home.component.spec.ts
index 536ce1a..00f0538 100644
--- a/src/test/javascript/spec/app/core/home/home.component.spec.ts
+++ b/src/test/javascript/spec/app/core/home/home.component.spec.ts
@@ -2,16 +2,24 @@ import { createLocalVue, shallowMount, Wrapper } from '@vue/test-utils';
 import Home from '@/core/home/home.vue';
 import HomeClass from '@/core/home/home.component';
 import * as config from '@/shared/config/config';
+import axios from 'axios';
 
 const localVue = createLocalVue();
 config.initVueApp(localVue);
 const store = config.initVueXStore(localVue);
 localVue.component('router-link', {});
 
+// jhcc-custom
+const mockedAxios: any = axios;
+jest.mock('axios', () => ({
+  get: jest.fn(),
+}));
+
 describe('Home', () => {
   let home: HomeClass;
   let wrapper: Wrapper<HomeClass>;
-  const loginService = { openLogin: jest.fn() };
+  // login() and getProfileInfo() are jhcc-custom
+  const loginService = { openLogin: jest.fn(), login: jest.fn(), getProfileInfo: jest.fn() };
 
   beforeEach(() => {
     wrapper = shallowMount<HomeClass>(Home, {
@@ -36,9 +44,19 @@ describe('Home', () => {
     expect(home.username).toBe('test');
   });
 
-  it('should use login service', () => {
+  // jhcc-custom
+  it('should use login service', async () => {
+    const profileInfo = {
+      'display-ribbon-on-profiles': 'dev',
+      activeProfiles: ['dev', 'swagger', 'consul'],
+    };
+    loginService.getProfileInfo.mockReturnValue(Promise.resolve(profileInfo));
+    const spy = jest.spyOn(home, 'openLogin');
+
     home.openLogin();
+    await home.$nextTick();
 
-    expect(loginService.openLogin).toHaveBeenCalled();
+    expect(spy).toHaveBeenCalled();
+    expect(loginService.getProfileInfo).toHaveBeenCalled();
   });
 });
diff --git a/src/test/javascript/spec/app/core/jhi-navbar/jhi-navbar.component.spec.ts b/src/test/javascript/spec/app/core/jhi-navbar/jhi-navbar.component.spec.ts
index 2a2eb5f..bd9cd4a 100644
--- a/src/test/javascript/spec/app/core/jhi-navbar/jhi-navbar.component.spec.ts
+++ b/src/test/javascript/spec/app/core/jhi-navbar/jhi-navbar.component.spec.ts
@@ -3,6 +3,7 @@ import JhiNavbar from '@/core/jhi-navbar/jhi-navbar.vue';
 import JhiNavbarClass from '@/core/jhi-navbar/jhi-navbar.component';
 import * as config from '@/shared/config/config';
 import router from '@/router';
+import axios from 'axios';
 
 const localVue = createLocalVue();
 config.initVueApp(localVue);
@@ -22,11 +23,16 @@ localVue.directive('bToggle', {});
 localVue.component('b-col', {});
 localVue.component('b-row', {});
 localVue.component('b-button', {});
+const mockedAxios: any = axios;
+jest.mock('axios', () => ({
+  get: jest.fn(),
+}));
 
 describe('JhiNavbar', () => {
   let jhiNavbar: JhiNavbarClass;
   let wrapper: Wrapper<JhiNavbarClass>;
-  const loginService = { openLogin: jest.fn() };
+  // login() and getProfileInfo() are jhcc-custom
+  const loginService = { openLogin: jest.fn(), login: jest.fn(), getProfileInfo: jest.fn() };
   const accountService = { hasAnyAuthorityAndCheckAuth: jest.fn().mockImplementation(() => Promise.resolve(true)) };
 
   beforeEach(() => {
@@ -61,9 +67,20 @@ describe('JhiNavbar', () => {
     expect(jhiNavbar.inProduction).toBeTruthy();
   });
 
-  it('should use login service', () => {
+  // jhcc-custom
+  it('should use login service', async () => {
+    const profileInfo = {
+      'display-ribbon-on-profiles': 'dev',
+      activeProfiles: ['dev', 'swagger', 'consul'],
+    };
+    loginService.getProfileInfo.mockReturnValue(Promise.resolve(profileInfo));
+    const spy = jest.spyOn(jhiNavbar, 'openLogin');
+
     jhiNavbar.openLogin();
-    expect(loginService.openLogin).toHaveBeenCalled();
+    await jhiNavbar.$nextTick();
+
+    expect(spy).toHaveBeenCalled();
+    expect(loginService.getProfileInfo).toHaveBeenCalled();
   });
 
   it('should use account service', () => {
@@ -72,11 +89,21 @@ describe('JhiNavbar', () => {
     expect(accountService.hasAnyAuthorityAndCheckAuth).toHaveBeenCalled();
   });
 
-  it('logout should clear credentials', () => {
+  // jhcc-custom
+  it('logout should clear credentials', async () => {
+    const profileInfo = {
+      'display-ribbon-on-profiles': 'dev',
+      activeProfiles: ['dev', 'swagger', 'consul'],
+    };
+    loginService.getProfileInfo.mockReturnValue(Promise.resolve(profileInfo));
+    const spy = jest.spyOn(jhiNavbar, 'logout');
     store.commit('authenticated', { login: 'test' });
+
     jhiNavbar.logout();
+    await jhiNavbar.$nextTick;
 
-    expect(jhiNavbar.authenticated).toBeFalsy();
+    expect(spy).toHaveBeenCalled();
+    expect(loginService.getProfileInfo).toHaveBeenCalled();
   });
 
   it('should determine active route', () => {