diff --git a/doc/keycloak.md b/doc/keycloak.md new file mode 100644 index 00000000..a6a1c7a1 --- /dev/null +++ b/doc/keycloak.md @@ -0,0 +1,103 @@ +# Keycloak + +[Keycloak](https://www.keycloak.org/) is an open-source identity and access management solution providing single sign-on (SSO), user federation, and support for OpenID Connect, OAuth 2.0 and SAML. + +> [!NOTE] +> +> This module runs Keycloak in development mode (`start --optimized` with an embedded database). It is intended for local development and testing, not production. + +{#start} + +## Getting Started + +```nix +# In `perSystem.process-compose.` +{ + services.keycloak."kc" = { + enable = true; + settings.http-port = 8091; + + database.type = "dev-file"; + + realms = { + master = { + export = { + enable = true; # Export to data folder. + }; + }; + test = { + # Set that once exported. + # import = "./test.json" + + export = { + enable = true; + path = "./test.json"; + }; + }; + }; + }; +} +``` + +Keycloak becomes available at [http://localhost:8091](http://localhost:8091). +The temporary admin user is `admin` with the password set by [`initialAdminPassword`](#admin-password) (`admin` by default). + +{#tips} + +## Tips & Tricks + +{#import-realms} + +### Import Realms + +A realm listed under `realms` with `import` set is imported on start up, provided the realm does not already exist. +The `import` path may be relative to the `process-compose` working directory or a Nix store path. + +```nix +{ + services.keycloak."kc" = { + enable = true; + + realms.test = { + import = ./test.json; # Nix store path. Quote to make it a relative path. + }; + }; +} +``` + +{#export-realms} + +### Export Settings + +To export realms when you made changes in the UI, make sure to have set `export` on the realms you care about: + +```nix +{ + services.keycloak."kc" = { + enable = true; + + realms.test = { + export = { + enable = true; + path = "./test.json"; # Optional. + }; + }; + }; +} +``` + +This creates two process-compose processes, both **disabled by default** (they are not run automatically, since exporting requires Keycloak to be stopped): + +- `«name»-realm-export-all` — exports every realm with `export.enable = true`. + +Run it manually once Keycloak has stopped, e.g. from the process-compose TUI, or: + +```bash +# Stop keycloak first then run the export: +process-compose process stop «name» +process-compose process start «name»-realm-export-all +``` + +Each realm is exported to its `path` when that path is a relative (non-store) location, otherwise to `${config.services.keycloak.«name».dataDir}/realm-export/.json`. Exports are pretty-printed with `jq` for easy diffing. + +You can disable the export processes/scripts globally with `exportRealms = false;`. diff --git a/doc/services.md b/doc/services.md index 00a499bf..05a847a3 100644 --- a/doc/services.md +++ b/doc/services.md @@ -16,6 +16,7 @@ short-title: Services - [[tempo]] - [[loki]] - [[pyroscope]] +- [[keycloak]]# - [[memcached]]# - [[minio]]# - [[mongodb]]# diff --git a/nix/services/default.nix b/nix/services/default.nix index 58c82bc9..6d4b38c0 100644 --- a/nix/services/default.nix +++ b/nix/services/default.nix @@ -38,7 +38,9 @@ in ./qdrant.nix ./chromadb.nix ./neo4j.nix - ]) ++ [ + ./keycloak.nix + ]) + ++ [ ./devshell.nix ]; diff --git a/nix/services/keycloak-certs_test.nix b/nix/services/keycloak-certs_test.nix new file mode 100644 index 00000000..601c5e17 --- /dev/null +++ b/nix/services/keycloak-certs_test.nix @@ -0,0 +1,78 @@ +{ pkgs, config, ... }: +let + name = "k1-certs"; + inherit (config.services.keycloak.${name}) dataDir; + realmSrc = ./keycloak/test-realms/test.json; + + sslCertificate = "./keycloak/test-certs/ssl-cert.crt"; + sslCertificateKey = "./keycloak/test-certs/ssl-cert.key"; +in +{ + services.keycloak.${name} = { + enable = true; + settings.http-port = 8090; + + database.type = "dev-file"; + + # They must not end up in the Nix store. + inherit sslCertificate sslCertificateKey; + + realms = { + master = { + export = { + enable = true; + }; + }; + + test = { + import = realmSrc; + export = { + enable = true; + }; + }; + }; + }; + + # Copy certificates from the Nix store to correct location (only for tests). + settings.processes.copy-certs = + let + cert = ./keycloak/test-certs/ssl-cert.crt; + certKey = ./keycloak/test-certs/ssl-cert.key; + in + { + command = pkgs.writeShellApplication { + name = "copy-certs"; + text = + # Bash + '' + echo "Copying certificates (pwd: $(pwd))..." + mkdir -p "keycloak/test-certs" + cp "${cert}" "keycloak/test-certs/ssl-cert.crt" + cp "${certKey}" "keycloak/test-certs/ssl-cert.key" + ''; + }; + }; + + settings.processes.${name} = { + depends_on.copy-certs.condition = "process_completed_successfully"; + }; + + settings.processes.test = + let + test-export = pkgs.callPackage ./keycloak/test-realms/export.nix { + process-compose = config.package; + realmDstDir = "${dataDir}/realm-export"; + pcSocketPath = config.cli.options.unix-socket; + keycloak-name = name; + }; + in + { + command = pkgs.writeShellApplication { + runtimeInputs = [ test-export ]; + text = "test-export"; + name = "${name}-test"; + }; + + depends_on.${name}.condition = "process_healthy"; + }; +} diff --git a/nix/services/keycloak.nix b/nix/services/keycloak.nix new file mode 100644 index 00000000..bea8814e --- /dev/null +++ b/nix/services/keycloak.nix @@ -0,0 +1,10 @@ +# Based on Devenv's keycloak module: +# Ref: https://github.com/cachix/devenv/commit/32f6747aabbd5aeb7413bae53d7e01e224ec77bc +{ ... +}: +{ + imports = [ + ./keycloak/service.nix + ./keycloak/options.nix + ]; +} diff --git a/nix/services/keycloak/options.nix b/nix/services/keycloak/options.nix new file mode 100644 index 00000000..03676f34 --- /dev/null +++ b/nix/services/keycloak/options.nix @@ -0,0 +1,297 @@ +# Based on Devenv's keycloak module: +# Ref: https://github.com/cachix/devenv/commit/32f6747aabbd5aeb7413bae53d7e01e224ec77bc +{ config +, lib +, pkgs +, ... +}: + +let + cfg = config; + + hasRealmExports = lib.any (lib.mapAttrsToList (realmName: opts: opts.export.enable) cfg.realms); + + inherit (lib) + mkOption + mkPackageOption + mkEnableOption + types + ; +in +{ + options = { + sslCertificate = mkOption { + type = types.nullOr ( + lib.types.pathWith { + inStore = false; + absolute = false; + } + ); + default = null; + example = "/run/keys/ssl_cert"; + description = '' + The path to a PEM formatted certificate to use for TLS/SSL + connections. + ''; + }; + + sslCertificateKey = mkOption { + type = types.nullOr ( + types.pathWith { + inStore = false; + absolute = false; + } + ); + default = null; + example = "/run/keys/ssl_key"; + description = '' + The path to a PEM formatted private key to use for TLS/SSL + connections. + ''; + }; + + plugins = mkOption { + type = types.listOf types.path; + default = [ ]; + description = '' + Keycloak plugin jar, ear files or derivations containing + them. Packaged plugins are available through + `pkgs.keycloak.plugins`. + ''; + }; + + database = { + type = mkOption { + type = types.enum [ + "dev-mem" + "dev-file" + ]; + default = "dev-file"; + example = "dev-mem"; + apply = + val: + assert lib.assertMsg (val == "dev-mem" -> !hasRealmExports) '' + You cannot export realms with `realms.«name».export == true` when + using `database.type == 'dev-mem'`, import however works. + You can disable realms export with `exportRealms = true` globally. + ''; + val; + description = '' + The type of database Keycloak should connect to. + If you use `dev-mem`, the realm export over script + `keycloak-realm-export-*` does not work. + ''; + }; + }; + + package = mkPackageOption pkgs "keycloak" { }; + + initialAdminPassword = mkOption { + type = types.str; + default = "admin"; + description = '' + Initial password set for the temporary `admin` user. + The password is not stored safely and should be changed + immediately in the admin panel. + + See [Admin bootstrap and recovery](https://www.keycloak.org/server/bootstrap-admin-recovery) for details. + ''; + }; + + exportRealms = mkOption { + type = types.bool; + default = true; + description = '' + Global toggle to enable/disable the realms export process `keycloak-realm-export-all` + if any realms have `realms.«name».export == true`. + ''; + }; + + realms = mkOption { + default = { }; + type = types.attrsOf ( + types.submodule { + options = { + import = mkOption { + type = types.nullOr ( + # A relative, user-provided path. + lib.types.either + (lib.types.pathWith { + inStore = false; + absolute = false; + }) + # A nix store path. + (lib.types.pathWith { inStore = true; }) + ); + default = null; + example = "./realms/a.json"; + description = '' + The path (relative to the `process-compose` working dir or an Nix store path) + where you want to import (or export) this realm «name» to. + - If not set and `import` is `true` this realm is not imported. + - If + - set to an Nix store path + - or not it is not set + and `export` is `true` then + it is exported to `''${config.dataDir}/realm-export/«name».json`. + ''; + }; + + export = { + enable = mkEnableOption '' + export the realm on process launch `-export-realms`. + ''; + + path = mkOption { + type = types.nullOr ( + lib.types.pathWith { + inStore = false; + absolute = false; + } + ); + default = null; + example = "./realms/a.json"; + description = '' + The path (relative to the `process-compose` working dir or an Nix store path) + where you want to export this realm «name» to. + - If not set and `import.path` is relative it is used as default. + - If not set otherwise its defaulted to a value in the data folder. + ''; + }; + }; + }; + } + ); + + example = lib.literalExpression '' + { + myrealm = { + path = "./myfolder/export.json"; + import = true; # default + export = true; + }; + } + ''; + + description = '' + Specify the realms you want to import on start up and + export on a manual start of process/script 'keycloak-realm-export-all'. + ''; + }; + + settings = mkOption { + type = lib.types.submodule { + freeformType = types.attrsOf ( + types.nullOr ( + types.oneOf [ + types.str + types.int + types.bool + (types.attrsOf types.path) + ] + ) + ); + + options = { + http-host = mkOption { + type = types.str; + default = "::"; + example = "::1"; + description = '' + On which address Keycloak should accept new connections. + ''; + }; + + http-port = mkOption { + type = types.port; + default = 8080; + example = 8080; + description = '' + On which port Keycloak should listen for new HTTP connections. + ''; + }; + + http-management-port = mkOption { + type = types.port; + default = 9000; + example = 9000; + description = '' + The port where Keycloak exposes management API endpoints (e.g. `/health`). + ''; + }; + + https-port = mkOption { + type = types.port; + default = 34429; + example = 34429; + description = '' + On which port Keycloak should listen for new HTTPS connections. + If its not set, its disabled. + ''; + }; + + http-relative-path = mkOption { + type = types.str; + default = "/"; + example = "/auth"; + apply = x: if !(lib.hasPrefix "/") x then "/" + x else x; + description = '' + The path relative to `/` for serving + resources. + + ::: {.note} + In versions of Keycloak using Wildfly (<17), + this defaulted to `/auth`. If + upgrading from the Wildfly version of Keycloak, + i.e. a NixOS version before 22.05, you'll likely + want to set this to `/auth` to + keep compatibility with your clients. + + See + for more information on migrating from Wildfly to Quarkus. + ::: + ''; + }; + + hostname = mkOption { + type = types.str; + default = "localhost"; + example = "localhost"; + description = '' + The hostname part of the public URL used as base for + all frontend requests. + + See + for more information about hostname configuration. + ''; + }; + }; + }; + + example = lib.literalExpression '' + { + hostname = "localhost"; + https-key-store-file = "/path/to/file"; + https-key-store-password = { _secret = "/run/keys/store_password"; }; + } + ''; + + description = '' + Configuration options corresponding to parameters set in + {file}`conf/keycloak.conf`. + + Most available options are documented at . + + Options containing secret data should be set to an attribute + set containing the attribute `_secret` - a + string pointing to a file containing the value the option + should be set to. See the example to get a better picture of + this: in the resulting + {file}`conf/keycloak.conf` file, the + `https-key-store-password` key will be set + to the contents of the + {file}`/run/keys/store_password` file. + ''; + }; + }; +} diff --git a/nix/services/keycloak/service.nix b/nix/services/keycloak/service.nix new file mode 100644 index 00000000..9b47079e --- /dev/null +++ b/nix/services/keycloak/service.nix @@ -0,0 +1,251 @@ +{ config +, lib +, pkgs +, name +, ... +}: +let + cfg = config; + + isSecret = v: lib.isAttrs v && v ? _secret && lib.isString v._secret; + + # Generate the keycloak config file to build it. + keycloakConfig = lib.generators.toKeyValue { + mkKeyValue = lib.flip lib.generators.mkKeyValueDefault "=" { + mkValueString = + v: + if builtins.isInt v then + toString v + else if builtins.isString v then + v + else if true == v then + "true" + else if false == v then + "false" + else if isSecret v then + builtins.hashString "sha256" v._secret + else + throw "unsupported type ${builtins.typeOf v}: ${(lib.generators.toPretty { }) v}"; + }; + }; + + # Filters empty values out. + filteredConfig = lib.converge + (lib.filterAttrsRecursive ( + _: v: + !builtins.elem v [ + { } + null + ] + )) + cfg.settings; + + # Write the keycloak config file. + confFile = pkgs.writeText "keycloak.conf" (keycloakConfig filteredConfig); + + # Build keycloak derivation. + keycloakBuild = ( + cfg.package.override { + inherit confFile; + + plugins = cfg.package.enabledPlugins ++ cfg.plugins; + } + ); + + # Create dummy certificate derivation. + dummyCertificates = pkgs.stdenv.mkDerivation { + pname = "dev-ssl-cert"; + version = "1.0"; + buildInputs = [ pkgs.openssl ]; + src = null; + dontUnpack = true; + buildPhase = '' + mkdir -p $out + openssl req -x509 -newkey rsa:2048 -nodes \ + -keyout $out/ssl-cert.key -out $out/ssl-cert.crt \ + -days 365 \ + -subj "/CN=localhost" + ''; + + installPhase = "true"; + }; + + providedSSLCerts = cfg.sslCertificate != null && cfg.sslCertificateKey != null; + + # Generate the command to import realms. + realmImport = lib.mapAttrsToList + ( + realm: e: + let + f = e.import; + in + # Bash + '' + f=$(realpath "${f}") + echo "Symlinking realm file '$f' to import path '$KC_HOME_DIR/data/import'." + if [ ! -f "$f" ]; then + echo "Realm file '$f' does not exist!" >&2 + exit 1 + fi + ln -fs "$f" "$KC_HOME_DIR/data/import/" + unset f + '' + ) + (lib.filterAttrs (_: v: v.import != null) cfg.realms); + + # Generate the commands to export realms. + assertKeycloakStopped = [ + '' + if ${keycloak-health}/bin/keycloak-health &>/dev/null; then + echo "You must first stop keycloak and then run this command again." >&2 + exit 1 + fi + + # Ensure the KC_HOME_DIR is set up for the export command. + mkdir -p "$KC_HOME_DIR/conf" + ln -fs ${keycloakBuild}/providers "$KC_HOME_DIR/" + ln -fs ${keycloakBuild}/lib "$KC_HOME_DIR/" + install -D -m 0600 ${confFile} "$KC_HOME_DIR/conf/keycloak.conf" + '' + ]; + + realmExportPath = + let + isInStore = + x: + lib.path.hasStorePathPrefix ( + if builtins.isPath x then x else /. + builtins.unsafeDiscardStringContext x + ); + in + realm: e: + if e.export.path != null then + e.export.path + else + ( + if e.import == null || isInStore e.import then + (config.dataDir + "/realm-export/${realm}.json") + else + e.import + ); + + realmsToExport = lib.filterAttrs (_: v: v.export.enable) cfg.realms; + realmsExport = + if (!cfg.exportRealms || lib.length (lib.attrNames realmsToExport) == 0) then + [ ] + else + assertKeycloakStopped + ++ lib.mapAttrsToList + ( + realm: e: + let + file = realmExportPath realm e; + in + # Bash + '' + echo "Exporting realm '${realm}' to '${file}'." + mkdir -p "$(dirname "${file}")" + ${keycloakBuild}/bin/kc.sh export --optimized --realm "${realm}" --file "${file}" + + echo "Beautifying realm export '${file}' for diffing." + temp_file=$(${pkgs.coreutils}/bin/mktemp) + ${pkgs.jq}/bin/jq --sort-keys . "${file}" > "$temp_file" + ${pkgs.coreutils}/bin/mv "$temp_file" "${file}" + '' + ) + realmsToExport; + + keycloak-realm-export-all = pkgs.writeShellScriptBin "keycloak-realm-export-all" ( + lib.concatStringsSep "\n" realmsExport + ); + + keycloak-health = pkgs.writeShellScriptBin "keycloak-health" '' + ${pkgs.curl}/bin/curl -k --head -fsS "https://localhost:${toString cfg.settings.http-management-port}${lib.removeSuffix "/" cfg.settings.http-management-relative-path}/health/ready" + ''; + + keycloakEnv = { + # Note: we add "./" cause keycloak's database url + # does not allow implicitly relative paths. + KC_HOME_DIR = "./" + cfg.dataDir; + KC_CONF_DIR = cfg.dataDir + "/conf"; + KC_TMP_DIR = cfg.dataDir + "/tmp"; + + KC_BOOTSTRAP_ADMIN_USERNAME = "admin"; + KC_BOOTSTRAP_ADMIN_PASSWORD = "${lib.escapeShellArg cfg.initialAdminPassword}"; + }; + + keycloak-start = + pkgs.writeShellScriptBin "keycloak-start" + # Bash + '' + set -euo pipefail + mkdir -p "$KC_HOME_DIR" + mkdir -p "$KC_HOME_DIR/conf" + mkdir -p "$KC_HOME_DIR/tmp" + + # Always remove the symlinks for the realm imports. + rm -rf "$KC_HOME_DIR/data/import" || true + mkdir -p "$KC_HOME_DIR/data/import" + + ln -fs ${keycloakBuild}/providers "$KC_HOME_DIR/" + ln -fs ${keycloakBuild}/lib "$KC_HOME_DIR/" + install -D -m 0600 ${confFile} "$KC_HOME_DIR/conf/keycloak.conf" + + echo "Keycloak config:" + ${keycloakBuild}/bin/kc.sh show-config || true + + echo "Import realms (if any)..." + ${builtins.concatStringsSep "\n" realmImport} + echo "========================" + + echo "Start keycloak:" + exec ${keycloakBuild}/bin/kc.sh start --optimized --import-realm + ''; +in +{ + # Merge some default values into the freeform options. + settings = lib.mapAttrs (n: v: lib.mkOptionDefault v) { + # We always enable http since we also use it to check the health. + http-enabled = true; + db = cfg.database.type; + + health-enabled = true; + http-management-relative-path = "/"; + + log-console-level = "info"; + log-level = "info"; + + https-certificate-file = + if providedSSLCerts then cfg.sslCertificate else "${dummyCertificates}/ssl-cert.crt"; + https-certificate-key-file = + if providedSSLCerts then cfg.sslCertificateKey else "${dummyCertificates}/ssl-cert.key"; + }; + + outputs = { + settings.processes = { + ${name} = { + environment = keycloakEnv; + command = keycloak-start; + readiness_probe = { + exec = { + command = "${lib.getExe keycloak-health}"; + }; + initial_delay_seconds = 10; + timeout_seconds = 4; + failure_threshold = 20; + }; + }; + + } + // (lib.optionalAttrs (realmsExport != [ ]) { + # Export all configured realms. + "${name}-realm-export-all" = { + environment = keycloakEnv; + command = keycloak-realm-export-all; + disabled = true; + description = '' + Save the configured realms from keycloak, to back them up. You can run it manually. + ''; + }; + }); + }; +} diff --git a/nix/services/keycloak/test-certs/ssl-cert.crt b/nix/services/keycloak/test-certs/ssl-cert.crt new file mode 100644 index 00000000..e633df0f --- /dev/null +++ b/nix/services/keycloak/test-certs/ssl-cert.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDCTCCAfGgAwIBAgIUePpk+xLT7G7317grthXd+ATTcWgwDQYJKoZIhvcNAQEL +BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI1MDUxMjA2MDM1NVoXDTI2MDUx +MjA2MDM1NVowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAmb/B9IIGerR7aIj1QUK8nukBetvwCqwYHs7tz1rfaROR +598d8cbXOwa+9BXJeqDUee29+3d3bctMCjgWpiDo1d7V3DlPrnbSzkkwezthNX1m +N2JvIlerOHqnIdG1TSJlQwX4q87I7+7zrscX7TOUjP+nM2pAxzaTLztjmFeccRbt +QaLCLKtsFBqCSj3DHaS+OUoWmwNjj1VodxHXeelouFp3uZA6fuPRLtWHo3LIQttJ +3wUDrxGKl7WXhpz1zr2DHUbLtzx/eRbkcwrxAO+p3avWgET6X0POLI34+bhfPme1 +WoY8E8ZuAdiBx6lOBc0LDN5k2M2KxfXicCOzFYE2sQIDAQABo1MwUTAdBgNVHQ4E +FgQUsQ1P7x5459sgVqHsCogOAzbYj5QwHwYDVR0jBBgwFoAUsQ1P7x5459sgVqHs +CogOAzbYj5QwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAh8Xy +1kQtRRo23qLJh+yAFZy2sB1vlcRUYr9TFn5ys/UHtqKvl+arn2VerPmAludeTbgz +Bpsqp4IXwxEW4YcQ848R7/G4iORLWqpszCCSlRSr2O6Mh0Zs0qasQJ3EH27zhF0B +xQgQ9bgdXEH/JvvpWTHiz3DN2Lr/5ZdWVeLhoDRIpKy5u7XXb2JToIH/sk4rKY2B +j5MQyQiSuId9najCH8ZLiIJ4rZld/yUESE/bn1Dp7jSwB8vOtxqLEpTuKb47+deM +hW4IsB0q0hi3FrGEsgAtBoUtsbJALTKMKulx/CAUmgF4BQ3IIIRPgSnGo4Vs6R3a +t3inOVJ+xg83lwkvSg== +-----END CERTIFICATE----- diff --git a/nix/services/keycloak/test-certs/ssl-cert.key b/nix/services/keycloak/test-certs/ssl-cert.key new file mode 100644 index 00000000..0d7b1c37 --- /dev/null +++ b/nix/services/keycloak/test-certs/ssl-cert.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCZv8H0ggZ6tHto +iPVBQrye6QF62/AKrBgezu3PWt9pE5Hn3x3xxtc7Br70Fcl6oNR57b37d3dty0wK +OBamIOjV3tXcOU+udtLOSTB7O2E1fWY3Ym8iV6s4eqch0bVNImVDBfirzsjv7vOu +xxftM5SM/6czakDHNpMvO2OYV5xxFu1BosIsq2wUGoJKPcMdpL45ShabA2OPVWh3 +Edd56Wi4Wne5kDp+49Eu1YejcshC20nfBQOvEYqXtZeGnPXOvYMdRsu3PH95FuRz +CvEA76ndq9aARPpfQ84sjfj5uF8+Z7VahjwTxm4B2IHHqU4FzQsM3mTYzYrF9eJw +I7MVgTaxAgMBAAECggEABksjKq8Siiijzz5L9m/qPgGrsyvC7QI+ZS8d7O77Vixx +kdxAODVmJLRnUfZ9A4Kx2ovbdapePnr5PIcvO32oCkPPh+7l+W5DvXihfhSK1iBb +9B3DZCnSiG1SNMAw0Qd8vabw7EIlV4JLLKGvCQv4zgy+ULcjZQQxjNgRgPA/0cve +NSz94u+aAxsCWUf4Urk2KaPFf8O8+OTcY0PntCW/BOa5F7J93vWYTgaycHmwGBjp +85FdofNRbs91kF0Nq2tPXGbVpoleR9xtelk8Sj4aTg1Bgt5GgXep24RZbSFOTHJ7 +4VyyfV3xyNDM43/qHuZkN6PePx7CnQjPv4mVBZadQQKBgQDMdd/zGQcnKUc7Db23 +xY1jWaGy3Ml+N3bJkWaQHOoW+RcFxfr6aKa4mYWXnXaWA74Ff6QbJKc/GxM7dqJZ +Fiv+VrX2CnVsJFDqayl26rlFcqBYkFmAlcfSNUOy+zh9tYxpHUu3scHxmpy4I0ao +w9F5JXk0q4UHpJf3rE2FC3TIrwKBgQDAgWdqT+/ibqwT7qoiPn9ZlWvoPZlhVq1C +AJ2CJD0bzPdLbB2gI6ZDf4XXayBG8Wdgs0rou7E5eHo+j3mHC7ALa2HQ1hiOYPvs +qRXGGMiI65iDA8oCNY4+VZ3HIBVwQQVBLy1tB2I2ipz2BOGZqkSPjAf/irTAmu7D +zBt4bLEOnwKBgQCcG2vP+k4B0nx2VDmENKuNLYROQlD9s29Y21FJuynoPGsdl1nX +E0woKd9cMXe3dkgBfsFXkBa6EfwPLLcr+cfBO4dWwgmBdgDp2sQf7Xtj0O8ob55G +lRWqI8z6vOEW6iS4pQuIx4ni5D/AP/7VIB2xt7DhTAwYF82H5uCRMnwKNQKBgQCr +T/2KHI0sArVcH56ETv3h6RlYWckvWIVjIU0KTdmb7fF8y0AqDM+fbdLb+9eDir0m +zyewqadzr4WKOwA5b+tAnlU0FfNQcXqAB5D6838yagcVQL/661IkouiAFks91H8q +nSNdzZ5XT0+TmJBzwZS18jYD8ZoddUNaNz+TEZcXnQKBgDpVhyX/1ILLCMffT1Wa +HUGMghPb03SuH6y3RYLkfD8DX5wycQ7jBELWnJFs1lWn8N9M0cSqupSfIcy65ZQS +URm5rM5LK2TeoxELVO0FWqzTbPmOfQwcDfTQ+FX0JsdtuTKhDKC57qavJcY/GoGN +mnJm/F1e1mncg3sH8mdlQmC2 +-----END PRIVATE KEY----- diff --git a/nix/services/keycloak/test-realms/export.nix b/nix/services/keycloak/test-realms/export.nix new file mode 100644 index 00000000..18b749a1 --- /dev/null +++ b/nix/services/keycloak/test-realms/export.nix @@ -0,0 +1,83 @@ +{ writeShellApplication +, process-compose +, jq +, # Own stuff. + keycloak-name +, realmDstDir +, pcSocketPath +, ... +}: + +writeShellApplication { + name = "test-export"; + + runtimeInputs = [ + process-compose + jq + ]; + + text = + # Bash + '' + export PC_SOCKET_PATH="${pcSocketPath}" + + # Silence process-compose not finding a config home. + mkdir -p "$(pwd)/.config/process-compose" + # shellcheck disable=SC2155 + export XDG_CONFIG_HOME="$(pwd)/.config" + + test_export() { + echo "Stop keycloak..." + process-compose process stop "${keycloak-name}" -u "$PC_SOCKET_PATH" + + for _ in $(seq 1 10); do + if + [ "$( + process-compose process get "${keycloak-name}" -o json -u "$PC_SOCKET_PATH" | + jq -r ".[0].status" + )" = "Completed" ] + then + completed="true" + break + fi + + sleep 2 + done + + echo "Export realms..." + process-compose process start "${keycloak-name}-realm-export-all" -u "$PC_SOCKET_PATH" + + completed="false" + for _ in $(seq 1 30); do + if + [ "$( + process-compose process get "${keycloak-name}-realm-export-all" \ + -o json -u "$PC_SOCKET_PATH" | + jq -r ".[0].status" + )" = "Completed" ] + then + completed="true" + break + fi + + sleep 2 + done + + if [ "$completed" != "true" ]; then + echo "!! Realm export did not complete in time." + return 1 + fi + + if [ ! -f "${realmDstDir}/master.json" ]; then + echo "!! Realm '${realmDstDir}/master.json' did not get exported." + return 1 + fi + + if [ ! -f "${realmDstDir}/test.json" ]; then + echo "!! Realm '${realmDstDir}/test.json' did not get exported". + fi + } + + test_export + ''; +} diff --git a/nix/services/keycloak/test-realms/test.json b/nix/services/keycloak/test-realms/test.json new file mode 100644 index 00000000..4a959540 --- /dev/null +++ b/nix/services/keycloak/test-realms/test.json @@ -0,0 +1,1980 @@ +{ + "id" : "552b672d-d487-4213-b464-1d3a6290ec67", + "realm" : "test", + "notBefore" : 0, + "defaultSignatureAlgorithm" : "RS256", + "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, + "clientOfflineSessionIdleTimeout" : 0, + "clientOfflineSessionMaxLifespan" : 0, + "accessCodeLifespan" : 60, + "accessCodeLifespanUserAction" : 300, + "accessCodeLifespanLogin" : 1800, + "actionTokenGeneratedByAdminLifespan" : 43200, + "actionTokenGeneratedByUserLifespan" : 300, + "oauth2DeviceCodeLifespan" : 600, + "oauth2DevicePollingInterval" : 5, + "enabled" : true, + "sslRequired" : "external", + "registrationAllowed" : false, + "registrationEmailAsUsername" : false, + "rememberMe" : false, + "verifyEmail" : false, + "loginWithEmailAllowed" : true, + "duplicateEmailsAllowed" : false, + "resetPasswordAllowed" : false, + "editUsernameAllowed" : false, + "bruteForceProtected" : false, + "permanentLockout" : false, + "maxTemporaryLockouts" : 0, + "bruteForceStrategy" : "MULTIPLE", + "maxFailureWaitSeconds" : 900, + "minimumQuickLoginWaitSeconds" : 60, + "waitIncrementSeconds" : 60, + "quickLoginCheckMilliSeconds" : 1000, + "maxDeltaTimeSeconds" : 43200, + "failureFactor" : 30, + "roles" : { + "realm" : [ { + "id" : "16367bbc-fdf0-4fd4-aa94-437eb16f052d", + "name" : "offline_access", + "description" : "${role_offline-access}", + "composite" : false, + "clientRole" : false, + "containerId" : "552b672d-d487-4213-b464-1d3a6290ec67", + "attributes" : { } + }, { + "id" : "346248d8-22cf-44bc-8f54-d8a5a169f4c9", + "name" : "uma_authorization", + "description" : "${role_uma_authorization}", + "composite" : false, + "clientRole" : false, + "containerId" : "552b672d-d487-4213-b464-1d3a6290ec67", + "attributes" : { } + }, { + "id" : "1cdfa035-675e-4984-a797-44ec4c45ffd8", + "name" : "default-roles-test", + "description" : "${role_default-roles}", + "composite" : true, + "composites" : { + "realm" : [ "offline_access", "uma_authorization" ], + "client" : { + "account" : [ "view-profile", "manage-account" ] + } + }, + "clientRole" : false, + "containerId" : "552b672d-d487-4213-b464-1d3a6290ec67", + "attributes" : { } + } ], + "client" : { + "realm-management" : [ { + "id" : "675fb0d6-88a9-4956-81c3-511db5ef171f", + "name" : "impersonation", + "description" : "${role_impersonation}", + "composite" : false, + "clientRole" : true, + "containerId" : "6bf0c67f-4593-4720-ab82-68cf955febc7", + "attributes" : { } + }, { + "id" : "9ed43326-2f5b-4bec-8f2b-4460fa4eb11b", + "name" : "manage-authorization", + "description" : "${role_manage-authorization}", + "composite" : false, + "clientRole" : true, + "containerId" : "6bf0c67f-4593-4720-ab82-68cf955febc7", + "attributes" : { } + }, { + "id" : "00db75ee-ba5e-4f2a-a87a-383f88dca26e", + "name" : "realm-admin", + "description" : "${role_realm-admin}", + "composite" : true, + "composites" : { + "client" : { + "realm-management" : [ "impersonation", "manage-authorization", "manage-clients", "manage-identity-providers", "view-realm", "query-groups", "query-users", "view-events", "view-clients", "manage-events", "query-clients", "view-authorization", "manage-realm", "view-users", "create-client", "query-realms", "view-identity-providers", "manage-users" ] + } + }, + "clientRole" : true, + "containerId" : "6bf0c67f-4593-4720-ab82-68cf955febc7", + "attributes" : { } + }, { + "id" : "0f93e63a-23a4-4c57-bfeb-8b7b779901c2", + "name" : "manage-clients", + "description" : "${role_manage-clients}", + "composite" : false, + "clientRole" : true, + "containerId" : "6bf0c67f-4593-4720-ab82-68cf955febc7", + "attributes" : { } + }, { + "id" : "9facf169-58f4-45bf-b7c4-0f41cf6cd2b4", + "name" : "manage-identity-providers", + "description" : "${role_manage-identity-providers}", + "composite" : false, + "clientRole" : true, + "containerId" : "6bf0c67f-4593-4720-ab82-68cf955febc7", + "attributes" : { } + }, { + "id" : "19a0e285-664c-436c-babe-8c572698e63d", + "name" : "view-realm", + "description" : "${role_view-realm}", + "composite" : false, + "clientRole" : true, + "containerId" : "6bf0c67f-4593-4720-ab82-68cf955febc7", + "attributes" : { } + }, { + "id" : "4c063494-03ec-4beb-9932-454faaf4d7fc", + "name" : "query-groups", + "description" : "${role_query-groups}", + "composite" : false, + "clientRole" : true, + "containerId" : "6bf0c67f-4593-4720-ab82-68cf955febc7", + "attributes" : { } + }, { + "id" : "83e9111d-3204-431a-bf4a-6b87fad954b3", + "name" : "query-users", + "description" : "${role_query-users}", + "composite" : false, + "clientRole" : true, + "containerId" : "6bf0c67f-4593-4720-ab82-68cf955febc7", + "attributes" : { } + }, { + "id" : "75098b40-cc49-41a2-bec4-335a7caf67e3", + "name" : "view-clients", + "description" : "${role_view-clients}", + "composite" : true, + "composites" : { + "client" : { + "realm-management" : [ "query-clients" ] + } + }, + "clientRole" : true, + "containerId" : "6bf0c67f-4593-4720-ab82-68cf955febc7", + "attributes" : { } + }, { + "id" : "3801bb21-9312-4939-bbf9-cf831332f17e", + "name" : "view-events", + "description" : "${role_view-events}", + "composite" : false, + "clientRole" : true, + "containerId" : "6bf0c67f-4593-4720-ab82-68cf955febc7", + "attributes" : { } + }, { + "id" : "968ac58b-5b6d-4578-9a37-7e581579f785", + "name" : "manage-events", + "description" : "${role_manage-events}", + "composite" : false, + "clientRole" : true, + "containerId" : "6bf0c67f-4593-4720-ab82-68cf955febc7", + "attributes" : { } + }, { + "id" : "7a6768e9-8955-4234-b8ea-1529762a3a08", + "name" : "query-clients", + "description" : "${role_query-clients}", + "composite" : false, + "clientRole" : true, + "containerId" : "6bf0c67f-4593-4720-ab82-68cf955febc7", + "attributes" : { } + }, { + "id" : "82471aa3-8dfd-4bdf-bf58-82109e124219", + "name" : "view-authorization", + "description" : "${role_view-authorization}", + "composite" : false, + "clientRole" : true, + "containerId" : "6bf0c67f-4593-4720-ab82-68cf955febc7", + "attributes" : { } + }, { + "id" : "cb97adc1-6677-42df-9420-853c47f692b7", + "name" : "manage-realm", + "description" : "${role_manage-realm}", + "composite" : false, + "clientRole" : true, + "containerId" : "6bf0c67f-4593-4720-ab82-68cf955febc7", + "attributes" : { } + }, { + "id" : "af732a84-c47d-4617-b7b1-57b12ebbc7c4", + "name" : "create-client", + "description" : "${role_create-client}", + "composite" : false, + "clientRole" : true, + "containerId" : "6bf0c67f-4593-4720-ab82-68cf955febc7", + "attributes" : { } + }, { + "id" : "483bd584-5ea7-4891-9362-a0c34f106cf0", + "name" : "view-users", + "description" : "${role_view-users}", + "composite" : true, + "composites" : { + "client" : { + "realm-management" : [ "query-groups", "query-users" ] + } + }, + "clientRole" : true, + "containerId" : "6bf0c67f-4593-4720-ab82-68cf955febc7", + "attributes" : { } + }, { + "id" : "28c8ee8d-38ad-41f9-910c-3bd13a7ec78b", + "name" : "query-realms", + "description" : "${role_query-realms}", + "composite" : false, + "clientRole" : true, + "containerId" : "6bf0c67f-4593-4720-ab82-68cf955febc7", + "attributes" : { } + }, { + "id" : "155d8f95-bc0c-435e-9a98-f9df471ff144", + "name" : "view-identity-providers", + "description" : "${role_view-identity-providers}", + "composite" : false, + "clientRole" : true, + "containerId" : "6bf0c67f-4593-4720-ab82-68cf955febc7", + "attributes" : { } + }, { + "id" : "b18cf4b2-a153-45e3-ba79-5311699ef0a3", + "name" : "manage-users", + "description" : "${role_manage-users}", + "composite" : false, + "clientRole" : true, + "containerId" : "6bf0c67f-4593-4720-ab82-68cf955febc7", + "attributes" : { } + } ], + "test" : [ ], + "security-admin-console" : [ ], + "admin-cli" : [ ], + "account-console" : [ ], + "broker" : [ { + "id" : "451c1d48-734f-4800-9774-48bd4bf4812f", + "name" : "read-token", + "description" : "${role_read-token}", + "composite" : false, + "clientRole" : true, + "containerId" : "67be77be-c624-4bbe-943f-6c3ebcd339cf", + "attributes" : { } + } ], + "account" : [ { + "id" : "04bb39fe-8f52-4ea2-b4e5-4bf64aab31dd", + "name" : "delete-account", + "description" : "${role_delete-account}", + "composite" : false, + "clientRole" : true, + "containerId" : "8ab5429a-e7e1-462d-9cad-4bebafeb3e38", + "attributes" : { } + }, { + "id" : "0e13a4ff-5279-445f-b094-6df79b2f5cf0", + "name" : "view-groups", + "description" : "${role_view-groups}", + "composite" : false, + "clientRole" : true, + "containerId" : "8ab5429a-e7e1-462d-9cad-4bebafeb3e38", + "attributes" : { } + }, { + "id" : "417f919c-74c7-4234-acfb-383834c373cd", + "name" : "manage-account-links", + "description" : "${role_manage-account-links}", + "composite" : false, + "clientRole" : true, + "containerId" : "8ab5429a-e7e1-462d-9cad-4bebafeb3e38", + "attributes" : { } + }, { + "id" : "4e7253a2-d890-4bb1-b4de-49ee8d8876e8", + "name" : "manage-consent", + "description" : "${role_manage-consent}", + "composite" : true, + "composites" : { + "client" : { + "account" : [ "view-consent" ] + } + }, + "clientRole" : true, + "containerId" : "8ab5429a-e7e1-462d-9cad-4bebafeb3e38", + "attributes" : { } + }, { + "id" : "99237935-d669-48bf-8e63-c87e4e7aaee0", + "name" : "view-profile", + "description" : "${role_view-profile}", + "composite" : false, + "clientRole" : true, + "containerId" : "8ab5429a-e7e1-462d-9cad-4bebafeb3e38", + "attributes" : { } + }, { + "id" : "be075bde-6dba-4fec-b09f-a2daf7dd40b4", + "name" : "view-applications", + "description" : "${role_view-applications}", + "composite" : false, + "clientRole" : true, + "containerId" : "8ab5429a-e7e1-462d-9cad-4bebafeb3e38", + "attributes" : { } + }, { + "id" : "9afbaab3-9df6-406a-a602-57081a52b594", + "name" : "view-consent", + "description" : "${role_view-consent}", + "composite" : false, + "clientRole" : true, + "containerId" : "8ab5429a-e7e1-462d-9cad-4bebafeb3e38", + "attributes" : { } + }, { + "id" : "5b248f9e-9824-4642-9686-579705c4a5ed", + "name" : "manage-account", + "description" : "${role_manage-account}", + "composite" : true, + "composites" : { + "client" : { + "account" : [ "manage-account-links" ] + } + }, + "clientRole" : true, + "containerId" : "8ab5429a-e7e1-462d-9cad-4bebafeb3e38", + "attributes" : { } + } ] + } + }, + "groups" : [ ], + "defaultRole" : { + "id" : "1cdfa035-675e-4984-a797-44ec4c45ffd8", + "name" : "default-roles-test", + "description" : "${role_default-roles}", + "composite" : true, + "clientRole" : false, + "containerId" : "552b672d-d487-4213-b464-1d3a6290ec67" + }, + "requiredCredentials" : [ "password" ], + "otpPolicyType" : "totp", + "otpPolicyAlgorithm" : "HmacSHA1", + "otpPolicyInitialCounter" : 0, + "otpPolicyDigits" : 6, + "otpPolicyLookAheadWindow" : 1, + "otpPolicyPeriod" : 30, + "otpPolicyCodeReusable" : false, + "otpSupportedApplications" : [ "totpAppFreeOTPName", "totpAppGoogleName", "totpAppMicrosoftAuthenticatorName" ], + "localizationTexts" : { }, + "webAuthnPolicyRpEntityName" : "keycloak", + "webAuthnPolicySignatureAlgorithms" : [ "ES256", "RS256" ], + "webAuthnPolicyRpId" : "", + "webAuthnPolicyAttestationConveyancePreference" : "not specified", + "webAuthnPolicyAuthenticatorAttachment" : "not specified", + "webAuthnPolicyRequireResidentKey" : "not specified", + "webAuthnPolicyUserVerificationRequirement" : "not specified", + "webAuthnPolicyCreateTimeout" : 0, + "webAuthnPolicyAvoidSameAuthenticatorRegister" : false, + "webAuthnPolicyAcceptableAaguids" : [ ], + "webAuthnPolicyExtraOrigins" : [ ], + "webAuthnPolicyPasswordlessRpEntityName" : "keycloak", + "webAuthnPolicyPasswordlessSignatureAlgorithms" : [ "ES256", "RS256" ], + "webAuthnPolicyPasswordlessRpId" : "", + "webAuthnPolicyPasswordlessAttestationConveyancePreference" : "not specified", + "webAuthnPolicyPasswordlessAuthenticatorAttachment" : "not specified", + "webAuthnPolicyPasswordlessRequireResidentKey" : "not specified", + "webAuthnPolicyPasswordlessUserVerificationRequirement" : "not specified", + "webAuthnPolicyPasswordlessCreateTimeout" : 0, + "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister" : false, + "webAuthnPolicyPasswordlessAcceptableAaguids" : [ ], + "webAuthnPolicyPasswordlessExtraOrigins" : [ ], + "scopeMappings" : [ { + "clientScope" : "offline_access", + "roles" : [ "offline_access" ] + } ], + "clientScopeMappings" : { + "account" : [ { + "client" : "account-console", + "roles" : [ "manage-account", "view-groups" ] + } ] + }, + "clients" : [ { + "id" : "8ab5429a-e7e1-462d-9cad-4bebafeb3e38", + "clientId" : "account", + "name" : "${client_account}", + "rootUrl" : "${authBaseUrl}", + "baseUrl" : "/realms/test/account/", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ "/realms/test/account/*" ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "realm_client" : "false", + "post.logout.redirect.uris" : "+" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "basic", "email" ], + "optionalClientScopes" : [ "address", "phone", "organization", "offline_access", "microprofile-jwt" ] + }, { + "id" : "4b57b2c5-f15c-4a00-a48d-3fcbaa9a7bae", + "clientId" : "account-console", + "name" : "${client_account-console}", + "rootUrl" : "${authBaseUrl}", + "baseUrl" : "/realms/test/account/", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ "/realms/test/account/*" ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "realm_client" : "false", + "post.logout.redirect.uris" : "+", + "pkce.code.challenge.method" : "S256" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "protocolMappers" : [ { + "id" : "d4d3ec7e-4016-49b6-9846-6351d3cf3253", + "name" : "audience resolve", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-audience-resolve-mapper", + "consentRequired" : false, + "config" : { } + } ], + "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "basic", "email" ], + "optionalClientScopes" : [ "address", "phone", "organization", "offline_access", "microprofile-jwt" ] + }, { + "id" : "2bd0bd42-b0c6-48d9-8e9d-056641672f3f", + "clientId" : "admin-cli", + "name" : "${client_admin-cli}", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-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" : { + "realm_client" : "false", + "client.use.lightweight.access.token.enabled" : "true" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : true, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "basic", "email" ], + "optionalClientScopes" : [ "address", "phone", "organization", "offline_access", "microprofile-jwt" ] + }, { + "id" : "67be77be-c624-4bbe-943f-6c3ebcd339cf", + "clientId" : "broker", + "name" : "${client_broker}", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-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" : { + "realm_client" : "true" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "basic", "email" ], + "optionalClientScopes" : [ "address", "phone", "organization", "offline_access", "microprofile-jwt" ] + }, { + "id" : "6bf0c67f-4593-4720-ab82-68cf955febc7", + "clientId" : "realm-management", + "name" : "${client_realm-management}", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-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" : { + "realm_client" : "true" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "basic", "email" ], + "optionalClientScopes" : [ "address", "phone", "organization", "offline_access", "microprofile-jwt" ] + }, { + "id" : "4a93c590-3487-44a9-a759-c7faf78dbdb4", + "clientId" : "security-admin-console", + "name" : "${client_security-admin-console}", + "rootUrl" : "${authAdminUrl}", + "baseUrl" : "/admin/test/console/", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ "/admin/test/console/*" ], + "webOrigins" : [ "+" ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "realm_client" : "false", + "client.use.lightweight.access.token.enabled" : "true", + "post.logout.redirect.uris" : "+", + "pkce.code.challenge.method" : "S256" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : true, + "nodeReRegistrationTimeout" : 0, + "protocolMappers" : [ { + "id" : "b58e7931-b037-4606-b29b-6d160bb68b71", + "name" : "locale", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "locale", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "locale", + "jsonType.label" : "String" + } + } ], + "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "basic", "email" ], + "optionalClientScopes" : [ "address", "phone", "organization", "offline_access", "microprofile-jwt" ] + }, { + "id" : "874dd5ca-1830-47ff-9f2a-f567eacb3cf6", + "clientId" : "test", + "name" : "test", + "description" : "test", + "rootUrl" : "", + "adminUrl" : "", + "baseUrl" : "", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : true, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ "/*" ], + "webOrigins" : [ "/*" ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : true, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : true, + "protocol" : "openid-connect", + "attributes" : { + "realm_client" : "false", + "oidc.ciba.grant.enabled" : "false", + "backchannel.logout.session.required" : "true", + "oauth2.device.authorization.grant.enabled" : "false", + "display.on.consent.screen" : "false", + "backchannel.logout.revoke.offline.tokens" : "false" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : true, + "nodeReRegistrationTimeout" : -1, + "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "basic", "email" ], + "optionalClientScopes" : [ "address", "phone", "organization", "offline_access", "microprofile-jwt" ] + } ], + "clientScopes" : [ { + "id" : "a78ea679-7a18-452d-9f9d-e431370e39ea", + "name" : "address", + "description" : "OpenID Connect built-in scope: address", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "consent.screen.text" : "${addressScopeConsentText}", + "display.on.consent.screen" : "true" + }, + "protocolMappers" : [ { + "id" : "fadeb169-a6b8-4963-bc26-9deb9801f5eb", + "name" : "address", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-address-mapper", + "consentRequired" : false, + "config" : { + "user.attribute.formatted" : "formatted", + "user.attribute.country" : "country", + "introspection.token.claim" : "true", + "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" : "60679a33-1b5b-490b-90a5-2ec1e9598c65", + "name" : "saml_organization", + "description" : "Organization Membership", + "protocol" : "saml", + "attributes" : { + "display.on.consent.screen" : "false" + }, + "protocolMappers" : [ { + "id" : "55c44c17-646e-483c-aed9-fbae2531192d", + "name" : "organization", + "protocol" : "saml", + "protocolMapper" : "saml-organization-membership-mapper", + "consentRequired" : false, + "config" : { } + } ] + }, { + "id" : "ef6d2a5b-32ef-4545-98dd-f0f17befbf4b", + "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" : "29a74667-da1a-4e27-ac9b-d8db3d8e3ce3", + "name" : "groups", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-realm-role-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "multivalued" : "true", + "user.attribute" : "foo", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "groups", + "jsonType.label" : "String" + } + }, { + "id" : "73979be6-8fda-4b04-a821-c49c5319cf65", + "name" : "upn", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "username", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "upn", + "jsonType.label" : "String" + } + } ] + }, { + "id" : "a4a354ff-a162-4e20-b3de-0c861ebb9339", + "name" : "profile", + "description" : "OpenID Connect built-in scope: profile", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "consent.screen.text" : "${profileScopeConsentText}", + "display.on.consent.screen" : "true" + }, + "protocolMappers" : [ { + "id" : "59388447-df18-4468-a851-ee54f3691382", + "name" : "nickname", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "nickname", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "nickname", + "jsonType.label" : "String" + } + }, { + "id" : "5ab38ba2-d09b-4c82-bb60-82ed961c4629", + "name" : "birthdate", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "birthdate", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "birthdate", + "jsonType.label" : "String" + } + }, { + "id" : "f69b07c7-e27f-4c62-9a85-cf6a5ac24f5a", + "name" : "picture", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "picture", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "picture", + "jsonType.label" : "String" + } + }, { + "id" : "c337f8fb-4a27-4161-aeae-6f5393425fdd", + "name" : "full name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-full-name-mapper", + "consentRequired" : false, + "config" : { + "id.token.claim" : "true", + "introspection.token.claim" : "true", + "access.token.claim" : "true", + "userinfo.token.claim" : "true" + } + }, { + "id" : "85cf7a83-72b2-40f3-8d5a-94d89e784003", + "name" : "zoneinfo", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "zoneinfo", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "zoneinfo", + "jsonType.label" : "String" + } + }, { + "id" : "fe78c20c-c1cc-415a-aeb4-01255711e192", + "name" : "middle name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "middleName", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "middle_name", + "jsonType.label" : "String" + } + }, { + "id" : "9659f64e-74ae-4c9e-a3dc-da50d31a5d55", + "name" : "family name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "lastName", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "family_name", + "jsonType.label" : "String" + } + }, { + "id" : "77de0854-9be9-4968-9999-545715bc8335", + "name" : "given name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "firstName", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "given_name", + "jsonType.label" : "String" + } + }, { + "id" : "b831aebf-44aa-4c88-a5a2-93ce71f8b226", + "name" : "username", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "username", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "preferred_username", + "jsonType.label" : "String" + } + }, { + "id" : "e1b3f4b0-f5de-4023-ba51-479cb531eabb", + "name" : "updated at", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "updatedAt", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "updated_at", + "jsonType.label" : "long" + } + }, { + "id" : "ca77142e-ba2a-471d-8d4a-b8cfb92bcad9", + "name" : "profile", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "profile", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "profile", + "jsonType.label" : "String" + } + }, { + "id" : "a540fdf9-d2f5-40f8-be99-3545ea5066f7", + "name" : "website", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "website", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "website", + "jsonType.label" : "String" + } + }, { + "id" : "f0f1a6d8-bbb7-4322-882e-c373dd7b3c03", + "name" : "locale", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "locale", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "locale", + "jsonType.label" : "String" + } + }, { + "id" : "62c83a8f-83cc-4d28-b1b3-edb24685fc88", + "name" : "gender", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "gender", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "gender", + "jsonType.label" : "String" + } + } ] + }, { + "id" : "c8bba8f0-0dc4-45f3-81d9-910ecd7af19a", + "name" : "role_list", + "description" : "SAML role list", + "protocol" : "saml", + "attributes" : { + "consent.screen.text" : "${samlRoleListScopeConsentText}", + "display.on.consent.screen" : "true" + }, + "protocolMappers" : [ { + "id" : "c8bf85c6-af49-4214-87b1-a1ab8a37fdb0", + "name" : "role list", + "protocol" : "saml", + "protocolMapper" : "saml-role-list-mapper", + "consentRequired" : false, + "config" : { + "single" : "false", + "attribute.nameformat" : "Basic", + "attribute.name" : "Role" + } + } ] + }, { + "id" : "5a669800-ce2d-44de-a644-7c579173ae2a", + "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", + "consent.screen.text" : "", + "display.on.consent.screen" : "false" + }, + "protocolMappers" : [ { + "id" : "6940024d-bf71-42ed-952a-d1f73667d8de", + "name" : "allowed web origins", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-allowed-origins-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "access.token.claim" : "true" + } + } ] + }, { + "id" : "2ee8d8a9-d6ca-4b7f-938e-55bbb20188e8", + "name" : "roles", + "description" : "OpenID Connect scope for add user roles to the access token", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "false", + "consent.screen.text" : "${rolesScopeConsentText}", + "display.on.consent.screen" : "true" + }, + "protocolMappers" : [ { + "id" : "bf02d5df-0af8-446f-ab85-ac81169ddf33", + "name" : "client roles", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-client-role-mapper", + "consentRequired" : false, + "config" : { + "user.attribute" : "foo", + "introspection.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "resource_access.${client_id}.roles", + "jsonType.label" : "String", + "multivalued" : "true" + } + }, { + "id" : "89a8e566-6c89-493c-a64c-69569a0ac487", + "name" : "audience resolve", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-audience-resolve-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "access.token.claim" : "true" + } + }, { + "id" : "7bb946ff-a185-47f0-a581-1a3d8a9d002e", + "name" : "realm roles", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-realm-role-mapper", + "consentRequired" : false, + "config" : { + "user.attribute" : "foo", + "introspection.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "realm_access.roles", + "jsonType.label" : "String", + "multivalued" : "true" + } + } ] + }, { + "id" : "3e208995-4ad7-4dd2-b24a-2125522cd8d6", + "name" : "organization", + "description" : "Additional claims about the organization a subject belongs to", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "consent.screen.text" : "${organizationScopeConsentText}", + "display.on.consent.screen" : "true" + }, + "protocolMappers" : [ { + "id" : "71595dca-ff58-4d32-a896-e89e54abfc97", + "name" : "organization", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-organization-membership-mapper", + "consentRequired" : false, + "config" : { + "id.token.claim" : "true", + "introspection.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "organization", + "jsonType.label" : "String", + "multivalued" : "true" + } + } ] + }, { + "id" : "c8247fa9-a443-4d3a-98c1-f77102d22a0b", + "name" : "acr", + "description" : "OpenID Connect scope for add acr (authentication context class reference) to the token", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "false", + "display.on.consent.screen" : "false" + }, + "protocolMappers" : [ { + "id" : "d5979017-4f94-4232-ace3-53e8f9c01b89", + "name" : "acr loa level", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-acr-mapper", + "consentRequired" : false, + "config" : { + "id.token.claim" : "true", + "introspection.token.claim" : "true", + "access.token.claim" : "true" + } + } ] + }, { + "id" : "46162a47-e6b3-4511-8ece-d47808b7a951", + "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" : "d59fa3e3-3e96-442a-90d7-ce19e61aa62a", + "name" : "phone", + "description" : "OpenID Connect built-in scope: phone", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "consent.screen.text" : "${phoneScopeConsentText}", + "display.on.consent.screen" : "true" + }, + "protocolMappers" : [ { + "id" : "0acb7991-6bc2-43d3-9bab-737c25066ee0", + "name" : "phone number verified", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "phoneNumberVerified", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "phone_number_verified", + "jsonType.label" : "boolean" + } + }, { + "id" : "cd8671b8-c6df-447f-935d-c741eed07041", + "name" : "phone number", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "phoneNumber", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "phone_number", + "jsonType.label" : "String" + } + } ] + }, { + "id" : "79b33ecb-fbc2-42cc-a091-fdd84a8a9cf2", + "name" : "basic", + "description" : "OpenID Connect scope for add all basic claims to the token", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "false", + "display.on.consent.screen" : "false" + }, + "protocolMappers" : [ { + "id" : "4679d850-5b86-4c75-915c-a002a4afa90c", + "name" : "auth_time", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usersessionmodel-note-mapper", + "consentRequired" : false, + "config" : { + "user.session.note" : "AUTH_TIME", + "id.token.claim" : "true", + "introspection.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "auth_time", + "jsonType.label" : "long" + } + }, { + "id" : "abc3e3ab-5e2e-4a5d-88ef-07d25efb53a0", + "name" : "sub", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-sub-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "access.token.claim" : "true" + } + } ] + }, { + "id" : "d18cb6e9-9480-4927-921e-d872b6c01606", + "name" : "email", + "description" : "OpenID Connect built-in scope: email", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "consent.screen.text" : "${emailScopeConsentText}", + "display.on.consent.screen" : "true" + }, + "protocolMappers" : [ { + "id" : "92aec799-88a0-4006-8c21-f12a201fadc3", + "name" : "email", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "email", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "email", + "jsonType.label" : "String" + } + }, { + "id" : "876aafa0-39ec-4797-8647-89b01c2c3c41", + "name" : "email verified", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-property-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "emailVerified", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "email_verified", + "jsonType.label" : "boolean" + } + } ] + } ], + "defaultDefaultClientScopes" : [ "role_list", "saml_organization", "profile", "email", "roles", "web-origins", "acr", "basic" ], + "defaultOptionalClientScopes" : [ "offline_access", "address", "phone", "microprofile-jwt", "organization" ], + "browserSecurityHeaders" : { + "contentSecurityPolicyReportOnly" : "", + "xContentTypeOptions" : "nosniff", + "referrerPolicy" : "no-referrer", + "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, + "identityProviders" : [ ], + "identityProviderMappers" : [ ], + "components" : { + "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy" : [ { + "id" : "b4b7ad08-eda3-4014-863f-410e302928a0", + "name" : "Trusted Hosts", + "providerId" : "trusted-hosts", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "host-sending-registration-request-must-match" : [ "true" ], + "client-uris-must-match" : [ "true" ] + } + }, { + "id" : "6f9b09ba-a4df-4450-baef-21ca5b38fb79", + "name" : "Max Clients Limit", + "providerId" : "max-clients", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "max-clients" : [ "200" ] + } + }, { + "id" : "f186f1a0-89d0-43eb-9b13-712bc794eb0b", + "name" : "Allowed Client Scopes", + "providerId" : "allowed-client-templates", + "subType" : "authenticated", + "subComponents" : { }, + "config" : { + "allow-default-scopes" : [ "true" ] + } + }, { + "id" : "c00ecb53-8184-455c-bc50-338f7acdffab", + "name" : "Consent Required", + "providerId" : "consent-required", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { } + }, { + "id" : "1688c409-e2a3-4fb0-a333-fae9aebe723d", + "name" : "Full Scope Disabled", + "providerId" : "scope", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { } + }, { + "id" : "1900175a-ff1c-46b6-9a1a-daa8cbc7b275", + "name" : "Allowed Protocol Mapper Types", + "providerId" : "allowed-protocol-mappers", + "subType" : "authenticated", + "subComponents" : { }, + "config" : { + "allowed-protocol-mapper-types" : [ "oidc-usermodel-attribute-mapper", "oidc-full-name-mapper", "oidc-address-mapper", "oidc-sha256-pairwise-sub-mapper", "saml-role-list-mapper", "saml-user-property-mapper", "oidc-usermodel-property-mapper", "saml-user-attribute-mapper" ] + } + }, { + "id" : "a773476d-4312-4aa1-86d5-bbf34a8b6427", + "name" : "Allowed Client Scopes", + "providerId" : "allowed-client-templates", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "allow-default-scopes" : [ "true" ] + } + }, { + "id" : "5c5f2f31-f7d9-4276-96a8-ab18d75d6d40", + "name" : "Allowed Protocol Mapper Types", + "providerId" : "allowed-protocol-mappers", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "allowed-protocol-mapper-types" : [ "saml-user-attribute-mapper", "oidc-usermodel-property-mapper", "saml-user-property-mapper", "oidc-full-name-mapper", "oidc-address-mapper", "oidc-usermodel-attribute-mapper", "oidc-sha256-pairwise-sub-mapper", "saml-role-list-mapper" ] + } + } ], + "org.keycloak.keys.KeyProvider" : [ { + "id" : "c7481f9f-0d25-4399-87fb-cd6ca77c98a0", + "name" : "rsa-generated", + "providerId" : "rsa-generated", + "subComponents" : { }, + "config" : { + "privateKey" : [ "MIIEowIBAAKCAQEAnSnYxW3Npy1y7XQccmo9bEUm1G0zWM6ldVkpeQ4egiRNfPXHjyOI7WIprkubkfsBj0F9SYRLiyvVJC8El61sXWWKWARmv4AD3zGSjIZh4FTFu6KFp6RHf/TLUwcxCJe/2wKhwAhj9G6dy5eNSaLr0Fbn0eWwXV0AnkvmBZod0o3A421ODZlby4Fp8Rscvnb0KNraS8v8pVewIFZ+kimaZ5fxv11h894kdqV1OSPc6bTfkyO4pQkEnPnIvEywjZbIl+2WhUvzZkTOmF1JlhcRn9FywsO1kUsqox6V6O5XvbPbgG53arVx96Mf+9goI7W1c3zNiLXdO5pGQIDN8+HC4QIDAQABAoIBACtYbkPNS+nuvALHgk0ABh7FV7dAwuecXr/lrm2HulxUXNa5BqfwugQWjb5XdfmmC8ER49cR2K/yj99h5Fdc6kU8CxLJa/km+mplRrDhIz92we5FHa2lvl6JCkbE9f4TtENYD3piFgdFNtn+22XSvE0Cmv8l5SVq0A/YiQFEhzMZnNJ8AiJEhU7/s8Fj7SsPOfBSPHE1P20TfVeCI96kv3g3H+xOq80zCBeeR6bUS8SqOtj8ZVoDQMLdSNKenMCgtTaAWvMhsXdlHvxh2g3vF20n57fF7ak39/ClXTMiMJXsMOjWGMIh6f3frbZ8E+j4YtcLHAee3ns4/tbHZOW1yZ0CgYEAyntkA3zMVhu/DFDYanGo6qW8AFeonayhzsv/TBgqlfRW3rAb0XlAWIyzOgq1c0c6r8FUx8HfxoksZlFfB3NpPYd/4uo5gm7x3+nZFfSsBWgtPUihf4ab3hh6FDs1SPmpglW+Bno/Auq1hs//QBuwZS7G8uXp1AXzSiRcniIj78sCgYEAxrQNeY4koK5tgNWeKzKGyDZIWlnc24hIu4rDU/KAjW/yHNN6nLtjGZ5AtKhc9IFn/nUtcoDU4B3xR0hHa/TPEA3B/p8SNMkPAA2GiE0d3gcSM8D0ZrC2DXbPT1mEX+B7uifxBFjD4F+TOo8JA2E7CYugUy7NQKhBFGOMiCAcaoMCgYEAxzqJufS9iaxbWt7hUjrrnZXdWejme940h6mpVVIh9NIp94sIlB/d9ELcxqtqxja7w9tSdAqLCGpISZSfEFG6p1P/vWIBnBlV3J/Xfgb0i41plfc1EKl+DBXBaM6pK1icNSXwh6cZAOG4IyHdYVLdSXGxR6Z5YKlKLRWCHAYznekCgYBT1fX1em6jyNR0zixs52WMpbKDRHT1vxuI3TQqgB+TDU9msCeEZ1+ZmHaOgpatpse8ya3CKO8oHBoHzEwuFV0j1doq1uy0jrwgdpRzf64BVpRsd5XmmdpF9gHj5c3/MdSiJ2X+QoFqcojI5T6VSXnCPkIHtrBs/lBvDM1nRb0XhwKBgDiyrcGGv3eevvx/xQKZkw8BK+YtEc81C52YjVKHSZ7wLMWA+hKDv6ls2fR51Thll1GnIC12iDH0ES+ulr1NJ/90XCA5HGwuVz6TjxmldFe066wCap6k7v6sJlY0UlBMWyLUvSNZBHcVYH9OLa+zEFiX0hoR3IWBwdj4tIveKrei" ], + "keyUse" : [ "SIG" ], + "certificate" : [ "MIIClzCCAX8CBgGWPnnAvjANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDAR0ZXN0MB4XDTI1MDQxNjExNTk0OFoXDTM1MDQxNjEyMDEyOFowDzENMAsGA1UEAwwEdGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ0p2MVtzactcu10HHJqPWxFJtRtM1jOpXVZKXkOHoIkTXz1x48jiO1iKa5Lm5H7AY9BfUmES4sr1SQvBJetbF1lilgEZr+AA98xkoyGYeBUxbuihaekR3/0y1MHMQiXv9sCocAIY/RuncuXjUmi69BW59HlsF1dAJ5L5gWaHdKNwONtTg2ZW8uBafEbHL529Cja2kvL/KVXsCBWfpIpmmeX8b9dYfPeJHaldTkj3Om035MjuKUJBJz5yLxMsI2WyJftloVL82ZEzphdSZYXEZ/RcsLDtZFLKqMelejuV72z24Bud2q1cfejH/vYKCO1tXN8zYi13TuaRkCAzfPhwuECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAWpvrgepMfJeQBSh+6Ifpe6olWIORLkzJ13Xhv/fjbFk4vH0mb9UoG5qF53T3M2TzTF9B0Rp47nie26lPCRcuYoVRux5MH5lzU6Toge/rE620J93/6jWX+dTsYW/0lFF9/lBWNmwr2xe9lxGSvDygtxoWr8ruAzdH24Se0Deo+HJk6YEHryOrvNg/e/MMEilSTv2rOG/CzQKK8Webu5zNj6x8vqJM82P0tlMgv40aDVoLPSu7pQf6Hj3Skz8knc1U6gV4JcBO602Pif5w/axk8oCzWjw1ut9tDNE4m0VB/uNMkU3oDCC0Zwfm0GPJBKGyl6ZQ4sMkT/M2DjaBQfvP1Q==" ], + "priority" : [ "100" ] + } + }, { + "id" : "2f871868-4ce3-48de-9929-fc59cf008fe7", + "name" : "aes-generated", + "providerId" : "aes-generated", + "subComponents" : { }, + "config" : { + "kid" : [ "d586998a-a144-493a-9eda-f791d88fe613" ], + "secret" : [ "M0aIVcP2VTSW7vFhG_fDog" ], + "priority" : [ "100" ] + } + }, { + "id" : "75438b30-2cfa-44d2-ac7b-d7195c875bfc", + "name" : "hmac-generated-hs512", + "providerId" : "hmac-generated", + "subComponents" : { }, + "config" : { + "kid" : [ "a18428cf-f714-4fb1-9263-247dfa4c17b6" ], + "secret" : [ "0Lce2dkRLmzsj5t6K-bE7SmBtOGAGBzP6UM7B3w0g7VXiYRPYGvNQFXWq-36v_En5IRTULMZWNnPGfFuZkQkq4JrK1jqK25jeL77SWCnoQvdJl9mT7fLSj_-5kx3YRkSsTxJNo3zivDNTeIssoySg9u3Gk0ycjnp5FopkCS_ldE" ], + "priority" : [ "100" ], + "algorithm" : [ "HS512" ] + } + }, { + "id" : "62755f35-490a-43ce-ad5f-dc0f8850351b", + "name" : "rsa-enc-generated", + "providerId" : "rsa-enc-generated", + "subComponents" : { }, + "config" : { + "privateKey" : [ "MIIEpQIBAAKCAQEAsEqc3bbbmK/qAzTZVwq+gV57oaAaDph2S+FPRt5qRbV8ZOCALk4w6NOn18kUcMP6kuDymOq6x8Nm4uZTu5SXzDzoEehclPjlPvlFA2Elw+Ik9ZtfYZaBrqDxWAq4q6m10SP88IaGA9Lh5d5+EcA/FUJMwwdtPiH1F7b6os8oNwMewOCs85B8r8hBK4xXG1lWJViJ112YK1Qt6uOn/2p6e5tNaRDLMTQ1ETt39i1cMnuw9pm3Uu23DdnycgQb/MSTQgYPf9rOH3Hn7fD8NhkRP/u3fJFl2a5giZAAqhZl7mHgK/Jsb+vGDbfCpDaVfvHcLudUHhwYbf431RvKTa/OuQIDAQABAoIBAAGWooQy5hahmyOtQboRjDbhMY76wNFZ2VMEtDCox19aa8UC6tS/+pvWcGA23RrJAUR6h2UOVGGBZrdrqNx7UcTS1ap3pAHpnjMrjs4hfXQe4QNfg4P/FasLozEOY6yUSmGYh+po4+M77rIiB5PEXi0kXEmzku3o42rzyJ1X7X5VYoRP0303tqZHQyqt5fsD8TVWRjMPtUczSGAcllflNHexIb7knBzYedvhiMdsu3ItDD01xcKt6Owx52yqdPrRzuwveV+qb2cxqjGhPT3xQiHB6lezGabL/6CZBS/Du7ADIUdCKH6ulNzDEA4dltf6KyszX3XB8/BMzXTPQ+9PQQECgYEA6kfDzROaCVtBw30IT1pEfKKMcvqsLo4wFqQpz41TY7i4xPwTAADCbc8uEAVjcpasjzOnVHRpLy84E0Kei/CfIIKW02130apIT+Ct4CpQlrksISfWYK3yejYEgnjoQA5cz30MfCZ1Iv3yd4kyv6az1YedUsh0INKExEpMxzYmcfkCgYEAwKKVRABjwgUCuKrNcco4Fd4QI0n7na1fPCwUeROPPIXuQ+rHKpXaK66yuovFap+2rJisKr9yRzpmNaTvXMVbGeBteZKT8f82boWYkhq9ZDFigsWwrhNkJH8M/tPKBed4CqmBruSiZ4TbRBd78OlchtGKUJ05+2ZDJhu6h7hLcsECgYEA3ZnAJSzTWOJO1EBiKdzyRwnh26gsUWUBK4lgWwgMxpilfP4KYshVIFUF9vWB2ZOX6WQsdIAgNhdt7RnoqemSOsgLLjWvTkzJVXTqQs0DqW3BxiLObmhaoSqTHW6MEHsFYuWfd5dQ2SZnwJWvwQRHukQqlXEyFxHJr3mwIgagWtECgYEAnWp+1cIHwz36+lNBbZJVgLEluOC1SCWsJGzVEhgEve2oRkHuHYO1dGrfTQf4/GeljKd6Ubh+t2wmqAGvRL1V8/BtJaK9WU03+tsbUZGeYOjmWn0YIzhfZl+YjAkgFvEPLI9WFUhq2nM8Bwm106mvXdCP2c6R2jm398VDCbN1nEECgYEAhSvLDtOthHGeE2RBOir15XJi+UjNncncyGrr54MF2K2JIT4cv3Y92AMRJhz0k43k4XQbXv5rLwxnzgEk7sLk3f53CPik95Q78ll8ZvwZyMz5CsV8HjeeGcQxoFqzxwOKFTUcgGnfUnSNlL6T25/cOHuVydqwX7a0NCd3+5YV/8I=" ], + "keyUse" : [ "ENC" ], + "certificate" : [ "MIIClzCCAX8CBgGWPnnBCjANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDAR0ZXN0MB4XDTI1MDQxNjExNTk0OFoXDTM1MDQxNjEyMDEyOFowDzENMAsGA1UEAwwEdGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALBKnN2225iv6gM02VcKvoFee6GgGg6YdkvhT0beakW1fGTggC5OMOjTp9fJFHDD+pLg8pjqusfDZuLmU7uUl8w86BHoXJT45T75RQNhJcPiJPWbX2GWga6g8VgKuKuptdEj/PCGhgPS4eXefhHAPxVCTMMHbT4h9Re2+qLPKDcDHsDgrPOQfK/IQSuMVxtZViVYidddmCtULerjp/9qenubTWkQyzE0NRE7d/YtXDJ7sPaZt1Lttw3Z8nIEG/zEk0IGD3/azh9x5+3w/DYZET/7t3yRZdmuYImQAKoWZe5h4CvybG/rxg23wqQ2lX7x3C7nVB4cGG3+N9Ubyk2vzrkCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAWKQx3QX3eR6KhkUE6NhhjNNbPcaIYE1/pcpeCxFvW6s4Awtmm8hmYHO3wlC7q+4JdJiq2vo2exphIUWyzGl0NS2j1RaLA2BRyP2jKFLPIsF3HIGg8lBcswXKNcqzqunHg//PGoKmzPjuGQylzeZbObMCkbdoMfbv4t0o6QUbpAbWZ1l8/7W+rimURTPP0mgUMPkIR4pnU9uItwb9pDqtFUvIxxd1rqleUQ9dfXUTCq/Ec0txnApv7MQVD601tRWIbVXyUKQWirIK9QsbW8Izjs3MBZ6dZo/S1ec7cgDMEiw8OgXNlm7TSRADJ5C1I+WVehQd3+DSEA4WLbamT6yhiA==" ], + "priority" : [ "100" ], + "algorithm" : [ "RSA-OAEP" ] + } + } ] + }, + "internationalizationEnabled" : false, + "supportedLocales" : [ ], + "authenticationFlows" : [ { + "id" : "0620c000-55d6-4297-83a4-29fb811bf31a", + "alias" : "Account verification options", + "description" : "Method with which to verity the existing account", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "idp-email-verification", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "ALTERNATIVE", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "Verify Existing Account by Re-authentication", + "userSetupAllowed" : false + } ] + }, { + "id" : "9eb72380-8b37-46ef-b97a-266ad6144a28", + "alias" : "Browser - Conditional OTP", + "description" : "Flow to determine if the OTP is required for the authentication", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "auth-otp-form", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "3512ba71-d34f-429f-97c6-b633c93ec667", + "alias" : "Browser - Conditional Organization", + "description" : "Flow to determine if the organization identity-first login is to be used", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "organization", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "407f6fde-7273-4248-9781-15d1be7810b4", + "alias" : "Direct Grant - Conditional OTP", + "description" : "Flow to determine if the OTP is required for the authentication", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "direct-grant-validate-otp", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "f5250e54-461b-4225-92fd-2337de0f6005", + "alias" : "First Broker Login - Conditional Organization", + "description" : "Flow to determine if the authenticator that adds organization members is to be used", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "idp-add-organization-member", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "fcb5ee37-cded-47bd-a078-5633d9fcaa57", + "alias" : "First broker login - Conditional OTP", + "description" : "Flow to determine if the OTP is required for the authentication", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "auth-otp-form", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "76091fc6-1521-4b44-b29b-3435999229d7", + "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", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "Account verification options", + "userSetupAllowed" : false + } ] + }, { + "id" : "344643c9-1447-4f00-ac02-c3d66aee834d", + "alias" : "Organization", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticatorFlow" : true, + "requirement" : "CONDITIONAL", + "priority" : 10, + "autheticatorFlow" : true, + "flowAlias" : "Browser - Conditional Organization", + "userSetupAllowed" : false + } ] + }, { + "id" : "7c7ab0b1-5f2d-493d-947e-4cbf24d715d6", + "alias" : "Reset - Conditional OTP", + "description" : "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "reset-otp", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "274c10eb-36a7-474a-abc8-9274c50a6016", + "alias" : "User creation or linking", + "description" : "Flow for the existing/non-existing user alternatives", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticatorConfig" : "create unique user config", + "authenticator" : "idp-create-user-if-unique", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "ALTERNATIVE", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "Handle Existing Account", + "userSetupAllowed" : false + } ] + }, { + "id" : "da5a2ea9-4df2-49d6-964d-2e08d7efa869", + "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", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "CONDITIONAL", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "First broker login - Conditional OTP", + "userSetupAllowed" : false + } ] + }, { + "id" : "8a48643d-e66e-4f15-843a-0f336ffe24c0", + "alias" : "browser", + "description" : "Browser based authentication", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "auth-cookie", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "auth-spnego", + "authenticatorFlow" : false, + "requirement" : "DISABLED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "identity-provider-redirector", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 25, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "ALTERNATIVE", + "priority" : 26, + "autheticatorFlow" : true, + "flowAlias" : "Organization", + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "ALTERNATIVE", + "priority" : 30, + "autheticatorFlow" : true, + "flowAlias" : "forms", + "userSetupAllowed" : false + } ] + }, { + "id" : "067a99b0-93c9-4df0-a60f-894767571e15", + "alias" : "clients", + "description" : "Base authentication for clients", + "providerId" : "client-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "client-secret", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "client-jwt", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "client-secret-jwt", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 30, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "client-x509", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 40, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "b65867de-ba79-40d8-8b42-64f1b170a3f0", + "alias" : "direct grant", + "description" : "OpenID Connect Resource Owner Grant", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "direct-grant-validate-username", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "direct-grant-validate-password", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "CONDITIONAL", + "priority" : 30, + "autheticatorFlow" : true, + "flowAlias" : "Direct Grant - Conditional OTP", + "userSetupAllowed" : false + } ] + }, { + "id" : "66b14857-1092-45fe-949d-40af91ab80e2", + "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", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "067f7472-9ac2-4a9e-a330-a4c3c2bd9b60", + "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", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "User creation or linking", + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "CONDITIONAL", + "priority" : 50, + "autheticatorFlow" : true, + "flowAlias" : "First Broker Login - Conditional Organization", + "userSetupAllowed" : false + } ] + }, { + "id" : "1481c1d1-08c1-4dcf-947f-33e7e9336ef7", + "alias" : "forms", + "description" : "Username, password, otp and other auth forms.", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "auth-username-password-form", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "CONDITIONAL", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "Browser - Conditional OTP", + "userSetupAllowed" : false + } ] + }, { + "id" : "64d969f4-baf9-446b-9ba1-b36721711594", + "alias" : "registration", + "description" : "Registration flow", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "registration-page-form", + "authenticatorFlow" : true, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : true, + "flowAlias" : "registration form", + "userSetupAllowed" : false + } ] + }, { + "id" : "e4c76812-79ae-4237-b7d6-4809a43fda16", + "alias" : "registration form", + "description" : "Registration form", + "providerId" : "form-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "registration-user-creation", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "registration-password-action", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 50, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "registration-recaptcha-action", + "authenticatorFlow" : false, + "requirement" : "DISABLED", + "priority" : 60, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "registration-terms-and-conditions", + "authenticatorFlow" : false, + "requirement" : "DISABLED", + "priority" : 70, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "d3f0087b-4e9b-45f9-9138-f965a80e2474", + "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", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "reset-credential-email", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "reset-password", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 30, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "CONDITIONAL", + "priority" : 40, + "autheticatorFlow" : true, + "flowAlias" : "Reset - Conditional OTP", + "userSetupAllowed" : false + } ] + }, { + "id" : "c7f62cee-a5ed-42b4-bcc5-11beeaf1ac02", + "alias" : "saml ecp", + "description" : "SAML ECP Profile Authentication Flow", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "http-basic-authenticator", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + } ], + "authenticatorConfig" : [ { + "id" : "b99be709-8dec-412c-89d3-226bfbaeb65a", + "alias" : "create unique user config", + "config" : { + "require.password.update.after.registration" : "false" + } + }, { + "id" : "7c5ca9a1-4681-46c6-af34-cad7175f621c", + "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" : "delete_account", + "name" : "Delete Account", + "providerId" : "delete_account", + "enabled" : false, + "defaultAction" : false, + "priority" : 60, + "config" : { } + }, { + "alias" : "webauthn-register", + "name" : "Webauthn Register", + "providerId" : "webauthn-register", + "enabled" : true, + "defaultAction" : false, + "priority" : 70, + "config" : { } + }, { + "alias" : "webauthn-register-passwordless", + "name" : "Webauthn Register Passwordless", + "providerId" : "webauthn-register-passwordless", + "enabled" : true, + "defaultAction" : false, + "priority" : 80, + "config" : { } + }, { + "alias" : "VERIFY_PROFILE", + "name" : "Verify Profile", + "providerId" : "VERIFY_PROFILE", + "enabled" : true, + "defaultAction" : false, + "priority" : 90, + "config" : { } + }, { + "alias" : "delete_credential", + "name" : "Delete Credential", + "providerId" : "delete_credential", + "enabled" : true, + "defaultAction" : false, + "priority" : 100, + "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", + "firstBrokerLoginFlow" : "first broker login", + "attributes" : { + "cibaBackchannelTokenDeliveryMode" : "poll", + "cibaExpiresIn" : "120", + "cibaAuthRequestedUserHint" : "login_hint", + "oauth2DeviceCodeLifespan" : "600", + "oauth2DevicePollingInterval" : "5", + "parRequestUriLifespan" : "60", + "cibaInterval" : "5", + "realmReusableOtpCode" : "false" + }, + "keycloakVersion" : "26.0.6", + "userManagedAccessAllowed" : false, + "organizationsEnabled" : false, + "clientProfiles" : { + "profiles" : [ ] + }, + "clientPolicies" : { + "policies" : [ ] + } +} \ No newline at end of file diff --git a/nix/services/keycloak_test.nix b/nix/services/keycloak_test.nix new file mode 100644 index 00000000..af3ffd36 --- /dev/null +++ b/nix/services/keycloak_test.nix @@ -0,0 +1,48 @@ +{ pkgs, config, ... }: +let + name = "k1"; + inherit (config.services.keycloak.${name}) dataDir; + realmSrc = ./keycloak/test-realms/test.json; +in +{ + services.keycloak.${name} = { + enable = true; + settings.http-port = 8091; + + database.type = "dev-file"; + + realms = { + master = { + export = { + enable = true; + }; + }; + + test = { + import = realmSrc; + export = { + enable = true; + }; + }; + }; + }; + + settings.processes.test = + let + test-export = pkgs.callPackage ./keycloak/test-realms/export.nix { + process-compose = config.package; + realmDstDir = "${dataDir}/realm-export"; + pcSocketPath = config.cli.options.unix-socket; + keycloak-name = name; + }; + in + { + command = pkgs.writeShellApplication { + runtimeInputs = [ test-export ]; + text = "test-export"; + name = "${name}-test"; + }; + + depends_on.${name}.condition = "process_healthy"; + }; +} diff --git a/test/flake.nix b/test/flake.nix index bd104f51..8687883f 100644 --- a/test/flake.nix +++ b/test/flake.nix @@ -48,6 +48,8 @@ "${inputs.services-flake}/nix/services/dynamodb-local_test.nix" "${inputs.services-flake}/nix/services/elasticmq_test.nix" "${inputs.services-flake}/nix/services/grafana_test.nix" + "${inputs.services-flake}/nix/services/keycloak_test.nix" + "${inputs.services-flake}/nix/services/keycloak-certs_test.nix" "${inputs.services-flake}/nix/services/memcached_test.nix" "${inputs.services-flake}/nix/services/mysql/mysql_test.nix" "${inputs.services-flake}/nix/services/nats-server_test.nix"