diff --git a/backend/app/main.py b/backend/app/main.py index 84f54d38b..2aaaaeb9d 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -80,6 +80,9 @@ logger = logging.getLogger(__name__) +if not settings.API_V2_STR.startswith("/"): + settings.API_V2_STR = "/" + settings.API_V2_STR + app = FastAPI( title=settings.APP_NAME, openapi_url=f"{settings.API_V2_STR}/openapi.json", diff --git a/docker-compose.subdirectory.yml b/docker-compose.subdirectory.yml new file mode 100644 index 000000000..a71744f1e --- /dev/null +++ b/docker-compose.subdirectory.yml @@ -0,0 +1,271 @@ +version: '3.7' + +# Settings and configurations that are common for all minio containers +x-minio-common: &minio-common + image: quay.io/minio/minio:RELEASE.2022-01-25T19-56-04Z + command: server --console-address ":9001" http://minio{1...4}/data + restart: unless-stopped + environment: + MINIO_ROOT_USER: minioadmin + MINIO_ROOT_PASSWORD: minioadmin + networks: + - clowder2 + healthcheck: + test: [ "CMD", "curl", "-f", "http://localhost:9000/minio/health/live" ] + interval: 30s + timeout: 20s + retries: 3 + +services: + + traefik: + image: traefik:v2.5 + restart: unless-stopped + command: + - --api.insecure=true + - --providers.docker + # - --entryPoints.web.address=:80 + ports: + # The HTTP port + - "80:80" + # The Web UI (enabled by --api.insecure=true) + - "8080:8080" + networks: + - clowder2 + volumes: + # So that Traefik can listen to the Docker events + - /var/run/docker.sock:/var/run/docker.sock + + backend: + image: 'clowder/clowder2-backend:2.0.0-beta.2' + restart: unless-stopped + build: + context: ./backend + networks: + - clowder2 + environment: + MONGODB_URL: mongodb://mongo:27017 + MINIO_SERVER_URL: minio-nginx:9000 + MINIO_EXTERNAL_SERVER_URL: minio-nginx:9000 + RABBITMQ_HOST: ${RABBITMQ_HOST:-rabbitmq} + RABBITMQ_USER: ${RABBITMQ_USER:-guest} + RABBITMQ_PASS: ${RABBITMQ_PASS:-guest} + API_HOST: ${API_HOST:-http://backend} + elasticsearch_url: http://elasticsearch:9200 + auth_base: http://localhost/${BASE_URL_ROUTE} + auth_realm: clowder + auth_client_id: clowder2-backend + auth_redirect_uri: http://localhost:80/${BASE_URL_ROUTE}/api/v2/auth + auth_url: http://localhost/${BASE_URL_ROUTE}/keycloak/realms/clowder/protocol/openid-connect/auth?client_id=clowder2-backend&response_type=code + oauth2_scheme_auth_url: http://keycloak:8080/keycloak/realms/clowder/protocol/openid-connect/auth?client_id=clowder2-backend&response_type=code + auth_register_url: http://localhost/${BASE_URL_ROUTE}/keycloak/realms/clowder/protocol/openid-connect/registrations?client_id=clowder2-backend&response_type=code&redirect_uri=http://localhost:80/api/v2/auth&scope=openid%20email + auth_token_url: http://keycloak:8080/${BASE_URL_ROUTE}/keycloak/realms/clowder/protocol/openid-connect/token + auth_server_url: http://keycloak:8080/${BASE_URL_ROUTE}/keycloak/ + keycloak_base: http://localhost/${BASE_URL_ROUTE}/api + frontend_url: http://localhost/${BASE_URL_ROUTE} + API_V2_STR: /${BASE_URL_ROUTE}/api/v2 + depends_on: + - mongo + - minio-nginx + - keycloak + - elasticsearch + - rabbitmq + labels: + - "traefik.enable=true" + - "traefik.http.routers.backend.rule=PathPrefix(`/${BASE_URL_ROUTE}/api`)" + - "traefik.http.routers.swagger.rule=PathPrefix(`/${BASE_URL_ROUTE}/docs`)" + - "traefik.http.services.backend.loadbalancer.server.port=80" + - "traefik.http.routers.backend.priority=5" + + frontend: + image: "clowder/clowder2-frontend:basename" + restart: unless-stopped + build: + context: ./frontend + networks: + - clowder2 + depends_on: + - backend + environment: + NODE_ENV: production + labels: + - "traefik.enable=true" + - "traefik.http.routers.frontend.rule=PathPrefix(`/`)" + - "traefik.http.services.frontend.loadbalancer.server.port=80" + - "traefik.http.routers.frontend.priority=1" + + extractors-heartbeat: + image: "clowder/clowder2-heartbeat:main" + build: + context: backend + dockerfile: heartbeat.Dockerfile + networks: + - clowder2 + restart: unless-stopped + environment: + MONGODB_URL: mongodb://mongo:27017 + RABBITMQ_HOST: ${RABBITMQ_HOST:-rabbitmq} + RABBITMQ_USER: ${RABBITMQ_USER:-guest} + RABBITMQ_PASS: ${RABBITMQ_PASS:-guest} + depends_on: + - mongo + - rabbitmq + + extractors-messages: + image: "clowder/clowder2-messages:main" + build: + context: backend + dockerfile: messages.Dockerfile + environment: + MONGODB_URL: mongodb://mongo:27017 + RABBITMQ_HOST: ${RABBITMQ_HOST:-rabbitmq} + RABBITMQ_USER: ${RABBITMQ_USER:-guest} + RABBITMQ_PASS: ${RABBITMQ_PASS:-guest} + networks: + - clowder2 + restart: unless-stopped + depends_on: + - mongo + - rabbitmq + + mongo: + image: mongo:5.0 + restart: unless-stopped + networks: + - clowder2 + volumes: + - mongo:/data/db + + # environment: + # MONGO_INITDB_ROOT_USERNAME: root + # MONGO_INITDB_ROOT_PASSWORD: example + + minio1: + <<: *minio-common + hostname: minio1 + volumes: + - data1:/data + + minio2: + <<: *minio-common + hostname: minio2 + volumes: + - data2:/data + + minio3: + <<: *minio-common + hostname: minio3 + volumes: + - data3:/data + + minio4: + <<: *minio-common + hostname: minio4 + volumes: + - data4:/data + + minio-nginx: + image: nginx:1.19.2-alpine + restart: unless-stopped + hostname: nginx + networks: + - clowder2 + volumes: + - ./deployments/docker/minio-nginx.conf:/etc/nginx/nginx.conf:ro + depends_on: + - minio1 + - minio2 + - minio3 + - minio4 + + postgres: + image: postgres + restart: unless-stopped + networks: + - clowder2 + volumes: + - postgres_data:/var/lib/postgresql/data + environment: + POSTGRES_DB: keycloak_prod + POSTGRES_USER: keycloak + POSTGRES_PASSWORD: password + + keycloak: + image: quay.io/keycloak/keycloak:20.0 + restart: unless-stopped + networks: + - clowder2 + volumes: + - ./scripts/keycloak/clowder-realm-subdirectory-example.json:/opt/keycloak/data/import/realm.json:ro + - ./scripts/keycloak/clowder-theme/:/opt/keycloak/themes/clowder-theme/:ro + command: + - start-dev + - --http-relative-path /${BASE_URL_ROUTE}/keycloak + - --import-realm + environment: + KEYCLOAK_ADMIN: admin + KEYCLOAK_ADMIN_PASSWORD: admin + KC_DB: postgres + KC_DB_URL_HOST: postgres + KC_DB_URL_DATABASE: keycloak_prod + KC_DB_USERNAME: keycloak + KC_DB_PASSWORD: password + depends_on: + - postgres + labels: + - "traefik.enable=true" + - "traefik.http.routers.keycloak.rule=PathPrefix(`/${BASE_URL_ROUTE}/keycloak`)" + - "traefik.http.services.keycloak.loadbalancer.server.port=8080" + - "traefik.http.routers.keycloak.priority=10" + + # message broker + rabbitmq: + image: rabbitmq:3-management-alpine + environment: + - RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS=-rabbitmq_management path_prefix "/rabbitmq" + - RABBITMQ_DEFAULT_USER=${RABBITMQ_USER:-guest} + - RABBITMQ_DEFAULT_PASS=${RABBITMQ_PASS:-guest} + expose: + - 5672 + - 15672 + # TODO remove + ports: + - "5672:5672" + - "15672:15672" + healthcheck: + test: [ "CMD", "nc", "-z", "localhost", "5672" ] + interval: 3s + timeout: 10s + retries: 3 + networks: + - clowder2 + volumes: + - rabbitmq:/var/lib/rabbitmq + + elasticsearch: + image: docker.elastic.co/elasticsearch/elasticsearch:8.3.3 + restart: unless-stopped + networks: + - clowder2 + environment: + - "cluster.name=clowder2" + - "discovery.type=single-node" + - "xpack.security.enabled=false" + - "xpack.security.http.ssl.enabled=false" + volumes: + - elasticsearch:/usr/share/elasticsearch/data + +## By default this config uses default local driver, +## For custom volumes replace with volume driver configuration. +volumes: + mongo: + data1: + data2: + data3: + data4: + postgres_data: + elasticsearch: + rabbitmq: + +networks: + clowder2: diff --git a/docs/docs/admins/deployatsubdirectory.md b/docs/docs/admins/deployatsubdirectory.md new file mode 100644 index 000000000..43c1672d3 --- /dev/null +++ b/docs/docs/admins/deployatsubdirectory.md @@ -0,0 +1,83 @@ +# Deploying Application to Subdirectory "clowder" + +This guide will walk you through the steps to deploy your frontend and backend applications to the +subdirectory `/clowder`, ensuring that both the frontend and backend are accessible under this path. + +## Step 1: Build the Frontend Image + +To deploy the frontend application under `/clowder`, build the Docker image with the appropriate build arguments: + +```bash +docker build --no-cache -t clowder/clowder2-frontend:basename \ + --build-arg BASE_URL_ROUTE=clowder \ + --build-arg CLOWDER_REMOTE_HOSTNAME="http://localhost/clowder" \ + . +``` + +- **`BASE_URL_ROUTE=clowder`**: Configures the frontend to operate under the `/clowder` subdirectory. +- **`CLOWDER_REMOTE_HOSTNAME=clowder`**: Point to the backend service that frontend will request, which should match + the subdirectory "/clowder" as well. + +## Step 2: Modify `docker-compose.subdirectory.yml` to Use the Built Image + +In your `docker-compose.subdirectory.yml`, update the frontend service to use the newly built image: +e.g. + +```yaml +frontend: + image: "clowder/clowder2-frontend:basename" +``` + +This ensures that the correct image, configured for the `/clowder` subdirectory, is used when deploying. + +## Step 3: Set the `BASE_URL_ROUTE` Environment Variable + +Before running Docker Compose, make sure the `BASE_URL_ROUTE` environment variable is set to `clowder`. This ensures +that both the frontend and backend services are configured to operate under the `/clowder` subdirectory. + +```bash +export BASE_URL_ROUTE=clowder +echo $BASE_URL_ROUTE +``` + +- This environment variable ensures that the backend is also aware that it needs to operate under the `/clowder` + subdirectory. + +## Step 4: Start the Services with Docker Compose + +Now, start your services using Docker Compose: + +```bash +docker compose -f docker-compose.subdirectory.yml up +``` + +- **Backend**: The backend service will be accessible at `http://localhost/clowder/api/v2`. +- **Frontend**: The frontend service will be accessible at `http://localhost/clowder/`. +- **Keycloak**: The Keycloak admin console will be accessible at `http://localhost/clowder/keycloak/`. + +## Step 5: Configuring Keycloak (Optional) + +If you're deploying Keycloak under a subdirectory other than `/clowder`, follow these steps to adjust the Keycloak +settings. If you're using `/clowder` as your subdirectory, this step is optional since the provided Docker Compose +setup will automatically load a pre-configured realm JSON file. + +1. **Login to Keycloak Admin Console**: Navigate to `http://localhost/clowder/keycloak` in your browser. +2. **Login Credentials**: + - **Username**: `admin` + - **Password**: `admin` +3. **Navigate to the Clowder Realm**: Go to `Realm Settings` > `General`. + 4.**Change Frontend URL**: + - Update the `Frontend URL` field to `http://localhost/clowder/keycloak`. + - Click `Save` to apply the changes. +5. **Find the Client**: + - Go to `Clients` in the Keycloak admin console. + - Select the client with `clientId`: `clowder2-backend`. +6. **Update the Root URL**: + - Set the `Root URL` to `http://localhost/clowder`. +7. **Update Redirect URIs**: + - Ensure that the `Redirect URIs` field includes `http://localhost/clowder/api/v2/auth`. +8. **Update Web Origins**: + - Ensure that the `Web Origins` field includes `http://localhost/clowder`. + +By following these steps, both your backend and frontend services will be correctly deployed under the `/clowder` +subdirectory, making them accessible through the configured paths. diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 946aa798a..0596c10c8 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -3,12 +3,18 @@ # ---------------------------------------------------------------------- FROM node:16.15.1 AS clowder-build -#ENV NODE_ENV=production WORKDIR /usr/src/app -# development or production -#ARG DEPLOY_ENV="" -#ENV DEPLOY_ENV="${DEPLOY_ENV:-}" +# Define the argument and environment variable +# Make sure BASE_URL_ROUTE starts with a slash and does not end with a slash +ARG BASE_URL_ROUTE="" +ENV BASE_URL_ROUTE=${BASE_URL_ROUTE:-} +RUN echo "Build time BASE_URL_ROUTE: ${BASE_URL_ROUTE}" + +# Add the build argument for CLOWDER_REMOTE_HOSTNAME to work with backend at a different host +ARG CLOWDER_REMOTE_HOSTNAME="" +ENV CLOWDER_REMOTE_HOSTNAME=${CLOWDER_REMOTE_HOSTNAME:-} +RUN echo "Build time CLOWDER_REMOTE_HOSTNAME: ${CLOWDER_REMOTE_HOSTNAME}" # copy only package for caching purposes COPY ["package.json", "package-lock.json*", "./"] @@ -28,12 +34,24 @@ RUN npm run build FROM nginx:alpine as clowder-runtime -RUN rm -rf /usr/share/nginx/html/ && \ - mkdir /usr/share/nginx/html && \ - mkdir /usr/share/nginx/html/public \ - mkdir /usr/share/nginx/html/styles +# Define the argument and environment variable +# Make sure BASE_URL_ROUTE starts with a slash and does not end with a slash +ARG BASE_URL_ROUTE="" +ENV BASE_URL_ROUTE=${BASE_URL_ROUTE:-} +RUN echo "Runtime BASE_URL_ROUTE: ${BASE_URL_ROUTE}" + +# Adjust the paths based on BASE_URL_ROUTE +RUN rm -rf /usr/share/nginx/html/ && \ + mkdir -p /usr/share/nginx/html/${BASE_URL_ROUTE}/public && \ + mkdir -p /usr/share/nginx/html/${BASE_URL_ROUTE}/styles + +# Copy the built application from the previous stage +COPY --from=clowder-build /usr/src/app/dist/ /usr/share/nginx/html/${BASE_URL_ROUTE} +COPY src/public /usr/share/nginx/html/${BASE_URL_ROUTE}/public/ +COPY src/styles /usr/share/nginx/html/${BASE_URL_ROUTE}/styles/ + +# Copy the NGINX configuration template +COPY clowder.conf.template /etc/nginx/conf.d/default.conf.template -COPY --from=clowder-build /usr/src/app/dist/ /usr/share/nginx/html/ -COPY src/public /usr/share/nginx/html/public/ -COPY src/styles /usr/share/nginx/html/styles/ -COPY clowder.conf /etc/nginx/conf.d/default.conf +# Use envsubst to substitute environment variables in the NGINX config template +CMD ["/bin/sh", "-c", "envsubst '${BASE_URL_ROUTE}' < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'"] diff --git a/frontend/clowder.conf b/frontend/clowder.conf deleted file mode 100644 index 4c373a118..000000000 --- a/frontend/clowder.conf +++ /dev/null @@ -1,19 +0,0 @@ -server { - listen 80; - - root /usr/share/nginx/html/; - - location / { - root /usr/share/nginx/html/; - index index.html; - try_files $uri /index.html; - } - location /public/ { - root /usr/share/nginx/html/; - } - - error_page 500 502 503 504 /50x.html; - location = /50x.html { - root html; - } -} diff --git a/frontend/clowder.conf.template b/frontend/clowder.conf.template new file mode 100644 index 000000000..e12a685e7 --- /dev/null +++ b/frontend/clowder.conf.template @@ -0,0 +1,17 @@ +server { + listen 80; + server_name localhost; + + location / { + root /usr/share/nginx/html/${BASE_URL_ROUTE}; + index index.html; + try_files $uri $uri/ /index.html; + } + + # redirect server error pages to the static page /50x.html + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html/${BASE_URL_ROUTE}; + } +} diff --git a/frontend/src/app.config.ts b/frontend/src/app.config.ts index 7c325852c..b338e20d8 100644 --- a/frontend/src/app.config.ts +++ b/frontend/src/app.config.ts @@ -7,6 +7,7 @@ interface Config { slackChannel: string; documentation: string; hostname: string; + baseUrlRoute: string; apikey: string; GHIssueBaseURL: string; jupyterHubURL: string; @@ -40,6 +41,8 @@ const hostname = process.env.CLOWDER_REMOTE_HOSTNAME || `${window.location.protocol}//${window.location.host}`; +const baseUrlRoute = process.env.BASE_URL_ROUTE || ""; + // TODO when add auth piece remove this env const apikey = process.env.APIKEY || ""; config["appVersion"] = "v2.0.0-beta.2"; @@ -48,6 +51,7 @@ config["slackChannel"] = "https://join.slack.com/t/clowder-software/shared_invite/enQtMzQzOTg0Nzk3OTUzLTYwZDlkZDI0NGI4YmI0ZjE5MTZiYmZhZTIyNWE1YzM0NWMwMzIxODNhZTA1Y2E3MTQzOTg1YThiNzkwOWQwYWE"; config["documentation"] = "https://clowder2.readthedocs.io/en/latest/"; config["hostname"] = hostname; +config["baseUrlRoute"] = baseUrlRoute; config["apikey"] = apikey; V2.OpenAPI.BASE = config.hostname; diff --git a/frontend/src/components/Layout.tsx b/frontend/src/components/Layout.tsx index a96716a61..038e62f1e 100644 --- a/frontend/src/components/Layout.tsx +++ b/frontend/src/components/Layout.tsx @@ -177,7 +177,7 @@ export default function PersistentDrawerLeft(props) { > - + {loggedOut ? ( <> - + Register - + Login diff --git a/frontend/src/components/Public.tsx b/frontend/src/components/Public.tsx index a0cbecb24..28d6067fe 100644 --- a/frontend/src/components/Public.tsx +++ b/frontend/src/components/Public.tsx @@ -104,9 +104,14 @@ export const Public = (): JSX.Element => {

No public datasets available.{" "} - Login or{" "} - register to create - datasets.{" "} + + Login + {" "} + or{" "} + + register + {" "} + to create datasets.{" "}

)} diff --git a/frontend/src/components/PublicLayout.tsx b/frontend/src/components/PublicLayout.tsx index d7c71b85a..87d3f4b0a 100644 --- a/frontend/src/components/PublicLayout.tsx +++ b/frontend/src/components/PublicLayout.tsx @@ -23,9 +23,7 @@ import PersonIcon from "@mui/icons-material/Person"; import VpnKeyIcon from "@mui/icons-material/VpnKey"; import LogoutIcon from "@mui/icons-material/Logout"; import { EmbeddedPublicSearch } from "./search/EmbeddedPublicSearch"; -import { AppVersion } from "./versions/AppVersion"; import SearchDatasetIcon from "@mui/icons-material/Search"; -import { EmbeddedSearch } from "./search/EmbeddedSearch"; import { Footer } from "./navigation/Footer"; const drawerWidth = 240; @@ -145,7 +143,7 @@ export default function PersistentDrawerLeft(props) { > - + - + Register - + Login diff --git a/frontend/src/components/auth/RedirectLogout.tsx b/frontend/src/components/auth/RedirectLogout.tsx index ec71a8cd0..6ce1dbe86 100644 --- a/frontend/src/components/auth/RedirectLogout.tsx +++ b/frontend/src/components/auth/RedirectLogout.tsx @@ -2,6 +2,8 @@ import React, { useEffect } from "react"; import { logout } from "../../actions/user"; import TopBar from "../navigation/TopBar"; import { useDispatch } from "react-redux"; +import { Link } from "@mui/material"; +import { Link as RouterLink } from "react-router-dom"; export const RedirectLogout = (): JSX.Element => { const dispatch = useDispatch(); @@ -16,7 +18,10 @@ export const RedirectLogout = (): JSX.Element => {

- You have logged out. Log in again. + You have logged out.{" "} + + Log in again. +

diff --git a/frontend/src/components/auth/legacy/Login.tsx b/frontend/src/components/auth/legacy/Login.tsx index 818bcfb30..fc6fbe1a9 100644 --- a/frontend/src/components/auth/legacy/Login.tsx +++ b/frontend/src/components/auth/legacy/Login.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from "react"; -import { useNavigate } from "react-router-dom"; +import { useNavigate, Link as RouterLink } from "react-router-dom"; import { Avatar, Button, @@ -113,8 +113,10 @@ export const Login = (): JSX.Element => { onChange={changePassword} onKeyPress={handleKeyPressed} /> + { > Sign In + { onChange={changePasswordConfirm} /> { Register { @@ -22,7 +23,7 @@ export const Forbidden = (): JSX.Element => { Go back{" "} - + home diff --git a/frontend/src/components/errors/PageNotFound.tsx b/frontend/src/components/errors/PageNotFound.tsx index 62c37d20e..1617b41f1 100644 --- a/frontend/src/components/errors/PageNotFound.tsx +++ b/frontend/src/components/errors/PageNotFound.tsx @@ -1,6 +1,7 @@ import React from "react"; import { Grid, Link, Typography } from "@mui/material"; import Layout from "../Layout"; +import { Link as RouterLink } from "react-router-dom"; export const PageNotFound = (): JSX.Element => { return ( @@ -22,7 +23,7 @@ export const PageNotFound = (): JSX.Element => { Go back{" "} - + home diff --git a/frontend/src/components/navigation/TopBar.tsx b/frontend/src/components/navigation/TopBar.tsx index c0f1e8f90..ed5b7bf68 100644 --- a/frontend/src/components/navigation/TopBar.tsx +++ b/frontend/src/components/navigation/TopBar.tsx @@ -1,5 +1,6 @@ import React from "react"; import { AppBar, Link, Toolbar } from "@mui/material"; +import { Link as RouterLink } from "react-router-dom"; import Box from "@mui/material/Box"; import { useSelector } from "react-redux"; import { RootState } from "../../types/data"; @@ -40,15 +41,15 @@ export default function TopBar() {
{loggedOut ? ( <> - + Register - + Login ) : ( - + Logout )} diff --git a/frontend/src/index.ejs b/frontend/src/index.ejs index 3c538c52a..5bfebe72f 100644 --- a/frontend/src/index.ejs +++ b/frontend/src/index.ejs @@ -26,8 +26,7 @@ rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" /> - - + Clowder v2 diff --git a/frontend/src/routes.tsx b/frontend/src/routes.tsx index cd9defe5b..1e3734d44 100644 --- a/frontend/src/routes.tsx +++ b/frontend/src/routes.tsx @@ -115,7 +115,7 @@ const PrivateRoute = (props): JSX.Element => { export const AppRoutes = (): JSX.Element => { return ( - + } /> {isAuthorized() ? ( diff --git a/frontend/webpack.config.dev.js b/frontend/webpack.config.dev.js index 6511e871f..58043ba39 100644 --- a/frontend/webpack.config.dev.js +++ b/frontend/webpack.config.dev.js @@ -9,8 +9,9 @@ console.log( `the current CLOWDER_REMOTE_HOSTNAME environment variable is ${process.env.CLOWDER_REMOTE_HOSTNAME}` ); console.log( - `the JupyterHub URL is set to ${process.env.JUPYTERHUB_URL}` -) + `The current BASE_URL_ROUTE environment variable is ${process.env.BASE_URL_ROUTE}. + The JupyterHub URL is set to ${process.env.JUPYTERHUB_URL}.` +); export default { mode: "development", @@ -31,7 +32,6 @@ export default { target: "web", output: { path: path.resolve(__dirname, "dist"), - publicPath: "", filename: "[name].bundle.js", chunkFilename: "[name].chunk.bundle.js", }, @@ -43,6 +43,7 @@ export default { CLOWDER_REMOTE_HOSTNAME: JSON.stringify( process.env.CLOWDER_REMOTE_HOSTNAME ), + BASE_URL_ROUTE: JSON.stringify(process.env.BASE_URL_ROUTE), JUPYTERHUB_URL: JSON.stringify(process.env.JUPYTERHUB_URL), APIKEY: JSON.stringify(process.env.APIKEY), KeycloakBaseURL: JSON.stringify(process.env.KeycloakBaseURL), @@ -56,7 +57,7 @@ export default { new webpack.HotModuleReplacementPlugin(), new HtmlWebpackPlugin({ template: "src/index.ejs", - favicon: "./src/public/favicon.ico", + favicon: "src/public/favicon.ico", minify: { removeComments: true, collapseWhitespace: true, diff --git a/frontend/webpack.config.prod.js b/frontend/webpack.config.prod.js index 67ccf827b..c35bb969e 100644 --- a/frontend/webpack.config.prod.js +++ b/frontend/webpack.config.prod.js @@ -12,8 +12,9 @@ console.log( `the current CLOWDER_REMOTE_HOSTNAME environment variable is ${process.env.CLOWDER_REMOTE_HOSTNAME}` ); console.log( - `the JupyterHub URL is set to ${process.env.JUPYTERHUB_URL}` -) + `The current BASE_URL_ROUTE environment variable is ${process.env.BASE_URL_ROUTE}. + The JupyterHub URL is set to ${process.env.JUPYTERHUB_URL}.` +); export default { mode: "production", @@ -34,6 +35,7 @@ export default { filename: "[name].bundle.js", chunkFilename: "[name].chunk.bundle.js", path: path.resolve(__dirname, "dist"), + publicPath: "", }, plugins: [ // NOTE: `npm run preinstall` currently runs eslint @@ -50,6 +52,7 @@ export default { CLOWDER_REMOTE_HOSTNAME: JSON.stringify( process.env.CLOWDER_REMOTE_HOSTNAME ), + BASE_URL_ROUTE: JSON.stringify(process.env.BASE_URL_ROUTE), JUPYTERHUB_URL: JSON.stringify(process.env.JUPYTERHUB_URL), APIKEY: JSON.stringify(process.env.APIKEY), KeycloakBaseURL: JSON.stringify(process.env.KeycloakBaseURL), @@ -63,7 +66,7 @@ export default { // Generate HTML file that contains references to generated bundles. See here for how this works: https://github.com/ampedandwired/html-webpack-plugin#basic-usage new HtmlWebpackPlugin({ template: "src/index.ejs", - favicon: "./src/public/favicon.ico", + favicon: `src/public/favicon.ico`, minify: { removeComments: true, collapseWhitespace: true, diff --git a/scripts/keycloak/clowder-realm-subdirectory-example.json b/scripts/keycloak/clowder-realm-subdirectory-example.json new file mode 100644 index 000000000..8940b5969 --- /dev/null +++ b/scripts/keycloak/clowder-realm-subdirectory-example.json @@ -0,0 +1,2191 @@ +{ + "id": "a95a9b0c-dfc0-42fc-a89d-6fab3a37fcf3", + "realm": "clowder", + "notBefore": 1647451804, + "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": true, + "registrationEmailAsUsername": true, + "rememberMe": false, + "verifyEmail": false, + "loginWithEmailAllowed": true, + "duplicateEmailsAllowed": false, + "resetPasswordAllowed": true, + "editUsernameAllowed": false, + "bruteForceProtected": false, + "permanentLockout": false, + "maxFailureWaitSeconds": 900, + "minimumQuickLoginWaitSeconds": 60, + "waitIncrementSeconds": 60, + "quickLoginCheckMilliSeconds": 1000, + "maxDeltaTimeSeconds": 43200, + "failureFactor": 30, + "roles": { + "realm": [ + { + "id": "255cadda-90be-4be8-aabe-8a1437e8db36", + "name": "default-roles-clowder", + "description": "${role_default-roles}", + "composite": true, + "composites": { + "realm": [ + "offline_access", + "uma_authorization" + ] + }, + "clientRole": false, + "containerId": "a95a9b0c-dfc0-42fc-a89d-6fab3a37fcf3", + "attributes": {} + }, + { + "id": "f5cb44bb-0164-4eea-9890-8a7f45de866f", + "name": "offline_access", + "description": "${role_offline-access}", + "composite": false, + "clientRole": false, + "containerId": "a95a9b0c-dfc0-42fc-a89d-6fab3a37fcf3", + "attributes": {} + }, + { + "id": "d2e9fd55-a43c-4ba3-8404-6ce15df1bf9a", + "name": "uma_authorization", + "description": "${role_uma_authorization}", + "composite": false, + "clientRole": false, + "containerId": "a95a9b0c-dfc0-42fc-a89d-6fab3a37fcf3", + "attributes": {} + } + ], + "client": { + "realm-management": [ + { + "id": "ad9e68f2-2ae8-49f9-89bc-2bc9b01e8cc8", + "name": "view-realm", + "description": "${role_view-realm}", + "composite": false, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + }, + { + "id": "29cbf565-9522-4c62-99dc-ac72af7fb8a7", + "name": "query-clients", + "description": "${role_query-clients}", + "composite": false, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + }, + { + "id": "be2054cf-2df5-48a3-b88a-67fc4e0ac4a4", + "name": "query-groups", + "description": "${role_query-groups}", + "composite": false, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + }, + { + "id": "ab761a56-8aab-4ab7-a96e-0e24261ed665", + "name": "view-events", + "description": "${role_view-events}", + "composite": false, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + }, + { + "id": "52936cf0-7ff9-4a31-b445-ba11d50c1ada", + "name": "manage-clients", + "description": "${role_manage-clients}", + "composite": false, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + }, + { + "id": "dea4489f-fd53-4228-9365-643e74fdad6a", + "name": "impersonation", + "description": "${role_impersonation}", + "composite": false, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + }, + { + "id": "84f86cee-d957-46af-bb07-02264f8cb319", + "name": "view-clients", + "description": "${role_view-clients}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "query-clients" + ] + } + }, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + }, + { + "id": "b17a67e6-5dc0-47f5-9849-d477978dc6c6", + "name": "manage-events", + "description": "${role_manage-events}", + "composite": false, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + }, + { + "id": "41c03bf6-1d4c-4827-8e11-e244741685d5", + "name": "query-realms", + "description": "${role_query-realms}", + "composite": false, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + }, + { + "id": "6acd5468-07d3-4132-8ed3-e53e1b6e4fa3", + "name": "view-identity-providers", + "description": "${role_view-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + }, + { + "id": "57ed79be-16b2-435e-bb05-c306cdbd6caf", + "name": "view-users", + "description": "${role_view-users}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "query-users", + "query-groups" + ] + } + }, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + }, + { + "id": "9fec4324-c1cf-4da1-b27f-2b0cf22c16eb", + "name": "query-users", + "description": "${role_query-users}", + "composite": false, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + }, + { + "id": "cf7e8b5b-228f-4982-baa6-d1d9c4477dc2", + "name": "create-client", + "description": "${role_create-client}", + "composite": false, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + }, + { + "id": "75522763-9eda-48b4-8b88-49d25accc722", + "name": "view-authorization", + "description": "${role_view-authorization}", + "composite": false, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + }, + { + "id": "f469e893-a3cf-4d5a-8f13-17f555496ef6", + "name": "manage-identity-providers", + "description": "${role_manage-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + }, + { + "id": "93d934d7-a455-423c-ac53-c21cabff1fc2", + "name": "manage-users", + "description": "${role_manage-users}", + "composite": false, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + }, + { + "id": "9bf9f1dd-bd30-42fb-84bd-b4c3674a133b", + "name": "manage-realm", + "description": "${role_manage-realm}", + "composite": false, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + }, + { + "id": "6ec7320f-75a2-404b-968b-fd4cefd3bdc0", + "name": "realm-admin", + "description": "${role_realm-admin}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "view-realm", + "query-clients", + "query-groups", + "view-events", + "manage-clients", + "view-clients", + "impersonation", + "manage-events", + "query-realms", + "view-users", + "view-identity-providers", + "query-users", + "view-authorization", + "create-client", + "manage-users", + "manage-identity-providers", + "manage-realm", + "manage-authorization" + ] + } + }, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + }, + { + "id": "94051464-a27f-4c02-9992-310ee0570648", + "name": "manage-authorization", + "description": "${role_manage-authorization}", + "composite": false, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + } + ], + "security-admin-console": [], + "admin-cli": [], + "clowder2-backend": [], + "account-console": [], + "broker": [], + "account": [ + { + "id": "1b32f3e7-1cea-4e06-b366-1466707a8786", + "name": "delete-account", + "description": "${role_delete-account}", + "composite": false, + "clientRole": true, + "containerId": "c0dbd9a3-d750-4ff1-aa42-ac778df83ef6", + "attributes": {} + }, + { + "id": "5abeaebe-0017-42ed-9496-8b6ffcf2539a", + "name": "manage-account", + "composite": false, + "clientRole": true, + "containerId": "c0dbd9a3-d750-4ff1-aa42-ac778df83ef6", + "attributes": {} + } + ] + } + }, + "groups": [], + "defaultRole": { + "id": "255cadda-90be-4be8-aabe-8a1437e8db36", + "name": "default-roles-clowder", + "description": "${role_default-roles}", + "composite": true, + "clientRole": false, + "containerId": "a95a9b0c-dfc0-42fc-a89d-6fab3a37fcf3" + }, + "requiredCredentials": [ + "password" + ], + "otpPolicyType": "totp", + "otpPolicyAlgorithm": "HmacSHA1", + "otpPolicyInitialCounter": 0, + "otpPolicyDigits": 6, + "otpPolicyLookAheadWindow": 1, + "otpPolicyPeriod": 30, + "otpSupportedApplications": [ + "FreeOTP", + "Google Authenticator" + ], + "webAuthnPolicyRpEntityName": "keycloak", + "webAuthnPolicySignatureAlgorithms": [ + "ES256" + ], + "webAuthnPolicyRpId": "", + "webAuthnPolicyAttestationConveyancePreference": "not specified", + "webAuthnPolicyAuthenticatorAttachment": "not specified", + "webAuthnPolicyRequireResidentKey": "not specified", + "webAuthnPolicyUserVerificationRequirement": "not specified", + "webAuthnPolicyCreateTimeout": 0, + "webAuthnPolicyAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyAcceptableAaguids": [], + "webAuthnPolicyPasswordlessRpEntityName": "keycloak", + "webAuthnPolicyPasswordlessSignatureAlgorithms": [ + "ES256" + ], + "webAuthnPolicyPasswordlessRpId": "", + "webAuthnPolicyPasswordlessAttestationConveyancePreference": "not specified", + "webAuthnPolicyPasswordlessAuthenticatorAttachment": "not specified", + "webAuthnPolicyPasswordlessRequireResidentKey": "not specified", + "webAuthnPolicyPasswordlessUserVerificationRequirement": "not specified", + "webAuthnPolicyPasswordlessCreateTimeout": 0, + "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyPasswordlessAcceptableAaguids": [], + "scopeMappings": [ + { + "clientScope": "offline_access", + "roles": [ + "offline_access" + ] + } + ], + "clientScopeMappings": { + "account": [ + { + "client": "account-console", + "roles": [ + "manage-account" + ] + } + ] + }, + "clients": [ + { + "id": "c0dbd9a3-d750-4ff1-aa42-ac778df83ef6", + "clientId": "account", + "name": "${client_account}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/clowder/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "/realms/clowder/account/*" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "c6d18450-47f3-4d2a-87ef-97d0f3414efe", + "clientId": "account-console", + "name": "${client_account-console}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/clowder/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "/realms/clowder/account/*" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "8268dd83-7d4a-4aa4-9b32-803a881c11a2", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + } + ], + "defaultClientScopes": [ + "web-origins", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "89a47015-9ef6-46e5-8059-874b675059ef", + "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": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "d2185eda-0e48-47c1-bda5-150c199865c7", + "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": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "9d3fa7e0-6aa2-45c1-865d-4eee673eb6bf", + "clientId": "clowder2-backend", + "rootUrl": "http://localhost/clowder", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "http://localhost/clowder/api/v2/auth" + ], + "webOrigins": [ + "http://localhost/clowder" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "saml.force.post.binding": "false", + "saml.multivalued.roles": "false", + "frontchannel.logout.session.required": "false", + "oauth2.device.authorization.grant.enabled": "false", + "backchannel.logout.revoke.offline.tokens": "false", + "saml.server.signature.keyinfo.ext": "false", + "use.refresh.tokens": "true", + "oidc.ciba.grant.enabled": "false", + "backchannel.logout.session.required": "false", + "client_credentials.use_refresh_token": "false", + "require.pushed.authorization.requests": "false", + "saml.client.signature": "false", + "saml.allow.ecp.flow": "false", + "id.token.as.detached.signature": "false", + "saml.assertion.signature": "false", + "saml.encrypt": "false", + "saml.server.signature": "false", + "exclude.session.state.from.auth.response": "false", + "saml.artifact.binding": "false", + "saml_force_name_id_format": "false", + "acr.loa.map": "{}", + "tls.client.certificate.bound.access.tokens": "false", + "saml.authnstatement": "false", + "display.on.consent.screen": "false", + "token.response.type.bearer.lower-case": "false", + "saml.onetimeuse.condition": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": [ + "web-origins", + "roles", + "profile", + "email", + "openid" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "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": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "cf8424c8-b84a-43d4-8e0f-93c3f394fe84", + "clientId": "security-admin-console", + "name": "${client_security-admin-console}", + "rootUrl": "${authAdminUrl}", + "baseUrl": "/admin/clowder/console/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "/admin/clowder/console/*" + ], + "webOrigins": [ + "+" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "c50de445-699e-4af6-958d-4dec02312a1c", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": [ + "web-origins", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + } + ], + "clientScopes": [ + { + "id": "b2fed6ad-3dfe-4522-b1ba-8a9b7c1b2de2", + "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": "52c045ac-ded8-4961-ae54-b8b7206ca36c", + "name": "acr loa level", + "protocol": "openid-connect", + "protocolMapper": "oidc-acr-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true" + } + } + ] + }, + { + "id": "bae6d53b-dc40-4819-95c2-99cc8addff6c", + "name": "web-origins", + "description": "OpenID Connect scope for add allowed web origins to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false", + "consent.screen.text": "" + }, + "protocolMappers": [ + { + "id": "e73aabcb-994e-4201-93fd-66bb9a75842a", + "name": "allowed web origins", + "protocol": "openid-connect", + "protocolMapper": "oidc-allowed-origins-mapper", + "consentRequired": false, + "config": {} + } + ] + }, + { + "id": "0a8f056e-36df-4aaf-8eee-289e13ec473c", + "name": "phone", + "description": "OpenID Connect built-in scope: phone", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${phoneScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "57a6b29f-e66a-4d32-93e6-d2c3b1fe4823", + "name": "phone number verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "phoneNumberVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number_verified", + "jsonType.label": "boolean" + } + }, + { + "id": "c0dc7048-d769-4d98-8fd8-a85e4bbfa62f", + "name": "phone number", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "phoneNumber", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "27b15f8c-ea65-4288-bfd5-bd88eb1d05cf", + "name": "openid", + "description": "", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "gui.order": "", + "consent.screen.text": "" + } + }, + { + "id": "aeeafc25-f5bc-4f8d-88c5-0b4150415d50", + "name": "roles", + "description": "OpenID Connect scope for add user roles to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "true", + "consent.screen.text": "${rolesScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "d808f1a4-7485-4abc-bc96-730ffaa3d541", + "name": "client roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-client-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "resource_access.${client_id}.roles", + "jsonType.label": "String", + "multivalued": "true" + } + }, + { + "id": "c97eee7b-93bb-46e3-b11e-e07bd9a88034", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + }, + { + "id": "e3ad1110-ec92-4cec-bfe2-03fd49c25ccc", + "name": "realm roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "realm_access.roles", + "jsonType.label": "String", + "multivalued": "true" + } + } + ] + }, + { + "id": "a5673fb0-526e-41b4-a1d7-47b53e7fe4d6", + "name": "profile", + "description": "OpenID Connect built-in scope: profile", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${profileScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "825256af-e9c1-4faf-91ba-b31cf812f625", + "name": "given name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "firstName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "given_name", + "jsonType.label": "String" + } + }, + { + "id": "667fc7f5-4230-44fa-b8b5-29943e77a102", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + }, + { + "id": "a3f2ce5f-88c0-4b6d-b908-8a4d933c9f48", + "name": "website", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "website", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "website", + "jsonType.label": "String" + } + }, + { + "id": "668b1177-85cf-4b3b-b893-d75a5c697495", + "name": "zoneinfo", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "zoneinfo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "zoneinfo", + "jsonType.label": "String" + } + }, + { + "id": "cb7b6bbc-1506-4fab-ba0f-fd65cfda5789", + "name": "middle name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "middleName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "middle_name", + "jsonType.label": "String" + } + }, + { + "id": "91896308-0855-4152-9a16-b4e63093b07c", + "name": "picture", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "picture", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "picture", + "jsonType.label": "String" + } + }, + { + "id": "95e292af-9fa5-44ff-892e-a32a42183627", + "name": "family name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "lastName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "family_name", + "jsonType.label": "String" + } + }, + { + "id": "b58166e6-87c2-474b-9b04-ba0d813b45b3", + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "preferred_username", + "jsonType.label": "String" + } + }, + { + "id": "30c5a069-1d72-4bb8-85ce-11eac851ec61", + "name": "gender", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "gender", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "gender", + "jsonType.label": "String" + } + }, + { + "id": "c8328579-ef1c-4737-83f4-cb129c8c7831", + "name": "updated at", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "updatedAt", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "updated_at", + "jsonType.label": "String" + } + }, + { + "id": "c54898da-56be-4c37-ba26-d07a9824c4f2", + "name": "profile", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "profile", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "profile", + "jsonType.label": "String" + } + }, + { + "id": "72a8da7e-bad0-4e79-be75-dd48c8900499", + "name": "full name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "id": "2e64287f-7bb2-48cd-94ab-dc86fa0c7c88", + "name": "nickname", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "nickname", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "nickname", + "jsonType.label": "String" + } + }, + { + "id": "446734d4-3ad8-4c97-aaea-44e21319402e", + "name": "birthdate", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "birthdate", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "birthdate", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "eb6c022f-2578-4828-87f1-e05927678120", + "name": "email", + "description": "OpenID Connect built-in scope: email", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${emailScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "9d242859-9217-4c9a-9889-08d49634784d", + "name": "email verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "emailVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email_verified", + "jsonType.label": "boolean" + } + }, + { + "id": "7cc66e50-4e9f-4940-b532-7bcadbdcb279", + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "email", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "ad46489a-f89f-4f61-b393-3a340106ac25", + "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": "5b3ba25d-d6fe-4672-b7bd-f87a738eefea", + "name": "role_list", + "description": "SAML role list", + "protocol": "saml", + "attributes": { + "consent.screen.text": "${samlRoleListScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "b8374915-aed5-4224-b143-ff0fba5145bb", + "name": "role list", + "protocol": "saml", + "protocolMapper": "saml-role-list-mapper", + "consentRequired": false, + "config": { + "single": "false", + "attribute.nameformat": "Basic", + "attribute.name": "Role" + } + } + ] + }, + { + "id": "42ddc8a7-f421-4f55-8b48-a4584ecb706c", + "name": "address", + "description": "OpenID Connect built-in scope: address", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${addressScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "70b9810a-ac2d-4cb0-ba9d-a0af109ed2c6", + "name": "address", + "protocol": "openid-connect", + "protocolMapper": "oidc-address-mapper", + "consentRequired": false, + "config": { + "user.attribute.formatted": "formatted", + "user.attribute.country": "country", + "user.attribute.postal_code": "postal_code", + "userinfo.token.claim": "true", + "user.attribute.street": "street", + "id.token.claim": "true", + "user.attribute.region": "region", + "access.token.claim": "true", + "user.attribute.locality": "locality" + } + } + ] + }, + { + "id": "9517fbc2-4bc7-44ad-bd3c-2134e6385b01", + "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": "3553f990-ba52-4713-b8d3-299b83f2a7a2", + "name": "groups", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "multivalued": "true", + "userinfo.token.claim": "true", + "user.attribute": "foo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "groups", + "jsonType.label": "String" + } + }, + { + "id": "f054f73a-24c3-4ffa-af87-243ddffd0b5a", + "name": "upn", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "upn", + "jsonType.label": "String" + } + } + ] + } + ], + "defaultDefaultClientScopes": [ + "role_list", + "profile", + "email", + "roles", + "web-origins", + "acr", + "openid" + ], + "defaultOptionalClientScopes": [ + "offline_access", + "address", + "phone", + "microprofile-jwt" + ], + "browserSecurityHeaders": { + "contentSecurityPolicyReportOnly": "", + "xContentTypeOptions": "nosniff", + "xRobotsTag": "none", + "xFrameOptions": "SAMEORIGIN", + "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "xXSSProtection": "1; mode=block", + "strictTransportSecurity": "max-age=31536000; includeSubDomains" + }, + "smtpServer": { + "host": "maildev", + "from": "devnull@ncsa.illinois.edu", + "starttls": "", + "auth": "", + "ssl": "" + }, + "loginTheme": "clowder-theme", + "eventsEnabled": false, + "eventsListeners": [ + "jboss-logging" + ], + "enabledEventTypes": [], + "adminEventsEnabled": false, + "adminEventsDetailsEnabled": false, + "identityProviders": [ + { + "alias": "cilogon", + "displayName": "CILogon", + "internalId": "165a05f4-f6d7-44ae-a906-285cba64bae7", + "providerId": "oidc", + "enabled": true, + "updateProfileFirstLoginMode": "on", + "trustEmail": false, + "storeToken": true, + "addReadTokenRoleOnCreate": true, + "authenticateByDefault": false, + "linkOnly": false, + "firstBrokerLoginFlowAlias": "first broker login", + "config": { + "userInfoUrl": "https://cilogon.org/oauth2/userinfo", + "clientId": "cilogon:/client_id/165f54b200b7bc4bf77635fe56237902", + "tokenUrl": "https://cilogon.org/oauth2/token", + "authorizationUrl": "https://cilogon.org/authorize", + "clientAuthMethod": "client_secret_post", + "syncMode": "IMPORT", + "clientSecret": "**********", + "defaultScope": "openid profile org.cilogon.userinfo email", + "useJwksUrl": "true" + } + }, + { + "alias": "globus", + "displayName": "Globus", + "internalId": "1f4df120-221f-4ed9-ab4a-f40bfeedafbb", + "providerId": "oidc", + "enabled": true, + "updateProfileFirstLoginMode": "on", + "trustEmail": false, + "storeToken": true, + "addReadTokenRoleOnCreate": true, + "authenticateByDefault": false, + "linkOnly": false, + "firstBrokerLoginFlowAlias": "first broker login", + "config": { + "clientId": "fa3320ff-4730-4395-a4ec-fc7fe23ec8a7", + "tokenUrl": "https://auth.globus.org/v2/oauth2/token", + "authorizationUrl": "https://auth.globus.org/v2/oauth2/authorize", + "clientAuthMethod": "client_secret_post", + "syncMode": "IMPORT", + "clientSecret": "**********", + "defaultScope": "openid profile email", + "useJwksUrl": "true" + } + } + ], + "identityProviderMappers": [], + "components": { + "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [ + { + "id": "e2dae728-1a1a-45af-9065-620384959e13", + "name": "Trusted Hosts", + "providerId": "trusted-hosts", + "subType": "anonymous", + "subComponents": {}, + "config": { + "host-sending-registration-request-must-match": [ + "true" + ], + "client-uris-must-match": [ + "true" + ] + } + }, + { + "id": "2a2f8ffa-334e-4736-8ce7-9669f3c1b57b", + "name": "Max Clients Limit", + "providerId": "max-clients", + "subType": "anonymous", + "subComponents": {}, + "config": { + "max-clients": [ + "200" + ] + } + }, + { + "id": "b61aee8f-89b9-429f-a00c-0e8ad8d397e8", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allow-default-scopes": [ + "true" + ] + } + }, + { + "id": "587e5d10-5f94-4b55-9eab-af3757126452", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "oidc-sha256-pairwise-sub-mapper", + "oidc-full-name-mapper", + "saml-user-attribute-mapper", + "saml-role-list-mapper", + "oidc-address-mapper", + "oidc-usermodel-property-mapper", + "oidc-usermodel-attribute-mapper", + "saml-user-property-mapper" + ] + } + }, + { + "id": "42de5d51-e99f-48c5-a347-8a943b55b11d", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allow-default-scopes": [ + "true" + ] + } + }, + { + "id": "ec10fb40-0867-403f-bf83-0dd804d9658c", + "name": "Consent Required", + "providerId": "consent-required", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "65145f79-7a4b-4ee0-823b-e6c901ea050f", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "oidc-address-mapper", + "oidc-usermodel-property-mapper", + "oidc-usermodel-attribute-mapper", + "saml-role-list-mapper", + "saml-user-attribute-mapper", + "oidc-sha256-pairwise-sub-mapper", + "saml-user-property-mapper", + "oidc-full-name-mapper" + ] + } + }, + { + "id": "768e411f-2942-4de1-8072-7c1a18ad158a", + "name": "Full Scope Disabled", + "providerId": "scope", + "subType": "anonymous", + "subComponents": {}, + "config": {} + } + ], + "org.keycloak.keys.KeyProvider": [ + { + "id": "25f8382e-c4c6-4a98-b2ec-c1381a946e2c", + "name": "rsa-enc-generated", + "providerId": "rsa-enc-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ], + "algorithm": [ + "RSA-OAEP" + ] + } + }, + { + "id": "5c263375-6225-41c5-8243-c13a4aa17fa4", + "name": "hmac-generated", + "providerId": "hmac-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ], + "algorithm": [ + "HS256" + ] + } + }, + { + "id": "5e332177-04bc-4d36-9ef6-93a2262a845b", + "name": "rsa-generated", + "providerId": "rsa-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ] + } + }, + { + "id": "32c7819c-b8fe-4187-8d4d-0620e17732c9", + "name": "aes-generated", + "providerId": "aes-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ] + } + } + ] + }, + "internationalizationEnabled": false, + "supportedLocales": [ + "" + ], + "authenticationFlows": [ + { + "id": "400aee4e-856a-422f-b1a0-eb6ce32f69e3", + "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": "bfa7993c-039d-427d-8b17-4b97d810be7e", + "alias": "Authentication Options", + "description": "Authentication options.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "basic-auth", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "basic-auth-otp", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-spnego", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "16b00e1b-3b22-4d2e-b5bf-9c8b220b3304", + "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": "962c17fa-c368-48dc-b98e-8d5c925737a3", + "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": "15cbf8fe-29c0-4a40-bfc4-047bd64561de", + "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": "4cc602f5-ce74-4fac-a45e-52aa4fb7cdcb", + "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": "12a01691-1248-457e-a3ed-d5246a6f3982", + "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": "34ddada2-d38c-4f8b-9df8-ad2cd8aaea35", + "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": "7faed55c-c6f0-4cfc-ac1b-27ee2fb0048c", + "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": "c6401558-ae19-4607-9d2a-42e1c777517e", + "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": 30, + "autheticatorFlow": true, + "flowAlias": "forms", + "userSetupAllowed": false + } + ] + }, + { + "id": "ad12f274-58d8-474a-a841-760a5c937f87", + "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": "c3a84b9f-de66-48e1-b3ee-097a711387a2", + "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": "95111482-c6b0-47ab-affa-b8ad9b98ab2e", + "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": "21832c48-5d22-465b-a147-61f9d22651e4", + "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 + } + ] + }, + { + "id": "deebfeac-43f7-4c33-ad8c-7f3e948480dd", + "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": "6209d001-17fe-4eae-8e49-c9fa96861e99", + "alias": "http challenge", + "description": "An authentication flow based on challenge-response HTTP Authentication Schemes", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "no-cookie-redirect", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Authentication Options", + "userSetupAllowed": false + } + ] + }, + { + "id": "1d9bdfb5-e2e4-44fe-891a-a3239c7d4efa", + "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": "17f77ee5-6665-4ebd-bb25-1d43ae653c54", + "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-profile-action", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 40, + "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 + } + ] + }, + { + "id": "6f9bb247-aaf4-4123-be3f-8732a55cc060", + "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": "d4b93ed0-2aef-4cbe-a455-cd6cd76f3927", + "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": "006fdb06-4dba-4478-9863-e51020f2a2ab", + "alias": "create unique user config", + "config": { + "require.password.update.after.registration": "false" + } + }, + { + "id": "510e58e8-bb74-4f51-a217-850f4dca371f", + "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": "update_user_locale", + "name": "Update User Locale", + "providerId": "update_user_locale", + "enabled": true, + "defaultAction": false, + "priority": 1000, + "config": {} + } + ], + "browserFlow": "browser", + "registrationFlow": "registration", + "directGrantFlow": "direct grant", + "resetCredentialsFlow": "reset credentials", + "clientAuthenticationFlow": "clients", + "dockerAuthenticationFlow": "docker auth", + "attributes": { + "cibaBackchannelTokenDeliveryMode": "poll", + "cibaAuthRequestedUserHint": "login_hint", + "clientOfflineSessionMaxLifespan": "0", + "oauth2DevicePollingInterval": "5", + "clientSessionIdleTimeout": "0", + "userProfileEnabled": "false", + "clientOfflineSessionIdleTimeout": "0", + "cibaInterval": "5", + "cibaExpiresIn": "120", + "oauth2DeviceCodeLifespan": "600", + "parRequestUriLifespan": "60", + "clientSessionMaxLifespan": "0", + "frontendUrl": "http://localhost/clowder/keycloak" + }, + "keycloakVersion": "18.0.0", + "userManagedAccessAllowed": false, + "clientProfiles": { + "profiles": [] + }, + "clientPolicies": { + "policies": [] + } +}