diff --git a/.github/workflows/build-docker-proxy.yml b/.github/workflows/build-docker-proxy.yml new file mode 100644 index 0000000..c160387 --- /dev/null +++ b/.github/workflows/build-docker-proxy.yml @@ -0,0 +1,47 @@ +name: Docker Build Proxy + +on: + push: + branches: + - 'main' + pull_request: + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }}-proxy + +jobs: + build: + name: Build docker image + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=ref,event=pr + type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }} + + - name: Build and push docker image + uses: docker/build-push-action@v6 + with: + context: nginx + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/backend/api/context.py b/backend/api/context.py index ddf3c98..671f1b4 100644 --- a/backend/api/context.py +++ b/backend/api/context.py @@ -1,14 +1,18 @@ -from typing import Any +from typing import TYPE_CHECKING, Any import strawberry from sqlalchemy.ext.asyncio import AsyncSession from strawberry.fastapi import BaseContext +if TYPE_CHECKING: + from database.models.user import User + class Context(BaseContext): - def __init__(self, db: AsyncSession): + def __init__(self, db: AsyncSession, user: "User | None" = None): super().__init__() self.db = db + self.user = user async def get_context(db: AsyncSession) -> Context: diff --git a/backend/auth.py b/backend/auth.py new file mode 100644 index 0000000..7ecc7d5 --- /dev/null +++ b/backend/auth.py @@ -0,0 +1,102 @@ +from typing import Any + +import requests +from config import CLIENT_ID, ISSUER_URI +from fastapi import HTTPException, status +from jose import jwk, jwt + +jwks_cache: dict[str, Any] = {} +openid_config_cache: dict[str, Any] = {} + + +def get_openid_config() -> dict[str, Any]: + global openid_config_cache + if openid_config_cache: + return openid_config_cache + + config_url = f"{ISSUER_URI.rstrip('/')}/.well-known/openid-configuration" + try: + response = requests.get(config_url, timeout=5) + response.raise_for_status() + config = response.json() + openid_config_cache = config + return config + except Exception as e: + print(f"OIDC Config Error: {e}") + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Could not fetch OpenID Configuration", + ) + + +def get_public_key(token: str) -> Any: + global jwks_cache + + try: + header = jwt.get_unverified_header(token) + kid = header.get("kid") + + if not kid: + raise Exception("Token header missing kid") + + if kid in jwks_cache: + return jwks_cache[kid] + + # TODO use openid config endpoint to obtain well-known endpoints in the future + jwks_uri = f"{ISSUER_URI}/protocol/openid-connect/certs" + + response = requests.get(jwks_uri, timeout=5) + response.raise_for_status() + jwks = response.json() + + for key_data in jwks.get("keys", []): + if key_data.get("kid") == kid: + key = jwk.construct(key_data) + jwks_cache[kid] = key + return key + + raise Exception("Public key not found in JWKS") + + except Exception as e: + print(f"Auth Error: {e}") + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Could not validate credentials", + headers={"WWW-Authenticate": "Bearer"}, + ) + + +def verify_token(token: str) -> dict: + try: + public_key = get_public_key(token) + + payload = jwt.decode( + token, + public_key, + algorithms=["RS256"], + options={"verify_aud": False}, + ) + + azp = payload.get("azp") + aud = payload.get("aud") + + if isinstance(aud, str): + aud = [aud] + elif aud is None: + aud = [] + + if azp and azp == CLIENT_ID: + return payload + + if CLIENT_ID in aud: + return payload + + raise Exception( + f"Invalid audience/azp. Expected {CLIENT_ID}, got azp={azp}, aud={aud}", + ) + except jwt.ExpiredSignatureError: + raise HTTPException(status_code=401, detail="Token has expired") + except jwt.JWTError as e: + raise HTTPException(status_code=401, detail=f"Invalid token: {e!s}") + except Exception: + raise HTTPException(status_code=401, detail="Authentication failed") diff --git a/backend/config.py b/backend/config.py index 71cde90..6da4a53 100644 --- a/backend/config.py +++ b/backend/config.py @@ -18,3 +18,11 @@ IS_DEV = ENV == "development" LOGGER = os.getenv("LOGGER", "uvicorn") + +ISSUER_URI = os.getenv("ISSUER_URI", "http://localhost:8080/realms/tasks") +PUBLIC_ISSUER_URI = os.getenv( + "PUBLIC_ISSUER_URI", + ISSUER_URI, +) +CLIENT_ID = os.getenv("CLIENT_ID", "tasks") +CLIENT_SECRET = os.getenv("CLIENT_SECRET", "tasks-secret") diff --git a/backend/database/migrations/env.py b/backend/database/migrations/env.py index 4629e8f..97cda60 100644 --- a/backend/database/migrations/env.py +++ b/backend/database/migrations/env.py @@ -9,7 +9,7 @@ # Importing 'Base' from 'models' triggers the import of all # sub-models (User, Patient, etc.) via models/__init__.py, # ensuring they are registered in Base.metadata for autogeneration. -from database.base import Base +from database.models.base import Base from database.session import DATABASE_URL from sqlalchemy import pool from sqlalchemy.engine import Connection diff --git a/backend/database/migrations/versions/7d4b70ab0232_initial_migration.py b/backend/database/migrations/versions/7d4b70ab0232_initial_migration.py deleted file mode 100644 index 35d50d5..0000000 --- a/backend/database/migrations/versions/7d4b70ab0232_initial_migration.py +++ /dev/null @@ -1,32 +0,0 @@ -"""Initial migration - -Revision ID: 7d4b70ab0232 -Revises: -Create Date: 2025-11-30 22:28:58.442845 - -""" -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision: str = '7d4b70ab0232' -down_revision: Union[str, Sequence[str], None] = None -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - """Upgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - pass - # ### end Alembic commands ### - - -def downgrade() -> None: - """Downgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - pass - # ### end Alembic commands ### diff --git a/backend/database/migrations/versions/92aad7cc3186_initial_models.py b/backend/database/migrations/versions/92aad7cc3186_initial_models.py new file mode 100644 index 0000000..f725369 --- /dev/null +++ b/backend/database/migrations/versions/92aad7cc3186_initial_models.py @@ -0,0 +1,111 @@ +"""Initial models + +Revision ID: 92aad7cc3186 +Revises: +Create Date: 2025-12-01 02:36:26.250686 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '92aad7cc3186' +down_revision: Union[str, Sequence[str], None] = None +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('location_nodes', + sa.Column('id', sa.String(), nullable=False), + sa.Column('title', sa.String(), nullable=False), + sa.Column('kind', sa.String(), nullable=False), + sa.Column('parent_id', sa.String(), nullable=True), + sa.ForeignKeyConstraint(['parent_id'], ['location_nodes.id'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('property_definitions', + sa.Column('id', sa.String(), nullable=False), + sa.Column('name', sa.String(), nullable=False), + sa.Column('description', sa.String(), nullable=True), + sa.Column('field_type', sa.String(), nullable=False), + sa.Column('options', sa.String(), nullable=True), + sa.Column('is_active', sa.Boolean(), nullable=False), + sa.Column('allowed_entities', sa.String(), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('users', + sa.Column('id', sa.String(), nullable=False), + sa.Column('name', sa.String(), nullable=False), + sa.Column('firstname', sa.String(), nullable=True), + sa.Column('lastname', sa.String(), nullable=True), + sa.Column('title', sa.String(), nullable=True), + sa.Column('avatar_url', sa.String(), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('patients', + sa.Column('id', sa.String(), nullable=False), + sa.Column('firstname', sa.String(), nullable=False), + sa.Column('lastname', sa.String(), nullable=False), + sa.Column('birthdate', sa.Date(), nullable=False), + sa.Column('gender', sa.String(), nullable=False), + sa.Column('assigned_location_id', sa.String(), nullable=True), + sa.ForeignKeyConstraint(['assigned_location_id'], ['location_nodes.id'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('tasks', + sa.Column('id', sa.String(), nullable=False), + sa.Column('title', sa.String(), nullable=False), + sa.Column('description', sa.String(), nullable=True), + sa.Column('done', sa.Boolean(), nullable=False), + sa.Column('creation_date', sa.DateTime(), nullable=False), + sa.Column('update_date', sa.DateTime(), nullable=True), + sa.Column('assignee_id', sa.String(), nullable=True), + sa.Column('patient_id', sa.String(), nullable=False), + sa.ForeignKeyConstraint(['assignee_id'], ['users.id'], ), + sa.ForeignKeyConstraint(['patient_id'], ['patients.id'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('property_values', + sa.Column('id', sa.String(), nullable=False), + sa.Column('definition_id', sa.String(), nullable=False), + sa.Column('patient_id', sa.String(), nullable=True), + sa.Column('task_id', sa.String(), nullable=True), + sa.Column('text_value', sa.String(), nullable=True), + sa.Column('number_value', sa.Float(), nullable=True), + sa.Column('boolean_value', sa.Boolean(), nullable=True), + sa.Column('date_value', sa.Date(), nullable=True), + sa.Column('date_time_value', sa.DateTime(), nullable=True), + sa.Column('select_value', sa.String(), nullable=True), + sa.Column('multi_select_values', sa.String(), nullable=True), + sa.ForeignKeyConstraint(['definition_id'], ['property_definitions.id'], ), + sa.ForeignKeyConstraint(['patient_id'], ['patients.id'], ), + sa.ForeignKeyConstraint(['task_id'], ['tasks.id'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('task_dependencies', + sa.Column('previous_task_id', sa.String(), nullable=False), + sa.Column('next_task_id', sa.String(), nullable=False), + sa.ForeignKeyConstraint(['next_task_id'], ['tasks.id'], ), + sa.ForeignKeyConstraint(['previous_task_id'], ['tasks.id'], ), + sa.PrimaryKeyConstraint('previous_task_id', 'next_task_id') + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('task_dependencies') + op.drop_table('property_values') + op.drop_table('tasks') + op.drop_table('patients') + op.drop_table('users') + op.drop_table('property_definitions') + op.drop_table('location_nodes') + # ### end Alembic commands ### diff --git a/backend/database/base.py b/backend/database/models/base.py similarity index 100% rename from backend/database/base.py rename to backend/database/models/base.py diff --git a/backend/database/models/user.py b/backend/database/models/user.py index 2d703b6..18028dc 100644 --- a/backend/database/models/user.py +++ b/backend/database/models/user.py @@ -15,12 +15,18 @@ class User(Base): __tablename__ = "users" id: Mapped[str] = mapped_column( - String, primary_key=True, default=lambda: str(uuid.uuid4()) + String, + primary_key=True, + default=lambda: str(uuid.uuid4()), ) name: Mapped[str] = mapped_column(String) firstname: Mapped[str | None] = mapped_column(String, nullable=True) lastname: Mapped[str | None] = mapped_column(String, nullable=True) title: Mapped[str | None] = mapped_column(String, nullable=True) - avatar_url: Mapped[str | None] = mapped_column(String, nullable=True) + avatar_url: Mapped[str | None] = mapped_column( + String, + nullable=True, + default="https://cdn.helpwave.de/boringavatar.svg", + ) tasks: Mapped[list[Task]] = relationship("Task", back_populates="assignee") diff --git a/backend/database/session.py b/backend/database/session.py index 3637689..991c63d 100644 --- a/backend/database/session.py +++ b/backend/database/session.py @@ -2,7 +2,7 @@ import redis.asyncio as redis from config import DATABASE_URL, REDIS_URL -from database.base import Base +from database.models.base import Base from sqlalchemy.ext.asyncio import ( AsyncSession, async_sessionmaker, diff --git a/backend/main.py b/backend/main.py index d9c6424..3c29eaa 100644 --- a/backend/main.py +++ b/backend/main.py @@ -1,14 +1,107 @@ +import requests from api.context import Context from api.resolvers import Mutation, Query, Subscription -from config import IS_DEV +from auth import verify_token +from config import CLIENT_ID, CLIENT_SECRET, IS_DEV, PUBLIC_ISSUER_URI, ISSUER_URI +from database.models.user import User from database.session import get_db_session -from fastapi import Depends, FastAPI +from fastapi import Depends, FastAPI, HTTPException, Request +from fastapi.responses import RedirectResponse +from fastapi.security import OAuth2PasswordBearer +from sqlalchemy import select from strawberry import Schema from strawberry.fastapi import GraphQLRouter +oauth2_scheme = OAuth2PasswordBearer( + tokenUrl=f"{ISSUER_URI}/protocol/openid-connect/token", + auto_error=False, +) + + +async def get_token_source( + request: Request, + header_token: str | None = Depends(oauth2_scheme), +) -> str | None: + if header_token: + return header_token + + return request.cookies.get("access_token") + + +class UnauthenticatedRedirect(Exception): + pass + + +async def unauthenticated_redirect_handler( + request: Request, + _: UnauthenticatedRedirect, +): + redirect_uri = f"{request.base_url}callback" + + login_url = ( + f"{PUBLIC_ISSUER_URI}/protocol/openid-connect/auth" + f"?client_id={CLIENT_ID}" + f"&response_type=code" + f"&scope=openid profile email" + f"&redirect_uri={redirect_uri}" + ) + return RedirectResponse(url=login_url) + + +async def get_context( + request: Request, + token: str | None = Depends(get_token_source), + session=Depends(get_db_session), +) -> Context: + user_payload = None + + if token: + try: + user_payload = verify_token(token) + except Exception as e: + raise e + user_payload = None + + if not user_payload: + accept_header = request.headers.get("accept", "") + if "text/html" in accept_header: + raise UnauthenticatedRedirect + raise HTTPException( + status_code=401, + detail="Not authenticated", + headers={"WWW-Authenticate": "Bearer"}, + ) + + user_id = user_payload.get("sub") + username = ( + user_payload.get("preferred_username") + or user_payload.get("name") + or "Unknown" + ) + firstname = user_payload.get("given_name") + lastname = user_payload.get("family_name") -async def get_context(session=Depends(get_db_session)) -> Context: - return Context(db=session) + result = await session.execute(select(User).where(User.id == user_id)) + db_user = result.scalars().first() + + if not db_user: + db_user = User( + id=user_id, + name=username, + firstname=firstname, + lastname=lastname, + title="User", + ) + session.add(db_user) + else: + db_user.name = username + db_user.firstname = firstname + db_user.lastname = lastname + + await session.commit() + await session.refresh(db_user) + + return Context(db=session, user=db_user) schema = Schema( @@ -30,4 +123,46 @@ async def get_context(session=Depends(get_db_session)) -> Context: openapi_url="/openapi.json" if IS_DEV else None, ) +app.add_exception_handler( + UnauthenticatedRedirect, + unauthenticated_redirect_handler, +) + + +@app.get("/callback") +def oauth_callback(code: str, request: Request): + token_endpoint = f"{ISSUER_URI}/protocol/openid-connect/token" + redirect_uri = f"{request.base_url}callback" + + payload = { + "grant_type": "authorization_code", + "code": code, + "client_id": CLIENT_ID, + "client_secret": CLIENT_SECRET, + "redirect_uri": redirect_uri, + } + + try: + response = requests.post(token_endpoint, data=payload, timeout=10) + + response.raise_for_status() + token_data = response.json() + access_token = token_data.get("access_token") + + resp = RedirectResponse(url="/graphql") + resp.set_cookie( + "access_token", + access_token, + httponly=True, + max_age=3600, + ) + return resp + + except Exception: + raise HTTPException( + status_code=400, + detail="Authentication failed during token exchange", + ) + + app.include_router(graphql_app, prefix="/graphql") diff --git a/backend/requirements.txt b/backend/requirements.txt index 54ba34b..09e5bd9 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -88,3 +88,5 @@ watchfiles==1.1.1 # via uvicorn websockets==15.0.1 # via uvicorn +python-jose[cryptography] +requests diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 2151fc0..1ba2d1b 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -26,7 +26,9 @@ services: KC_HEALTH_ENABLED: true KEYCLOAK_ADMIN: "admin" KEYCLOAK_ADMIN_PASSWORD: "admin" - command: start-dev + command: start-dev --import-realm + volumes: + - ./keycloak:/opt/keycloak/data/import:ro depends_on: - keycloak-postgres ports: @@ -70,17 +72,16 @@ services: - "8000:80" environment: ENV: "development" - EXTERNAL_URL: "http://localhost:8000" DATABASE_HOSTNAME: "postgres" DATABASE_NAME: "postgres" DATABASE_USERNAME: "postgres" DATABASE_PASSWORD: "password" - KEYCLOAK_SERVER_URL: "http://keycloak:8080" - KEYCLOAK_REALM: "test" - KEYCLOAK_CLIENT_ID: "customer-api-keycloak" - KEYCLOAK_CLIENT_SECRET: "customer-api-client-secret" + ISSUER_URI: "http://keycloak:8080/realms/tasks" + PUBLIC_ISSUER_URI: "http://localhost:8080/realms/tasks" + CLIENT_ID: "tasks" + CLIENT_SECRET: "tasks-secret" depends_on: - postgres @@ -89,8 +90,23 @@ services: restart: always ports: - "3000:80" + environment: + ISSUER_URI: "http://localhost:8080/realms/tasks" + CLIENT_ID: "tasks" + depends_on: + - backend + + nginx: + build: nginx + restart: always + environment: + BACKEND_HOST: backend:80 + FRONTEND_HOST: web:80 + ports: + - "80:80" depends_on: - backend + - web volumes: keycloak-postgres-data: diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..cd6ba1b --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,85 @@ +services: + keycloak-postgres: + image: postgres + volumes: + - "keycloak-postgres-data:/var/lib/postgresql" + environment: + POSTGRES_DB: "keycloak" + POSTGRES_USER: "keycloak" + POSTGRES_PASSWORD: "password" + + keycloak: + image: quay.io/keycloak/keycloak + environment: + KC_DB: "postgres" + KC_DB_URL: "jdbc:postgresql://keycloak-postgres:5432/keycloak" + KC_DB_USERNAME: "keycloak" + KC_DB_PASSWORD: "password" + + KC_HOSTNAME: "localhost" + KC_HOSTNAME_PORT: 8080 + KC_HOSTNAME_STRICT: false + KC_HOSTNAME_STRICT_HTTPS: false + + KC_LOG_LEVEL: "info" + KC_METRICS_ENABLED: true + KC_HEALTH_ENABLED: true + KEYCLOAK_ADMIN: "admin" + KEYCLOAK_ADMIN_PASSWORD: "admin" + command: start-dev --import-realm + volumes: + - ./keycloak:/opt/keycloak/data/import:ro + depends_on: + - keycloak-postgres + ports: + - "8080:8080" + + postgres: + image: postgres + environment: + POSTGRES_DB: "postgres" + POSTGRES_USER: "postgres" + POSTGRES_PASSWORD: "password" + volumes: + - "postgres-data:/var/lib/postgresql" + + backend: + image: ghcr.io/helpwave/tasks-backend:latest + restart: always + environment: + DATABASE_HOSTNAME: "postgres" + DATABASE_NAME: "postgres" + DATABASE_USERNAME: "postgres" + DATABASE_PASSWORD: "password" + + ISSUER_URI: "http://keycloak:8080/realms/tasks" + PUBLIC_ISSUER_URI: "http://localhost:8080/realms/tasks" + CLIENT_ID: "tasks" + CLIENT_SECRET: "tasks-secret" + depends_on: + - postgres + + web: + image: ghcr.io/helpwave/tasks-web:latest + restart: always + environment: + ISSUER_URI: "http://localhost:8080/realms/tasks" + CLIENT_ID: "tasks" + depends_on: + - backend + + proxy: + image: ghcr.io/helpwave/tasks-proxy:latest + restart: always + environment: + BACKEND_HOST: backend:80 + FRONTEND_HOST: web:80 + ports: + - "80:80" + depends_on: + - backend + - web + +volumes: + keycloak-postgres-data: + postgres-data: diff --git a/keycloak/tasks.json b/keycloak/tasks.json new file mode 100644 index 0000000..63c9a6a --- /dev/null +++ b/keycloak/tasks.json @@ -0,0 +1,2139 @@ +{ + "id" : "d5aaecfc-fee3-4dce-adde-6280b9ed23a9", + "realm" : "tasks", + "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" : "023b7144-f870-4ecc-8226-04f37e47495e", + "name" : "offline_access", + "description" : "${role_offline-access}", + "composite" : false, + "clientRole" : false, + "containerId" : "d5aaecfc-fee3-4dce-adde-6280b9ed23a9", + "attributes" : { } + }, { + "id" : "7f22789f-8a98-48ab-bf0b-58fc8ba07d05", + "name" : "default-roles-tasks", + "description" : "${role_default-roles}", + "composite" : true, + "composites" : { + "realm" : [ "offline_access", "uma_authorization" ], + "client" : { + "account" : [ "manage-account", "view-profile" ] + } + }, + "clientRole" : false, + "containerId" : "d5aaecfc-fee3-4dce-adde-6280b9ed23a9", + "attributes" : { } + }, { + "id" : "0096dea2-639f-4ecd-8f0d-7eeee899d87b", + "name" : "uma_authorization", + "description" : "${role_uma_authorization}", + "composite" : false, + "clientRole" : false, + "containerId" : "d5aaecfc-fee3-4dce-adde-6280b9ed23a9", + "attributes" : { } + } ], + "client" : { + "realm-management" : [ { + "id" : "50b278bf-2460-4d0a-bf86-0fc0add5b500", + "name" : "query-realms", + "description" : "${role_query-realms}", + "composite" : false, + "clientRole" : true, + "containerId" : "e772ee1c-0b8c-4bd3-95b1-be72786197ac", + "attributes" : { } + }, { + "id" : "d0d27681-8e43-4707-b673-0df05331c62d", + "name" : "view-events", + "description" : "${role_view-events}", + "composite" : false, + "clientRole" : true, + "containerId" : "e772ee1c-0b8c-4bd3-95b1-be72786197ac", + "attributes" : { } + }, { + "id" : "9fe240f0-569e-4234-b0ff-f7d224cffda6", + "name" : "view-users", + "description" : "${role_view-users}", + "composite" : true, + "composites" : { + "client" : { + "realm-management" : [ "query-groups", "query-users" ] + } + }, + "clientRole" : true, + "containerId" : "e772ee1c-0b8c-4bd3-95b1-be72786197ac", + "attributes" : { } + }, { + "id" : "cfb28d98-8e4f-4466-b6ae-864ea779ec87", + "name" : "manage-identity-providers", + "description" : "${role_manage-identity-providers}", + "composite" : false, + "clientRole" : true, + "containerId" : "e772ee1c-0b8c-4bd3-95b1-be72786197ac", + "attributes" : { } + }, { + "id" : "73e250fc-f5ff-480b-8f62-b6920f91914b", + "name" : "query-groups", + "description" : "${role_query-groups}", + "composite" : false, + "clientRole" : true, + "containerId" : "e772ee1c-0b8c-4bd3-95b1-be72786197ac", + "attributes" : { } + }, { + "id" : "a540d644-3867-43c0-827e-21a117065525", + "name" : "manage-events", + "description" : "${role_manage-events}", + "composite" : false, + "clientRole" : true, + "containerId" : "e772ee1c-0b8c-4bd3-95b1-be72786197ac", + "attributes" : { } + }, { + "id" : "cf3fddee-982b-4658-8422-8a4c4246681d", + "name" : "query-users", + "description" : "${role_query-users}", + "composite" : false, + "clientRole" : true, + "containerId" : "e772ee1c-0b8c-4bd3-95b1-be72786197ac", + "attributes" : { } + }, { + "id" : "eab5102f-4e56-41f5-bea1-6e1c91227cdf", + "name" : "manage-authorization", + "description" : "${role_manage-authorization}", + "composite" : false, + "clientRole" : true, + "containerId" : "e772ee1c-0b8c-4bd3-95b1-be72786197ac", + "attributes" : { } + }, { + "id" : "8d63a3fa-22c0-4533-abe5-db18bbbf98a4", + "name" : "realm-admin", + "description" : "${role_realm-admin}", + "composite" : true, + "composites" : { + "client" : { + "realm-management" : [ "view-events", "query-realms", "view-users", "manage-identity-providers", "query-groups", "manage-events", "query-users", "manage-authorization", "impersonation", "create-client", "view-clients", "manage-clients", "view-identity-providers", "view-authorization", "query-clients", "view-realm", "manage-realm", "manage-users" ] + } + }, + "clientRole" : true, + "containerId" : "e772ee1c-0b8c-4bd3-95b1-be72786197ac", + "attributes" : { } + }, { + "id" : "cf147d82-0990-4f95-8635-af3951168c6f", + "name" : "impersonation", + "description" : "${role_impersonation}", + "composite" : false, + "clientRole" : true, + "containerId" : "e772ee1c-0b8c-4bd3-95b1-be72786197ac", + "attributes" : { } + }, { + "id" : "db7af357-1936-4ffb-80b0-a064d01d7058", + "name" : "create-client", + "description" : "${role_create-client}", + "composite" : false, + "clientRole" : true, + "containerId" : "e772ee1c-0b8c-4bd3-95b1-be72786197ac", + "attributes" : { } + }, { + "id" : "d1d7e393-b588-48c0-baf9-a2d714c84e3b", + "name" : "view-clients", + "description" : "${role_view-clients}", + "composite" : true, + "composites" : { + "client" : { + "realm-management" : [ "query-clients" ] + } + }, + "clientRole" : true, + "containerId" : "e772ee1c-0b8c-4bd3-95b1-be72786197ac", + "attributes" : { } + }, { + "id" : "0bca05ed-8f26-4c2e-9226-db584d1534fe", + "name" : "manage-clients", + "description" : "${role_manage-clients}", + "composite" : false, + "clientRole" : true, + "containerId" : "e772ee1c-0b8c-4bd3-95b1-be72786197ac", + "attributes" : { } + }, { + "id" : "f8febd44-f940-41ef-b43c-858516dc6807", + "name" : "view-authorization", + "description" : "${role_view-authorization}", + "composite" : false, + "clientRole" : true, + "containerId" : "e772ee1c-0b8c-4bd3-95b1-be72786197ac", + "attributes" : { } + }, { + "id" : "7745a218-4224-445a-aaa7-f6fc5090ff83", + "name" : "view-identity-providers", + "description" : "${role_view-identity-providers}", + "composite" : false, + "clientRole" : true, + "containerId" : "e772ee1c-0b8c-4bd3-95b1-be72786197ac", + "attributes" : { } + }, { + "id" : "85de0753-3220-429e-9e99-093e254cb52d", + "name" : "query-clients", + "description" : "${role_query-clients}", + "composite" : false, + "clientRole" : true, + "containerId" : "e772ee1c-0b8c-4bd3-95b1-be72786197ac", + "attributes" : { } + }, { + "id" : "1f021080-7572-4fd5-8820-08be079aa966", + "name" : "manage-realm", + "description" : "${role_manage-realm}", + "composite" : false, + "clientRole" : true, + "containerId" : "e772ee1c-0b8c-4bd3-95b1-be72786197ac", + "attributes" : { } + }, { + "id" : "18a20079-4933-481f-a6d1-ef5b4996e86d", + "name" : "view-realm", + "description" : "${role_view-realm}", + "composite" : false, + "clientRole" : true, + "containerId" : "e772ee1c-0b8c-4bd3-95b1-be72786197ac", + "attributes" : { } + }, { + "id" : "12f8ade4-cd20-41df-9231-ded509f026d3", + "name" : "manage-users", + "description" : "${role_manage-users}", + "composite" : false, + "clientRole" : true, + "containerId" : "e772ee1c-0b8c-4bd3-95b1-be72786197ac", + "attributes" : { } + } ], + "security-admin-console" : [ ], + "admin-cli" : [ ], + "account-console" : [ ], + "broker" : [ { + "id" : "86847fb4-731b-4064-a6c0-f34144a63060", + "name" : "read-token", + "description" : "${role_read-token}", + "composite" : false, + "clientRole" : true, + "containerId" : "68fca6e8-2756-4e26-8131-7f9a436e91e5", + "attributes" : { } + } ], + "account" : [ { + "id" : "dc0deb7d-874b-4c6a-b9ae-fac234b10efa", + "name" : "manage-account-links", + "description" : "${role_manage-account-links}", + "composite" : false, + "clientRole" : true, + "containerId" : "1343d64d-58b3-494e-af8d-dc951ab7227e", + "attributes" : { } + }, { + "id" : "91dc9dbd-5db6-4d5f-aa83-f8a62478bf86", + "name" : "view-applications", + "description" : "${role_view-applications}", + "composite" : false, + "clientRole" : true, + "containerId" : "1343d64d-58b3-494e-af8d-dc951ab7227e", + "attributes" : { } + }, { + "id" : "94b5bfc8-5e99-4423-a6d8-30a10c1511f5", + "name" : "view-consent", + "description" : "${role_view-consent}", + "composite" : false, + "clientRole" : true, + "containerId" : "1343d64d-58b3-494e-af8d-dc951ab7227e", + "attributes" : { } + }, { + "id" : "6592ab66-fd98-4788-bb6c-788371a57e83", + "name" : "manage-account", + "description" : "${role_manage-account}", + "composite" : true, + "composites" : { + "client" : { + "account" : [ "manage-account-links" ] + } + }, + "clientRole" : true, + "containerId" : "1343d64d-58b3-494e-af8d-dc951ab7227e", + "attributes" : { } + }, { + "id" : "da86c8d1-11af-4369-98fe-df385d9eeae2", + "name" : "view-groups", + "description" : "${role_view-groups}", + "composite" : false, + "clientRole" : true, + "containerId" : "1343d64d-58b3-494e-af8d-dc951ab7227e", + "attributes" : { } + }, { + "id" : "860c86cf-630f-4f88-9983-390e626fea6b", + "name" : "delete-account", + "description" : "${role_delete-account}", + "composite" : false, + "clientRole" : true, + "containerId" : "1343d64d-58b3-494e-af8d-dc951ab7227e", + "attributes" : { } + }, { + "id" : "2d853266-86b0-4f41-9fb5-82fa4042dc5a", + "name" : "manage-consent", + "description" : "${role_manage-consent}", + "composite" : true, + "composites" : { + "client" : { + "account" : [ "view-consent" ] + } + }, + "clientRole" : true, + "containerId" : "1343d64d-58b3-494e-af8d-dc951ab7227e", + "attributes" : { } + }, { + "id" : "382db886-8429-4312-be4b-03deb2b01bc9", + "name" : "view-profile", + "description" : "${role_view-profile}", + "composite" : false, + "clientRole" : true, + "containerId" : "1343d64d-58b3-494e-af8d-dc951ab7227e", + "attributes" : { } + } ], + "tasks" : [ ] + } + }, + "groups" : [ ], + "defaultRole" : { + "id" : "7f22789f-8a98-48ab-bf0b-58fc8ba07d05", + "name" : "default-roles-tasks", + "description" : "${role_default-roles}", + "composite" : true, + "clientRole" : false, + "containerId" : "d5aaecfc-fee3-4dce-adde-6280b9ed23a9" + }, + "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" : "Yes", + "webAuthnPolicyPasswordlessUserVerificationRequirement" : "required", + "webAuthnPolicyPasswordlessCreateTimeout" : 0, + "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister" : false, + "webAuthnPolicyPasswordlessAcceptableAaguids" : [ ], + "webAuthnPolicyPasswordlessExtraOrigins" : [ ], + "users" : [ { + "id" : "05d5c9b3-2d30-4a0d-9c2d-ed3212d5e2da", + "username" : "test", + "firstName" : "John", + "lastName" : "Doe", + "email" : "john.doe@helpwave.de", + "emailVerified" : true, + "enabled" : true, + "createdTimestamp" : 1764546986927, + "totp" : false, + "credentials" : [ { + "id" : "e15dab03-6c5b-4b53-ad9e-91d1024f2a1b", + "type" : "password", + "userLabel" : "My password", + "createdDate" : 1764546995065, + "secretData" : "{\"value\":\"Yg9msci7ctlF0zTXiQe+vjPkTrDq7lBkKyeWcxHLxlE=\",\"salt\":\"mYbxcfft3FMwjIDx03aHdw==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":5,\"algorithm\":\"argon2\",\"additionalParameters\":{\"hashLength\":[\"32\"],\"memory\":[\"7168\"],\"type\":[\"id\"],\"version\":[\"1.3\"],\"parallelism\":[\"1\"]}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-tasks" ], + "notBefore" : 0, + "groups" : [ ] + } ], + "scopeMappings" : [ { + "clientScope" : "offline_access", + "roles" : [ "offline_access" ] + } ], + "clientScopeMappings" : { + "account" : [ { + "client" : "account-console", + "roles" : [ "manage-account", "view-groups" ] + } ] + }, + "clients" : [ { + "id" : "1343d64d-58b3-494e-af8d-dc951ab7227e", + "clientId" : "account", + "name" : "${client_account}", + "rootUrl" : "${authBaseUrl}", + "baseUrl" : "/realms/tasks/account/", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ "/realms/tasks/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" : "ebbdc2e8-d1aa-4196-bf73-ac95c01bd62a", + "clientId" : "account-console", + "name" : "${client_account-console}", + "rootUrl" : "${authBaseUrl}", + "baseUrl" : "/realms/tasks/account/", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ "/realms/tasks/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" : "fdbe4379-b50c-4d9e-9f8e-54735eb481c2", + "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" : "1b4d8379-276a-4c4f-86c5-aad887243a09", + "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" : "68fca6e8-2756-4e26-8131-7f9a436e91e5", + "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" : "e772ee1c-0b8c-4bd3-95b1-be72786197ac", + "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" : "1002ec09-fea0-4d2f-a997-a3c8106abeb3", + "clientId" : "security-admin-console", + "name" : "${client_security-admin-console}", + "rootUrl" : "${authAdminUrl}", + "baseUrl" : "/admin/tasks/console/", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ "/admin/tasks/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" : "cfc2132d-77b5-45cf-a1b2-85bac50ffad1", + "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" : "e43b587c-07db-445d-b6d9-e9b4b7cd03ac", + "clientId" : "tasks", + "name" : "helpwave tasks", + "description" : "", + "rootUrl" : "", + "adminUrl" : "", + "baseUrl" : "", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : true, + "clientAuthenticatorType" : "client-secret", + "secret" : "tasks-secret", + "redirectUris" : [ "*" ], + "webOrigins" : [ "*" ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : false, + "frontchannelLogout" : true, + "protocol" : "openid-connect", + "attributes" : { + "realm_client" : "false", + "oidc.ciba.grant.enabled" : "false", + "client.secret.creation.time" : "1764546967", + "backchannel.logout.session.required" : "true", + "standard.token.exchange.enabled" : "false", + "oauth2.device.authorization.grant.enabled" : "false", + "backchannel.logout.revoke.offline.tokens" : "false", + "dpop.bound.access.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" : "4f248cab-0ecf-4736-9d24-2ba8cd5f20e7", + "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" : "1ddd2d88-fdd5-46ef-ba17-ac011f954df1", + "name" : "allowed web origins", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-allowed-origins-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "access.token.claim" : "true" + } + } ] + }, { + "id" : "66617177-e352-4f37-a92d-2c9339b6c919", + "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" : "91b2dd62-834f-4711-9599-d2b934f6ac61", + "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" : "6be61f75-2baa-4e42-a3c2-e33e5307a4a0", + "name" : "sub", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-sub-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "access.token.claim" : "true" + } + } ] + }, { + "id" : "fcf5abc7-222d-4bf2-8e88-06104788b8c6", + "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" : "4fb48520-d67f-43c9-b2fd-e48a7caa235c", + "name" : "audience resolve", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-audience-resolve-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "access.token.claim" : "true" + } + }, { + "id" : "76fdd922-eedf-4fb2-a670-73c712899131", + "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" : "0226f6c5-4fd7-43a2-abbd-dbf669968c8c", + "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" : "b5ae8edf-29b0-467e-b3f3-844ef644bfa2", + "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" : "024d2862-b1a0-4acf-a7c1-b8d8ee3efc0b", + "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" : "a6fbe902-edb9-46cb-8a74-a0e0b6485526", + "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" : "15b92c2c-851e-485d-a1e2-b9f9468dcc2c", + "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" : "bff963e7-205d-4eeb-9022-cc338ea78661", + "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" : "dc31ad71-5324-4066-83f9-b725f261b45d", + "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" : "4bd80b1a-7d77-4a2a-b58b-1083feb95efb", + "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" : "c955d1da-b89d-42e5-ae93-91c7395dea59", + "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" : "0d03f640-866e-48b2-90b8-e53fddb9cea7", + "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" : "0527eb0c-6a2f-4baa-b9b8-760daca60203", + "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" : "9b49830f-9199-42bf-a71f-db343b0dbe1e", + "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" : "1c9544fc-943b-4f67-b14e-6ab81a39dbfe", + "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" : "36d1af49-d77d-47d7-a5e9-6dbca64ddf42", + "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" : "7b5a3a2c-0c53-45c6-957c-4c74a78b9d5f", + "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" : "14d74128-daa0-4471-93e8-ab66c0b05d6e", + "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" : "d9c6b2d6-e1c3-4649-9808-17b2a8357bc6", + "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" : "33c829a7-cb8f-4eb6-b0f6-0b0eba158546", + "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" : "15a26819-45d3-4edf-9916-bf5669d16f91", + "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" : "c2ad80c8-4161-42bd-8086-4c3dd7e6f252", + "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" : "a21dbac4-ef48-4f80-9efe-1cbed204c0bb", + "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" : "69e41a87-43f7-4e55-9059-9455efb04368", + "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" : "b2976017-ee13-44f1-875c-f370a7ca2861", + "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" : "9fe0a578-cd15-49ad-b360-b53ae4ddb48c", + "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" : "5c38dfec-9d4a-4b4c-9441-95761b80b4f4", + "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" : "464a3cb0-1ed1-4b70-a561-ed7ee8ef4987", + "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" + } + }, { + "id" : "b9426495-c360-40f3-ab93-8df2732f292f", + "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" : "dc613ee1-d2aa-451f-b26a-5ee1a09070d6", + "name" : "role_list", + "description" : "SAML role list", + "protocol" : "saml", + "attributes" : { + "consent.screen.text" : "${samlRoleListScopeConsentText}", + "display.on.consent.screen" : "true" + }, + "protocolMappers" : [ { + "id" : "df99924d-b776-4530-91c2-118c421c1f35", + "name" : "role list", + "protocol" : "saml", + "protocolMapper" : "saml-role-list-mapper", + "consentRequired" : false, + "config" : { + "single" : "false", + "attribute.nameformat" : "Basic", + "attribute.name" : "Role" + } + } ] + }, { + "id" : "4e789a14-6a1c-4f6f-8a19-9f301a6131f5", + "name" : "service_account", + "description" : "Specific scope for a client enabled for service accounts", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "false", + "display.on.consent.screen" : "false" + }, + "protocolMappers" : [ { + "id" : "b79bae73-bf10-4021-8c80-b77891c82bb1", + "name" : "Client IP Address", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usersessionmodel-note-mapper", + "consentRequired" : false, + "config" : { + "user.session.note" : "clientAddress", + "id.token.claim" : "true", + "introspection.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "clientAddress", + "jsonType.label" : "String" + } + }, { + "id" : "988f9580-6c88-42a9-bd95-82e8a7244ee8", + "name" : "Client ID", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usersessionmodel-note-mapper", + "consentRequired" : false, + "config" : { + "user.session.note" : "client_id", + "id.token.claim" : "true", + "introspection.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "client_id", + "jsonType.label" : "String" + } + }, { + "id" : "9aed28e8-62bf-42ae-b269-a100f2ee940f", + "name" : "Client Host", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usersessionmodel-note-mapper", + "consentRequired" : false, + "config" : { + "user.session.note" : "clientHost", + "id.token.claim" : "true", + "introspection.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "clientHost", + "jsonType.label" : "String" + } + } ] + }, { + "id" : "bc113469-4342-4c64-98c9-3aa269eb6e79", + "name" : "saml_organization", + "description" : "Organization Membership", + "protocol" : "saml", + "attributes" : { + "display.on.consent.screen" : "false" + }, + "protocolMappers" : [ { + "id" : "8022bfd6-161d-45b4-b0bc-2c9aa18f8f87", + "name" : "organization", + "protocol" : "saml", + "protocolMapper" : "saml-organization-membership-mapper", + "consentRequired" : false, + "config" : { } + } ] + }, { + "id" : "966bd930-6fcb-4040-8a9e-2da7673e729f", + "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" : "32ae8303-353f-43ad-bc9d-130923bdea7d", + "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" : "4fb6edef-a625-48a1-a2de-fd7068a3d652", + "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" : "96ec5285-b427-4c9b-8c0c-ceb84c489cc7", + "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" : "25041712-bd9f-4684-b7b7-b1ca23d8d781", + "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" + } + } ] + } ], + "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';", + "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" : "4fd6aabb-2320-416c-b64d-11b25ded0d69", + "name" : "Consent Required", + "providerId" : "consent-required", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { } + }, { + "id" : "f32d362e-6080-493c-bac9-b20c26aa66f0", + "name" : "Full Scope Disabled", + "providerId" : "scope", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { } + }, { + "id" : "deb7b1f1-a0ec-41ca-a481-cbe2b4fcc14e", + "name" : "Allowed Protocol Mapper Types", + "providerId" : "allowed-protocol-mappers", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "allowed-protocol-mapper-types" : [ "oidc-sha256-pairwise-sub-mapper", "saml-role-list-mapper", "oidc-address-mapper", "oidc-usermodel-property-mapper", "saml-user-attribute-mapper", "saml-user-property-mapper", "oidc-full-name-mapper", "oidc-usermodel-attribute-mapper" ] + } + }, { + "id" : "c2c89f17-9dc3-4b48-9cc0-96cf40c186aa", + "name" : "Allowed Protocol Mapper Types", + "providerId" : "allowed-protocol-mappers", + "subType" : "authenticated", + "subComponents" : { }, + "config" : { + "allowed-protocol-mapper-types" : [ "oidc-usermodel-property-mapper", "saml-user-attribute-mapper", "saml-user-property-mapper", "oidc-usermodel-attribute-mapper", "oidc-full-name-mapper", "saml-role-list-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-address-mapper" ] + } + }, { + "id" : "87223906-c75c-4ea0-bfaf-2c845185de52", + "name" : "Max Clients Limit", + "providerId" : "max-clients", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "max-clients" : [ "200" ] + } + }, { + "id" : "36289166-13a9-4973-a039-ace1e7697dc3", + "name" : "Allowed Client Scopes", + "providerId" : "allowed-client-templates", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "allow-default-scopes" : [ "true" ] + } + }, { + "id" : "49437b08-900a-4429-a9d7-0d613a1c9be3", + "name" : "Allowed Client Scopes", + "providerId" : "allowed-client-templates", + "subType" : "authenticated", + "subComponents" : { }, + "config" : { + "allow-default-scopes" : [ "true" ] + } + }, { + "id" : "3b022043-bf54-4dfa-ae1f-159972121ad2", + "name" : "Trusted Hosts", + "providerId" : "trusted-hosts", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "host-sending-registration-request-must-match" : [ "true" ], + "client-uris-must-match" : [ "true" ] + } + } ], + "org.keycloak.keys.KeyProvider" : [ { + "id" : "d0cd5796-9fd1-45b2-93c4-5e5e68b7224e", + "name" : "aes-generated", + "providerId" : "aes-generated", + "subComponents" : { }, + "config" : { + "kid" : [ "73aa66a9-029e-434b-931c-5c1361fb181c" ], + "secret" : [ "DpWL8cwxPwYvP1bVBA4sog" ], + "priority" : [ "100" ] + } + }, { + "id" : "f2660cec-9306-4bc7-a5ce-a94c52984595", + "name" : "hmac-generated-hs512", + "providerId" : "hmac-generated", + "subComponents" : { }, + "config" : { + "kid" : [ "3e94b346-d8ce-4dec-872c-9b343cf9a000" ], + "secret" : [ "NMI4AeXNpC79RohT9Ghni0btvmSDZdoLXy9RMJq53BVKdQ7dFCoEKjx_an_YBZoDwMZCaF_K2GvNnzwhW-hXmhdQ1JpBoZ0HS0cdyBmpUPvgOBYlvb8YiMCLAOU-cmQDrYH9lrL7clEuAadhOBiWHF9Wb9h8vDocsDt5hc08dMk" ], + "priority" : [ "100" ], + "algorithm" : [ "HS512" ] + } + }, { + "id" : "3b0b4d63-4bec-41b6-9e70-0f37f6a94c3c", + "name" : "rsa-enc-generated", + "providerId" : "rsa-enc-generated", + "subComponents" : { }, + "config" : { + "privateKey" : [ "MIIEpQIBAAKCAQEAuul/aW6sASoeiGZBJRxnEGkRj22tv1Y1CRZPXOzFWWIIiKp2eE3amJsx1SOFSDJPzBiJCMS6HV3TNBqBQ5Sf7y2WYSUT+rtmBgUxM/Xpzgi+XPyodyFKUlJrEIJhtmTXXUvL4/WpVavNANhJZBgg10Cw1y8hp0cmDvkX4CVWO0JZ8V7ZMpqX4RbpAfY4IgsLFctypZEg/P6+LiyL3cBQaSjcMUtaugoMYqAMyMJbQFLbknAk2O+Ho6CbhERomQP11LVdZF57qlV5szLnTwwIZqa4YA2BDo9qDEx/ROeLV5c06teL0qDc/Oya99vdLeYTlha4QECynqxQ+SR7QRd4cQIDAQABAoIBAA8Tv73a+g/7/EuHaBDcyimGOC7oK4BILW4GuS8nQEIWCNcxXO4WZaAywKHPRDSPLmWpwwAMknDE/UDyNGQlrIMIG93JNmPe0vuAVrg4yXvfs69fiB1X951NXaqOweALIV46u38vdVHeUmKTj9P9qESEyN8Kw72xHQ5PJJBlop+T9s7UHUvMjgFjCj6qKZWevSVKEw6c7RTXr/Q2y/Ktsf+q75HQoFphLShTZ01kQryqkH3TlPaom4LhF6XZyC/ABFFFP/5JJrDedGESlH3A46DrjbABJ6gXN2tx3tdGPq+PboVRfnVm5EkstnXBKSPVzTXI2Rk56ewue47txuikS/MCgYEA3lthL8e7spQOVV59FH6XHivm9koQfRCLzGdlYgN8joVL/Xui0nQKEomH5uD2fv9jwbUAXTDiQX1lvU6Hk9+Vc0Wz5W3UQK5KihjMM+7rhDy14j+re2vVf1o83hHZfpAN8v1rjyNWjEVbvZZmXj++S/Ga0Pa+Ykbm5x3YqOgNCfMCgYEA1zE4wj1Vf6KM3jpMT0GFd3soVkMMbz+In+TpGgr/a7lZPRo6YYhwGlEpZ8xDduc2Ln/Z8C099RYNCD7q59/+FmlBR8EF6Nq4F7KqCGsTsHU7slZieWiS3H/IF37JrOgrSNDf2u/4LVCjW0/mI1WLzMuucNwRQgdRNJ4L1ek3iQsCgYEAtsFycpv+SHtqAH6F3ZdiS1kYHqunO7Oiw2DkMhfdgyJJ39CDdegL80p4mamiz6TG8An9f5rTC7KuMVbfFgn5QuIyRsrrXjIib7iL9c9UYb6oW4mrPujVdDPNvVP8aAqsfGAPzHjmBKppCb0Y7DQGC1NLxJ5Ywlu2IpUkY0c+mEMCgYEAz7MDfH8kga/L1UZhlvG/t4XYdgH7A3lIDkQsNOW+iCP6nP4usSq2QMBrfXLq/t3FLvZZzPZt4Ztp8rZ/llRc8xaeWnpUkN5iZwUcFkn7VAqjFstH+rTTYx22a+8FCu1/saVhGvkZCevV7/FbSGluKLnC4c2WnqtfVrDg7ZNO9ecCgYEAxnKdqwed+lzjBzlOXQv33po9A/7jiBCIIN9GNZvuhJgUcQ2OW+mdhazPelP9tA1SA5rvyIY3zgUCrXhoA/kRdoclDg1PeTAy0M0wqPUjb3ZSOkqyTcSq8lG8qGAGdG1T9UP4+KZSxJsCk+8CNFJmT6VW6OOW7G5Ee67xc6lJTKU=" ], + "keyUse" : [ "ENC" ], + "certificate" : [ "MIICmTCCAYECBgGa1zGr0zANBgkqhkiG9w0BAQsFADAQMQ4wDAYDVQQDDAV0YXNrczAeFw0yNTExMzAyMzU0MDhaFw0zNTExMzAyMzU1NDhaMBAxDjAMBgNVBAMMBXRhc2tzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuul/aW6sASoeiGZBJRxnEGkRj22tv1Y1CRZPXOzFWWIIiKp2eE3amJsx1SOFSDJPzBiJCMS6HV3TNBqBQ5Sf7y2WYSUT+rtmBgUxM/Xpzgi+XPyodyFKUlJrEIJhtmTXXUvL4/WpVavNANhJZBgg10Cw1y8hp0cmDvkX4CVWO0JZ8V7ZMpqX4RbpAfY4IgsLFctypZEg/P6+LiyL3cBQaSjcMUtaugoMYqAMyMJbQFLbknAk2O+Ho6CbhERomQP11LVdZF57qlV5szLnTwwIZqa4YA2BDo9qDEx/ROeLV5c06teL0qDc/Oya99vdLeYTlha4QECynqxQ+SR7QRd4cQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQA1rylvhJPQydrG6VK2zDod+5XlGNUwbd1tZwjJo8eq2T1SjEf6sH+UvsLVTPvcb61gSZx+o96Z3ZyOZrpIqkmMGFjdmyPtKm6glFHv5j75VGdxYkxEzUKL+DoVIGfzlYU3EIPhC1U3NU1xCPHBqsAUo0fp3Dfzn9T8mO2aZJ+OpffUQgDu5PR7RSu+SuWnHz0M9j0W/wV+gToVr61LEFBPQwK6TjHmX8NdO/EQZeISCM05iFXyawC/EKDrrdnKtsH7Pt7u2p/aoTO7GSwgxXEXhKePHPeojtd3BoRsKgQGuY4tkkPSaY8s2Cxdqrf+5qrlLUgpisJbFIXY9VQxLbCL" ], + "priority" : [ "100" ], + "algorithm" : [ "RSA-OAEP" ] + } + }, { + "id" : "97e1fb47-c069-43cf-8632-03123f47e2de", + "name" : "rsa-generated", + "providerId" : "rsa-generated", + "subComponents" : { }, + "config" : { + "privateKey" : [ "MIIEpAIBAAKCAQEA1zKUVbBPHYqP2BM6IFPklsKbZEWNh1y9tjucqGzBIj/ihdRNtsVH2SmL9QqIAuWnG+OMy/5apRlsr3hKA0JfEdGPcMw+iZXZn/0Og4OLJ0Rqc5Kgs6h4/JabI/OwAc6mPM5h09dRD/zuEjUZfAklMRdrsnlVZquDnkC2ulZWMNE9WzoO4kRewjRWeDW/quytKy7WjJ0XSGfZGRZvNtk8a1xHS4mAgyvOoen+cXYIRmWN/LRmFaOrN20f033QaRqoMcXYV0qblBjKSx72eiq9mivNd7IQh6pCc3He7DtJfVegwRfhaZoXdO/6W+YgKI5Oevdjnk+QN8LrBWe/gbx3IQIDAQABAoIBAAgo5dD993ICUAP8bqbNJ4zD4kqCDKTGUnc3eXA1d3lJ/NfgPfYhMuE8Hw9yj8e0V4rKLQuLIHrEJQfeQsgu2xX84TiAthpXcUIMwWEhOsSJZpPtSmSljbjeKRHK6rCW2lzR4om6z1sQ1yIfm1DFtABRiAbjbOaQuFPm1kbogSomox3BuSVrVHUiLZUzYuXOR/nn7DzXz+HVC3FYxDxL27KkZI7GC42E4lJeToTSN90BUmgBn/oquAy5j/XoQSgz3YpB5KW+ixXdIS7zIGbpWfwqmkgGXhHraRWuJY/PWRC0j49RTo6VjxPhySZbh/ib++qNl3up5/IIdBBWaeSUpXECgYEA+IUpFHzgHgt7Tclo/eNiUPGpBGJw2QlwOcdLUJXeBleQj4yOhFMl8z31kt5eIe094fQ5PM3heenAHXd8L64U4QPZB/I9QQB5qpOlOnhwigsDVQvuKd08nlQ2P4i0yNrzFRViZq3jXzt3pKhUraAzRvpTGaYy9zsQNBFVYcd/M20CgYEA3ayraYm1PNq2sZfu2IbToOsi6KWVWZUZEyyvc2gAq/tEo/MLo/IqwP3m77vQBpHmHC+f8L6sOjhkGtRPMaFEBwm4l/popTkL5/nJiYYhigqDY6ZQ0e31ayTFpFaN7sFWMuG0QZHGAEqwKtTF2YlJkKrTrG2dg3ZspwHTtRZijgUCgYEA8++AyyNnuRX8CfZQoCS5NEqAZFVb/y1Mguoj+w2fyQnaU4zbtvcGoSOIEIlETHjZ7RgTtqM/VrMiZ8oIk6SDyfpE5Y/YoB0fT1dtL19Y16bHtornUyBdh+uQ8/vzt9NyFpfO5op4S9nLLkwsEeDVdC3xs5N3I0VOOk1pSXjQWmECgYADfZyVZN29v5Pw5/uS3Uyn1AwQrcrURkzGyIVaw+B+1M4X5tPgGQgjUdw5GjQEZnfBKd7S463CnPC2m3FDzs5LbhPCEOdk8qkFViGeUMZmbhPzlcLgC2c58LJMDDDf+Ztp4bP89Fax2ns7sW4MJuVJqeb7aB5623qKhrTWvSuAIQKBgQC8UHSyQx+bk7q4Kvma7e8LQJaTvKq9/wu9RGsJ+TkGumsFm6rZ4g77imdkRxm4F1/T3TuESI1/IhilPG/wNWiDF+hxOaF5MLfvlBKfSvmUkucvZmmbqBMAmc77Pp3KcwV4AW8Bho17FkMdYZvH71cF/Q11WbmVbczLNcBH5Hfduw==" ], + "keyUse" : [ "SIG" ], + "certificate" : [ "MIICmTCCAYECBgGa1zGrfjANBgkqhkiG9w0BAQsFADAQMQ4wDAYDVQQDDAV0YXNrczAeFw0yNTExMzAyMzU0MDdaFw0zNTExMzAyMzU1NDdaMBAxDjAMBgNVBAMMBXRhc2tzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1zKUVbBPHYqP2BM6IFPklsKbZEWNh1y9tjucqGzBIj/ihdRNtsVH2SmL9QqIAuWnG+OMy/5apRlsr3hKA0JfEdGPcMw+iZXZn/0Og4OLJ0Rqc5Kgs6h4/JabI/OwAc6mPM5h09dRD/zuEjUZfAklMRdrsnlVZquDnkC2ulZWMNE9WzoO4kRewjRWeDW/quytKy7WjJ0XSGfZGRZvNtk8a1xHS4mAgyvOoen+cXYIRmWN/LRmFaOrN20f033QaRqoMcXYV0qblBjKSx72eiq9mivNd7IQh6pCc3He7DtJfVegwRfhaZoXdO/6W+YgKI5Oevdjnk+QN8LrBWe/gbx3IQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBuYktPFwmIDa25REYft3Y0CwA4IjUzMOtJHull2XzKBsdMHIY0bnADJreNvYZwKT2kPZk7TRCLH4I/Mt+yeRtQo/a++3t92L469vYXCTH7JYEjM9RRQ52PtXUcS8ZH4v9iMd2h1eYAwwbVqQ1IyGLXR1ZQaL9sOXzOE8aASskcFKWG/AdSB/RglbqisCrXMqQdOlLZs1qDRzkXPuwEsY2/MA2xwmIPSDjqcnL0F5cH0UUn5jHI1rjd3UFJGura5HS9OIN5z36u40urmCZINHtzaG/NG2paYNyK0abOqM7zxw1a1zBHooEDEzFhzd1PeCQ1ZDP0sGlE18GHjh4mLxfc" ], + "priority" : [ "100" ] + } + } ] + }, + "internationalizationEnabled" : false, + "authenticationFlows" : [ { + "id" : "96f5fbdb-ea61-4789-a971-47735fe3c801", + "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" : "9f40be6d-dc80-4ae4-a775-6ff080cd5273", + "alias" : "Browser - Conditional 2FA", + "description" : "Flow to determine if any 2FA 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 + }, { + "authenticatorConfig" : "browser-conditional-credential", + "authenticator" : "conditional-credential", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "auth-otp-form", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 30, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "webauthn-authenticator", + "authenticatorFlow" : false, + "requirement" : "DISABLED", + "priority" : 40, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "auth-recovery-authn-code-form", + "authenticatorFlow" : false, + "requirement" : "DISABLED", + "priority" : 50, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "0ed0cece-0166-4c1e-996b-d117ce09c81c", + "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" : "87cb5add-ada5-42ec-8e38-5063474ff6ff", + "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" : "ab0bb02d-cb93-4d26-b8fa-e242406eea4a", + "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" : "a7244029-6989-48e0-9eef-651ce565d5f5", + "alias" : "First broker login - Conditional 2FA", + "description" : "Flow to determine if any 2FA 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 + }, { + "authenticatorConfig" : "first-broker-login-conditional-credential", + "authenticator" : "conditional-credential", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "auth-otp-form", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 30, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "webauthn-authenticator", + "authenticatorFlow" : false, + "requirement" : "DISABLED", + "priority" : 40, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "auth-recovery-authn-code-form", + "authenticatorFlow" : false, + "requirement" : "DISABLED", + "priority" : 50, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "6149ad2d-71b3-4ea7-83e7-f261965f77e9", + "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" : "2de4e9e4-f338-45cf-8f14-07503bc113e9", + "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" : "1090277e-d34e-46ea-871e-074111f8d4a4", + "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" : "f8d83102-147d-4ed7-bdce-2dff1563575b", + "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" : "b44aa23e-e10a-4d85-9fe0-81ed3789e9a3", + "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 2FA", + "userSetupAllowed" : false + } ] + }, { + "id" : "d5118fb6-4c83-42ed-a5a4-89128d8f2478", + "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" : "164c1686-bde5-4888-87e8-50bf880004a2", + "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" : "431c0a97-8ebe-449f-bc2d-e07741d7d223", + "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" : "41b87931-d8c3-46c0-bc1c-e03af219bf1c", + "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" : "65b9625a-71bc-492f-a498-49f55d00eae3", + "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" : 60, + "autheticatorFlow" : true, + "flowAlias" : "First Broker Login - Conditional Organization", + "userSetupAllowed" : false + } ] + }, { + "id" : "f04e5132-d90f-4370-9f96-3d820a6eaadf", + "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 2FA", + "userSetupAllowed" : false + } ] + }, { + "id" : "573e07e1-943d-4aac-b92e-f8d2ee5b95b6", + "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" : "35020e6b-7e0b-41aa-8c19-e7c6f62dc191", + "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" : "1c4a22f1-4937-4147-8e69-4a8b9314f103", + "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" : "239b816b-3503-4f34-a6bd-36e79859e889", + "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" : "9fde114a-7504-4010-b38e-89105bf8c2ab", + "alias" : "browser-conditional-credential", + "config" : { + "credentials" : "webauthn-passwordless" + } + }, { + "id" : "f55d89bc-a788-427d-842b-12f70b0c1315", + "alias" : "create unique user config", + "config" : { + "require.password.update.after.registration" : "false" + } + }, { + "id" : "1a38575e-2b64-49bb-b59d-6f8ed709c765", + "alias" : "first-broker-login-conditional-credential", + "config" : { + "credentials" : "webauthn-passwordless" + } + }, { + "id" : "5c9c0063-5b38-4534-a7b7-6d45d8dc2741", + "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_EMAIL", + "name" : "Update Email", + "providerId" : "UPDATE_EMAIL", + "enabled" : false, + "defaultAction" : false, + "priority" : 70, + "config" : { } + }, { + "alias" : "webauthn-register", + "name" : "Webauthn Register", + "providerId" : "webauthn-register", + "enabled" : true, + "defaultAction" : false, + "priority" : 80, + "config" : { } + }, { + "alias" : "webauthn-register-passwordless", + "name" : "Webauthn Register Passwordless", + "providerId" : "webauthn-register-passwordless", + "enabled" : true, + "defaultAction" : false, + "priority" : 90, + "config" : { } + }, { + "alias" : "VERIFY_PROFILE", + "name" : "Verify Profile", + "providerId" : "VERIFY_PROFILE", + "enabled" : true, + "defaultAction" : false, + "priority" : 100, + "config" : { } + }, { + "alias" : "delete_credential", + "name" : "Delete Credential", + "providerId" : "delete_credential", + "enabled" : true, + "defaultAction" : false, + "priority" : 110, + "config" : { } + }, { + "alias" : "idp_link", + "name" : "Linking Identity Provider", + "providerId" : "idp_link", + "enabled" : true, + "defaultAction" : false, + "priority" : 120, + "config" : { } + }, { + "alias" : "CONFIGURE_RECOVERY_AUTHN_CODES", + "name" : "Recovery Authentication Codes", + "providerId" : "CONFIGURE_RECOVERY_AUTHN_CODES", + "enabled" : true, + "defaultAction" : false, + "priority" : 130, + "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.4.6", + "userManagedAccessAllowed" : false, + "organizationsEnabled" : false, + "verifiableCredentialsEnabled" : false, + "adminPermissionsEnabled" : false, + "clientProfiles" : { + "profiles" : [ ] + }, + "clientPolicies" : { + "policies" : [ ] + } +} diff --git a/nginx/Dockerfile b/nginx/Dockerfile new file mode 100644 index 0000000..4d39130 --- /dev/null +++ b/nginx/Dockerfile @@ -0,0 +1,7 @@ +FROM nginx:alpine + +COPY nginx.conf /etc/nginx/nginx.conf.template + +EXPOSE 80 + +CMD ["/bin/sh", "-c", "envsubst '${FRONTEND_HOST} ${BACKEND_HOST}' < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf && exec nginx -g 'daemon off;'"] diff --git a/nginx/nginx.conf b/nginx/nginx.conf new file mode 100644 index 0000000..98847eb --- /dev/null +++ b/nginx/nginx.conf @@ -0,0 +1,47 @@ +events { + worker_connections 1024; +} + +http { + include mime.types; + default_type application/octet-stream; + + upstream frontend_upstream { + server ${FRONTEND_HOST}; + } + + upstream backend_upstream { + server ${BACKEND_HOST}; + } + + server { + listen 80; + server_name localhost; + + location ~ ^/(graphql|callback)$ { + proxy_pass http://backend_upstream; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + + location / { + proxy_pass http://frontend_upstream; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + } +} diff --git a/shell.nix b/shell.nix index b04aecb..4cb84f0 100644 --- a/shell.nix +++ b/shell.nix @@ -1,8 +1,8 @@ { pkgs ? import { }, - pgUser ? "user", + pgUser ? "postgres", pgPassword ? "password", - pgDatabase ? "dbname", + pgDatabase ? "postgres", pgVersion ? 15, redisVersion ? 7, }: @@ -93,7 +93,7 @@ pkgs.mkShell { run-dev-all() { start-docker-db trap "echo '>>> Stopping all dev services...'; stop-docker-db; exit" SIGINT - sleep 5 + sleep 3 run-alembic-upgrade bash -c ' trap "exit" SIGINT